"""Tests for Tru-Guard scanners."""

import pytest

from trusys.scanners import BaseScanner, ScannerRegistry, register_scanner
from trusys.types import ActionType, ScanContext, ScanResult, ScanType
from trusys.exceptions import ScannerAlreadyRegisteredError, ScannerNotFoundError


class TestBaseScanner:
    """Tests for BaseScanner class."""

    def test_scanner_initialization(self, simple_scanner_class):
        scanner = simple_scanner_class()
        assert scanner.name == "simple_test"
        assert scanner.on_fail == ActionType.FAIL  # default
        assert scanner.enabled is True

    def test_scanner_custom_on_fail(self, simple_scanner_class):
        scanner = simple_scanner_class(on_fail="fix")
        assert scanner.on_fail == ActionType.FIX

    def test_scanner_on_fail_enum(self, simple_scanner_class):
        scanner = simple_scanner_class(on_fail=ActionType.FILTER)
        assert scanner.on_fail == ActionType.FILTER

    def test_scanner_disabled(self, simple_scanner_class):
        scanner = simple_scanner_class(enabled=False)
        assert scanner.enabled is False

    def test_scanner_scan_types(self, simple_scanner_class):
        scanner = simple_scanner_class()
        assert ScanType.INPUT in scanner.scan_types
        assert ScanType.OUTPUT in scanner.scan_types

    def test_supports_scan_type(self, simple_scanner_class):
        scanner = simple_scanner_class()
        assert scanner.supports_scan_type(ScanType.INPUT)
        assert scanner.supports_scan_type(ScanType.OUTPUT)

    def test_scan_passing(self, simple_scanner_class):
        scanner = simple_scanner_class(should_pass=True)
        context = ScanContext(scan_type=ScanType.INPUT, prompt="test")
        result = scanner.scan("test", context)
        assert result.passed is True

    def test_scan_failing(self, simple_scanner_class):
        scanner = simple_scanner_class(should_pass=False)
        context = ScanContext(scan_type=ScanType.INPUT, prompt="test")
        result = scanner.scan("test", context)
        assert result.passed is False
        assert len(result.violations) == 1

    def test_get_config(self, simple_scanner_class):
        scanner = simple_scanner_class(on_fail="fix", enabled=True)
        config = scanner.get_config()
        assert config["name"] == "simple_test"
        assert config["on_fail"] == "fix"
        assert config["enabled"] is True


class TestScannerRegistry:
    """Tests for ScannerRegistry class."""

    def test_register_scanner(self, simple_scanner_class):
        assert ScannerRegistry.is_registered("simple_test")
        assert "simple_test" in ScannerRegistry.list_scanners()

    def test_get_scanner(self, simple_scanner_class):
        scanner_class = ScannerRegistry.get_scanner("simple_test")
        assert scanner_class is simple_scanner_class

    def test_get_scanner_not_found(self):
        with pytest.raises(ScannerNotFoundError):
            ScannerRegistry.get_scanner("nonexistent")

    def test_duplicate_registration(self, simple_scanner_class):
        with pytest.raises(ScannerAlreadyRegisteredError):

            @register_scanner(name="simple_test", scan_types=["input"])
            class DuplicateScanner(BaseScanner):
                def scan(self, content, context):
                    pass

    def test_get_scanner_info(self, simple_scanner_class):
        info = ScannerRegistry.get_scanner_info("simple_test")
        assert info.name == "simple_test"
        assert ScanType.INPUT in info.scan_types
        assert ScanType.OUTPUT in info.scan_types

    def test_unregister_scanner(self, simple_scanner_class):
        ScannerRegistry.unregister("simple_test")
        assert not ScannerRegistry.is_registered("simple_test")

    def test_unregister_nonexistent(self):
        with pytest.raises(ScannerNotFoundError):
            ScannerRegistry.unregister("nonexistent")

    def test_create_scanner(self, simple_scanner_class):
        scanner = ScannerRegistry.create_scanner("simple_test", on_fail="fix")
        assert scanner.name == "simple_test"
        assert scanner.on_fail == ActionType.FIX

    def test_get_all_metadata(self, simple_scanner_class, keyword_scanner_class):
        metadata = ScannerRegistry.get_all_metadata()
        assert "simple_test" in metadata
        assert "keyword_scanner" in metadata


class TestKeywordScanner:
    """Tests for the keyword scanner fixture."""

    def test_no_keywords_found(self, keyword_scanner_class):
        scanner = keyword_scanner_class(keywords=["forbidden"])
        context = ScanContext(scan_type=ScanType.INPUT, prompt="Hello world")
        result = scanner.scan("Hello world", context)
        assert result.passed is True
        assert len(result.violations) == 0

    def test_keyword_found(self, keyword_scanner_class):
        scanner = keyword_scanner_class(keywords=["bad"])
        context = ScanContext(scan_type=ScanType.INPUT, prompt="This is bad")
        result = scanner.scan("This is bad", context)
        assert result.passed is False
        assert len(result.violations) == 1
        assert result.violations[0].span is not None

    def test_multiple_keywords_found(self, keyword_scanner_class):
        scanner = keyword_scanner_class(keywords=["bad", "wrong"])
        context = ScanContext(scan_type=ScanType.INPUT, prompt="bad and wrong")
        result = scanner.scan("bad and wrong", context)
        assert result.passed is False
        assert len(result.violations) == 2
