# #region TestLoggingAuditFixes [C:3] [TYPE Module] [SEMANTICS test,logging,except,audit]
# @BRIEF Verify Class 2 fix — no bare `except: pass` patterns remain in src/.
# @RELATION BINDS_TO -> [EXT:frontend:PluginLoaderCore]
# @TEST_EDGE: except_pass_replaced -> bare `except: pass` patterns are absent from src/
# @TEST_EDGE: plugin_loader_uses_logger -> plugin_loader.py uses logger, not print
import ast
from pathlib import Path
import sys

sys.path.insert(0, str(Path(__file__).parent.parent / "src"))


class TestLoggingAuditFixes:
    """Verify that all bare `except: pass` patterns have been replaced with proper logging."""

    PLUGIN_LOADER_PATH = Path(__file__).parent.parent / "src" / "core" / "plugin_loader.py"

    # #region _collect_src_files [C:1] [TYPE Function]
    # @BRIEF Collect all .py files under src/ excluding test/ and mock/ directories.
    @staticmethod
    def _collect_src_files():
        src_root = Path(__file__).parent.parent / "src"
        return sorted(
            p for p in src_root.rglob("*.py")
            if "test" not in p.parts and "mock" not in p.parts and p.name != "__init__.py"
        )
    # #endregion _collect_src_files

    # #region test_except_pass_replaced_with_log [C:2] [TYPE Function]
    # @BRIEF AST audit: bare `except: pass` nodes are absent from src/ source files.
    def test_except_pass_replaced_with_log(self):
        files_with_bare_except_pass = []

        for filepath in self._collect_src_files():
            try:
                tree = ast.parse(filepath.read_text(encoding="utf-8"), filename=str(filepath))
            except SyntaxError:
                continue

            for node in ast.walk(tree):
                if not isinstance(node, ast.ExceptHandler):
                    continue
                if node.type is not None:
                    continue
                if len(node.body) == 1 and isinstance(node.body[0], ast.Pass):
                    files_with_bare_except_pass.append(str(filepath))
                    break

        assert not files_with_bare_except_pass, (
            f"Bare `except: pass` found in {len(files_with_bare_except_pass)} file(s):\n"
            + "\n".join(files_with_bare_except_pass)
        )
    # #endregion test_except_pass_replaced_with_log

    # #region test_plugin_loader_uses_logger_not_print [C:2] [TYPE Function]
    # @BRIEF plugin_loader.py (src/core/) uses logger for error/warning, not print().
    def test_plugin_loader_uses_logger_not_print(self):
        code = self.PLUGIN_LOADER_PATH.read_text(encoding="utf-8")
        tree = ast.parse(code, filename=str(self.PLUGIN_LOADER_PATH))

        # Find all print() calls in executable code
        print_lines = []
        for node in ast.walk(tree):
            if (isinstance(node, ast.Call)
                    and isinstance(node.func, ast.Name)
                    and node.func.id == "print"):
                print_lines.append(node.lineno)

        assert len(print_lines) == 0, (
            f"plugin_loader.py still contains print() calls at lines: {print_lines}"
        )
    # #endregion test_plugin_loader_uses_logger_not_print

    # #region test_plugin_loader_imports_logger [C:2] [TYPE Function]
    # @BRIEF plugin_loader.py imports a logger or _logger for structured reporting.
    def test_plugin_loader_imports_logger(self):
        code = self.PLUGIN_LOADER_PATH.read_text(encoding="utf-8")
        tree = ast.parse(code, filename=str(self.PLUGIN_LOADER_PATH))

        has_logger_import = False
        for node in ast.walk(tree):
            if isinstance(node, (ast.Import, ast.ImportFrom)):
                for alias in node.names:
                    if "logger" in alias.name.lower():
                        has_logger_import = True
                        break

        assert has_logger_import, (
            "plugin_loader.py should import 'logger' or '_logger' "
            "for log-based error handling (instead of print)"
        )
    # #endregion test_plugin_loader_imports_logger

    # #region test_plugin_loader_logger_calls_exist [C:2] [TYPE Function]
    # @BRIEF plugin_loader.py references logger.error/warning/info in its code.
    def test_plugin_loader_logger_calls_exist(self):
        code = self.PLUGIN_LOADER_PATH.read_text(encoding="utf-8")
        tree = ast.parse(code, filename=str(self.PLUGIN_LOADER_PATH))

        logger_calls = []
        for node in ast.walk(tree):
            if (isinstance(node, ast.Call)
                    and isinstance(node.func, ast.Attribute)
                    and isinstance(node.func.value, ast.Name)
                    and "logger" in node.func.value.id.lower()):
                logger_calls.append((node.lineno, node.func.attr))

        error_levels = {"error", "warning", "info", "debug", "reason", "reflect", "explore"}
        matching = [call for call in logger_calls if call[1] in error_levels]
        assert len(matching) >= 4, (
            f"plugin_loader.py has only {len(matching)} logger.error/warning/info calls "
            f"(expected at least 4 for the 4 replaced print() statements): {matching}"
        )
    # #endregion test_plugin_loader_logger_calls_exist
# #endregion TestLoggingAuditFixes
