from pathlib import Path
import re
import subprocess
import unittest


ROOT = Path(__file__).resolve().parents[1]
PHP_TEST = ROOT / "tests" / "php" / "diagnostic_log_test.php"
PRODUCTION_PHP = [ROOT / "bee_transaction_log.php"] + [
    path
    for folder in (ROOT / "api", ROOT / "classes")
    for path in folder.rglob("*.php")
    if "vendor" not in path.parts
]


class DiagnosticLogContractTests(unittest.TestCase):
    def test_logger_is_loaded_before_error_handler(self):
        classes = (ROOT / "classes" / "classes.inc").read_text(encoding="utf-8")
        self.assertIn('require_once("BeeDiagnosticLog.php");', classes)
        self.assertLess(
            classes.index('require_once("BeeDiagnosticLog.php");'),
            classes.index('require_once("Error.php");'),
        )

    def test_php_errors_use_managed_log_path(self):
        init = (ROOT / "init.inc.php").read_text(encoding="utf-8")
        self.assertIn("'/var/log/bee_logs/php-error.log'", init)
        self.assertNotIn("'/var/www/html/debug.txt'", init)

    def test_diagnostic_directory_override_is_cli_only(self):
        logger = (ROOT / "classes" / "BeeDiagnosticLog.php").read_text(encoding="utf-8")
        self.assertIn("PHP_SAPI !== 'cli'", logger)
        self.assertIn("private const DEFAULT_DIRECTORY = '/var/log/bee_logs';", logger)
        self.assertIn("return self::DEFAULT_DIRECTORY;", logger)
        self.assertNotIn('WORKER_TEST_LOG_DIR', logger)

    def test_logger_swallows_filesystem_and_lock_warnings(self):
        logger = (ROOT / "classes" / "BeeDiagnosticLog.php").read_text(encoding="utf-8")
        for function in (
            'is_dir',
            'is_writable',
            'fopen',
            'flock',
            'ftruncate',
            'fflush',
            'fseek',
            'fwrite',
            'fclose',
        ):
            self.assertIsNone(
                re.search(rf'(?<!@)\b{function}\s*\(', logger),
                msg=f'{function} warning is not suppressed',
            )

    def test_candle_has_no_empty_else_blocks(self):
        candle = (ROOT / "classes" / "Candle.php").read_text(encoding="utf-8")
        self.assertIsNone(re.search(r'}\s*else\s*{\s*}', candle))

    def test_no_production_php_file_mentions_legacy_test_output_files(self):
        offenders = sorted(
            path.relative_to(ROOT).as_posix()
            for path in PRODUCTION_PHP
            if "test_output_" in path.read_text(encoding="utf-8")
        )
        self.assertEqual([], offenders)

    def test_no_tracked_non_test_source_mentions_legacy_test_output_files(self):
        completed = subprocess.run(
            [
                "git",
                "grep",
                "-n",
                "-I",
                "test_output_",
                "--",
                ":(exclude)tests/**",
                ":(exclude)deploy/logrotate/razgar-bee",
            ],
            cwd=ROOT,
            text=True,
            capture_output=True,
            check=False,
        )
        self.assertEqual(
            1,
            completed.returncode,
            msg=completed.stdout + completed.stderr,
        )

    def test_cli_logger_cases(self):
        for case in ("disabled", "selected", "uncapped", "hard-bound", "error-selected", "unavailable"):
            with self.subTest(case=case):
                completed = subprocess.run(
                    ["php", str(PHP_TEST), case],
                    cwd=ROOT,
                    text=True,
                    capture_output=True,
                    check=False,
                )
                self.assertEqual(
                    0,
                    completed.returncode,
                    msg=completed.stdout + completed.stderr,
                )


if __name__ == "__main__":
    unittest.main()
