"""
Tests for the check-silent-exceptions pre-commit hook.

Ensures the hook detects silent exception swallowing (except: pass) that
masks bugs and violates the project's no-fallbacks principle. Exceptions
should always be logged or re-raised.
"""

import sys
from importlib import import_module
from pathlib import Path


HOOKS_DIR = Path(__file__).parent.parent.parent / ".pre-commit-hooks"
sys.path.insert(0, str(HOOKS_DIR))

hook_module = import_module("check-silent-exceptions")
check_file_fn = hook_module.check_file


def _write_and_check(tmp_path, code: str, filename: str = "module.py"):
    p = tmp_path / filename
    p.write_text(code, encoding="utf-8")
    return check_file_fn(str(p))


class TestDetectsSilentExceptions:
    """Ensures bare except:pass and except Exception:pass are caught."""

    def test_detects_except_exception_pass(self, tmp_path):
        code = """
try:
    do_work()
except Exception:
    pass
"""
        issues = _write_and_check(tmp_path, code)
        assert len(issues) >= 1
        assert any("silent" in msg.lower() for _, msg in issues)

    def test_detects_bare_except_pass(self, tmp_path):
        code = """
try:
    do_work()
except:
    pass
"""
        issues = _write_and_check(tmp_path, code)
        assert len(issues) >= 1

    def test_detects_except_with_as_pass(self, tmp_path):
        code = """
try:
    do_work()
except Exception as e:
    pass
"""
        issues = _write_and_check(tmp_path, code)
        assert len(issues) >= 1


class TestAllowsProperHandling:
    """Ensures exceptions with logging or re-raise are not flagged."""

    def test_allows_logger_debug(self, tmp_path):
        code = """
try:
    do_work()
except Exception:
    logger.debug("Expected failure")
"""
        issues = _write_and_check(tmp_path, code)
        assert len(issues) == 0

    def test_allows_logger_warning(self, tmp_path):
        code = """
try:
    do_work()
except Exception:
    logger.warning("Something went wrong")
"""
        issues = _write_and_check(tmp_path, code)
        assert len(issues) == 0

    def test_allows_logger_exception(self, tmp_path):
        code = """
try:
    do_work()
except Exception:
    logger.exception("Unexpected error")
"""
        issues = _write_and_check(tmp_path, code)
        assert len(issues) == 0

    def test_allows_reraise(self, tmp_path):
        code = """
try:
    do_work()
except Exception:
    raise
"""
        issues = _write_and_check(tmp_path, code)
        assert len(issues) == 0

    def test_allows_noqa_comment(self, tmp_path):
        code = """
try:
    do_work()
except Exception:  # noqa: silent-exception
    pass
"""
        issues = _write_and_check(tmp_path, code)
        assert len(issues) == 0


class TestAllowsSpecificExceptions:
    """Ensures catching specific exceptions (not Exception) is allowed."""

    def test_allows_value_error_pass(self, tmp_path):
        code = """
try:
    int("abc")
except ValueError:
    pass
"""
        issues = _write_and_check(tmp_path, code)
        assert len(issues) == 0

    def test_allows_type_error_pass(self, tmp_path):
        code = """
try:
    len(None)
except TypeError:
    pass
"""
        issues = _write_and_check(tmp_path, code)
        assert len(issues) == 0

    def test_allows_key_error_pass(self, tmp_path):
        code = """
try:
    d = {}
    val = d["missing"]
except KeyError:
    pass
"""
        issues = _write_and_check(tmp_path, code)
        assert len(issues) == 0
