"""Tests for RegexScanner."""

import re

import pytest

from trusys import TruGuard
from trusys.scanners.builtin import RegexScanner
from trusys.types import MatchType, ScanType


class TestRegexScanner:
    """Tests for RegexScanner."""

    def test_blacklist_mode_match_fails(self):
        """Test that blacklist mode fails when pattern matches."""
        scanner = RegexScanner(
            config={
                "pattern": r"bad|forbidden",
                "match_type": MatchType.BLACKLIST,
            },
            on_fail="ignore",
        )
        from trusys.types import ScanContext

        context = ScanContext(scan_type=ScanType.INPUT, prompt="This is bad content")
        result = scanner.scan({"prompt": "This is bad content"}, context)
        assert result.passed is False
        assert len(result.violations) == 1
        assert result.violations[0].rule == "regex_blacklist_match"

    def test_blacklist_mode_no_match_passes(self):
        """Test that blacklist mode passes when pattern doesn't match."""
        scanner = RegexScanner(
            config={
                "pattern": r"bad|forbidden",
                "match_type": MatchType.BLACKLIST,
            }
        )
        from trusys.types import ScanContext

        context = ScanContext(scan_type=ScanType.INPUT, prompt="This is good content")
        result = scanner.scan({"prompt": "This is good content"}, context)
        assert result.passed is True
        assert len(result.violations) == 0

    def test_whitelist_mode_match_passes(self):
        """Test that whitelist mode passes when pattern matches."""
        scanner = RegexScanner(
            config={
                "pattern": r"^[A-Z].*\.$",
                "match_type": MatchType.WHITELIST,
            }
        )
        from trusys.types import ScanContext

        context = ScanContext(scan_type=ScanType.INPUT, prompt="This is a valid sentence.")
        result = scanner.scan({"prompt": "This is a valid sentence."}, context)
        assert result.passed is True
        assert len(result.violations) == 0

    def test_whitelist_mode_no_match_fails(self):
        """Test that whitelist mode fails when pattern doesn't match."""
        scanner = RegexScanner(
            config={
                "pattern": r"^[A-Z].*\.$",
                "match_type": MatchType.WHITELIST,
            },
            on_fail="ignore",
        )
        from trusys.types import ScanContext

        context = ScanContext(scan_type=ScanType.INPUT, prompt="invalid sentence")
        result = scanner.scan({"prompt": "invalid sentence"}, context)
        assert result.passed is False
        assert len(result.violations) == 1
        assert result.violations[0].rule == "regex_whitelist_no_match"

    def test_multiple_matches_blacklist(self):
        """Test that multiple matches create multiple violations."""
        scanner = RegexScanner(
            config={
                "pattern": r"spam|virus",
                "match_type": MatchType.BLACKLIST,
            },
            on_fail="ignore",
        )
        from trusys.types import ScanContext

        context = ScanContext(scan_type=ScanType.INPUT, prompt="This has spam and virus content")
        result = scanner.scan({"prompt": "This has spam and virus content"}, context)
        assert result.passed is False
        assert len(result.violations) == 2

    def test_case_insensitive_matching(self):
        """Test case insensitive matching with flags."""
        scanner = RegexScanner(
            config={
                "pattern": r"bad",
                "match_type": MatchType.BLACKLIST,
                "flags": re.IGNORECASE,
            },
            on_fail="ignore",
        )
        from trusys.types import ScanContext

        context = ScanContext(scan_type=ScanType.INPUT, prompt="This is BAD content")
        result = scanner.scan({"prompt": "This is BAD content"}, context)
        assert result.passed is False
        assert len(result.violations) == 1

    def test_invalid_regex_pattern(self):
        """Test that invalid regex patterns raise ValueError."""
        with pytest.raises(ValueError) as exc_info:
            RegexScanner(config={"pattern": r"[invalid", "match_type": MatchType.BLACKLIST})
        assert "Invalid regex pattern" in str(exc_info.value)

    def test_violation_spans(self):
        """Test that violations include span information for blacklist matches."""
        scanner = RegexScanner(
            config={
                "pattern": r"bad",
                "match_type": MatchType.BLACKLIST,
            },
            on_fail="ignore",
        )
        from trusys.types import ScanContext

        context = ScanContext(scan_type=ScanType.INPUT, prompt="This is bad content")
        result = scanner.scan({"prompt": "This is bad content"}, context)
        assert result.passed is False
        assert result.violations[0].span is not None
        start, end = result.violations[0].span
        assert "bad" in "This is bad content"[start:end]

    def test_metadata_in_violations(self):
        """Test that violations include matched text in metadata."""
        scanner = RegexScanner(
            config={
                "pattern": r"bad",
                "match_type": MatchType.BLACKLIST,
            },
            on_fail="ignore",
        )
        from trusys.types import ScanContext

        context = ScanContext(scan_type=ScanType.INPUT, prompt="This is bad content")
        result = scanner.scan({"prompt": "This is bad content"}, context)
        assert result.violations[0].metadata.get("matched_text") == "bad"

    def test_scan_result_metadata(self):
        """Test that scan result includes pattern and match type in metadata."""
        scanner = RegexScanner(
            config={
                "pattern": r"test",
                "match_type": MatchType.BLACKLIST,
            }
        )
        from trusys.types import ScanContext

        context = ScanContext(scan_type=ScanType.INPUT, prompt="test content")
        result = scanner.scan({"prompt": "test content"}, context)
        assert result.metadata["pattern"] == "test"
        assert result.metadata["match_type"] == "blacklist"
        assert result.metadata["match_count"] == 1

    def test_with_input_guard(self):
        """Test regex scanner with input guard."""
        guard = TruGuard(type="input")
        guard.add_scanners(
            [
                RegexScanner(
                    config={
                        "pattern": r"forbidden",
                        "match_type": MatchType.BLACKLIST,
                    },
                    on_fail="ignore",
                )
            ]
        )

        result = guard.scan(content={"prompt": "This is forbidden content"})
        assert result.passed is False

        result = guard.scan(content={"prompt": "This is allowed content"})
        assert result.passed is True

    def test_with_output_guard(self):
        """Test regex scanner with output guard."""
        guard = TruGuard(type="output")
        guard.add_scanners(
            [
                RegexScanner(
                    config={
                        "pattern": r"confidential",
                        "match_type": MatchType.BLACKLIST,
                    },
                    on_fail="ignore",
                )
            ]
        )

        result = guard.scan(content={"prompt": "user query", "response": "This is confidential information"})
        assert result.passed is False

        result = guard.scan(content={"prompt": "user query", "response": "This is public information"})
        assert result.passed is True

    def test_string_match_type(self):
        """Test that match_type can be passed as string."""
        scanner = RegexScanner(
            config={
                "pattern": r"test",
                "match_type": "blacklist",
            }
        )
        assert scanner.match_type == MatchType.BLACKLIST

    def test_default_match_type(self):
        """Test that default match_type is blacklist."""
        scanner = RegexScanner(config={"pattern": r"test"})
        assert scanner.match_type == MatchType.BLACKLIST

    def test_get_config(self):
        """Test that get_config includes regex-specific parameters."""
        scanner = RegexScanner(
            config={
                "pattern": r"test",
                "match_type": MatchType.WHITELIST,
                "flags": re.IGNORECASE,
            }
        )
        config = scanner.get_config()
        assert config["pattern"] == "test"
        assert config["match_type"] == "whitelist"
        assert config["flags"] == re.IGNORECASE
