"""Tests for CLI error handler (cli_errors.py)."""

import pytest
import typer
from typer.testing import CliRunner

from autodebug.cli_errors import (
    _HTTP_REMEDIATION,
    _REMEDIATION,
    _handle_exception,
    handle_cli_errors,
)

runner = CliRunner()


class TestHandleCliErrors:
    """Test the @handle_cli_errors decorator."""

    def test_normal_return(self):
        """Decorated function returns normally."""

        @handle_cli_errors
        def good_func():
            return "ok"

        assert good_func() == "ok"

    def test_keyboard_interrupt(self):
        """KeyboardInterrupt raises typer.Exit(130)."""

        @handle_cli_errors
        def interrupted():
            raise KeyboardInterrupt()

        with pytest.raises(typer.Exit) as exc_info:
            interrupted()
        assert exc_info.value.exit_code == 130

    def test_system_exit_passthrough(self):
        """SystemExit is re-raised without wrapping."""

        @handle_cli_errors
        def sys_exit():
            raise SystemExit(42)

        with pytest.raises(SystemExit) as exc_info:
            sys_exit()
        assert exc_info.value.code == 42

    def test_typer_exit_passthrough(self):
        """typer.Exit is re-raised without wrapping."""

        @handle_cli_errors
        def typer_exit():
            raise typer.Exit(0)

        with pytest.raises(typer.Exit) as exc_info:
            typer_exit()
        assert exc_info.value.exit_code == 0

    def test_generic_exception(self):
        """Generic exceptions are caught and wrapped as typer.Exit(1)."""

        @handle_cli_errors
        def bad_func():
            raise RuntimeError("something went wrong")

        with pytest.raises(typer.Exit) as exc_info:
            bad_func()
        assert exc_info.value.exit_code == 1

    def test_connection_error(self):
        """ConnectionError is handled with remediation."""

        @handle_cli_errors
        def conn_fail():
            raise ConnectionError("cannot connect")

        with pytest.raises(typer.Exit) as exc_info:
            conn_fail()
        assert exc_info.value.exit_code == 1

    def test_file_not_found(self):
        """FileNotFoundError is handled with remediation."""

        @handle_cli_errors
        def missing_file():
            raise FileNotFoundError("no such file")

        with pytest.raises(typer.Exit) as exc_info:
            missing_file()
        assert exc_info.value.exit_code == 1

    def test_permission_error(self):
        """PermissionError is handled with remediation."""

        @handle_cli_errors
        def perm_denied():
            raise PermissionError("access denied")

        with pytest.raises(typer.Exit) as exc_info:
            perm_denied()
        assert exc_info.value.exit_code == 1

    def test_preserves_function_name(self):
        """Decorator preserves original function name."""

        @handle_cli_errors
        def original_name():
            pass

        assert original_name.__name__ == "original_name"


class TestRemediationMappings:
    """Test that remediation mappings are complete."""

    def test_remediation_has_connection_error(self):
        assert "ConnectionError" in _REMEDIATION

    def test_remediation_has_timeout_error(self):
        assert "TimeoutError" in _REMEDIATION

    def test_remediation_has_file_not_found(self):
        assert "FileNotFoundError" in _REMEDIATION

    def test_remediation_has_json_decode_error(self):
        assert "JSONDecodeError" in _REMEDIATION

    def test_remediation_has_permission_error(self):
        assert "PermissionError" in _REMEDIATION

    def test_http_remediation_401(self):
        assert 401 in _HTTP_REMEDIATION
        msg, detail = _HTTP_REMEDIATION[401]
        assert "login" in detail.lower()

    def test_http_remediation_403(self):
        assert 403 in _HTTP_REMEDIATION

    def test_http_remediation_404(self):
        assert 404 in _HTTP_REMEDIATION

    def test_http_remediation_429(self):
        assert 429 in _HTTP_REMEDIATION

    def test_http_remediation_500(self):
        assert 500 in _HTTP_REMEDIATION

    def test_http_remediation_502(self):
        assert 502 in _HTTP_REMEDIATION

    def test_http_remediation_503(self):
        assert 503 in _HTTP_REMEDIATION

    def test_all_remediations_have_two_fields(self):
        """Each remediation should have (message, detail) tuple."""
        for key, value in _REMEDIATION.items():
            assert len(value) == 2, f"_REMEDIATION[{key}] should have 2 items"
        for key, value in _HTTP_REMEDIATION.items():
            assert len(value) == 2, f"_HTTP_REMEDIATION[{key}] should have 2 items"


class TestHandleException:
    """Test _handle_exception helper."""

    def test_http_status_from_attribute(self):
        """Test HTTP error detection via status_code attribute."""

        class FakeHTTPError(Exception):
            def __init__(self, status_code):
                self.status_code = status_code

        # Should not raise (only prints)
        _handle_exception(FakeHTTPError(401))

    def test_http_status_from_response(self):
        """Test HTTP error detection via response.status_code."""

        class FakeResponse:
            status_code = 403

        class FakeHTTPError(Exception):
            def __init__(self):
                self.response = FakeResponse()

        _handle_exception(FakeHTTPError())

    def test_unknown_exception_fallback(self):
        """Unknown exceptions get generic message."""

        class CustomError(Exception):
            pass

        # Should not raise
        _handle_exception(CustomError("something odd"))
