"""Tests for Tru-Guard types."""

import pytest

from trusys.types import (
    ActionType,
    AggregatedResult,
    ConflictStrategy,
    ExecutionMode,
    ScanContext,
    ScanResult,
    ScanType,
    Severity,
    Violation,
)


class TestEnums:
    """Tests for enum types."""

    def test_scan_type_values(self):
        assert ScanType.INPUT.value == "input"
        assert ScanType.OUTPUT.value == "output"

    def test_action_type_values(self):
        assert ActionType.FIX.value == "fix"
        assert ActionType.MASK.value == "mask"
        assert ActionType.ENCRYPT.value == "encrypt"
        assert ActionType.FILTER.value == "filter"
        assert ActionType.FAIL.value == "fail"
        assert ActionType.IGNORE.value == "ignore"
        assert ActionType.CUSTOM.value == "custom"

    def test_execution_mode_values(self):
        assert ExecutionMode.PARALLEL.value == "parallel"
        assert ExecutionMode.SEQUENTIAL.value == "sequential"
        assert ExecutionMode.PIPELINE.value == "pipeline"

    def test_conflict_strategy_values(self):
        assert ConflictStrategy.PRIORITY.value == "priority"
        assert ConflictStrategy.FIRST_WINS.value == "first_wins"
        assert ConflictStrategy.RAISE_ERROR.value == "raise_error"

    def test_severity_values(self):
        assert Severity.LOW.value == "low"
        assert Severity.MEDIUM.value == "medium"
        assert Severity.HIGH.value == "high"
        assert Severity.CRITICAL.value == "critical"


class TestViolation:
    """Tests for Violation dataclass."""

    def test_basic_violation(self):
        v = Violation(
            rule="test_rule",
            severity=Severity.HIGH,
            message="Test message",
        )
        assert v.rule == "test_rule"
        assert v.severity == Severity.HIGH
        assert v.message == "Test message"
        assert v.span is None
        assert v.metadata == {}

    def test_violation_with_span(self):
        v = Violation(
            rule="test_rule",
            severity=Severity.LOW,
            message="Test",
            span=(0, 10),
        )
        assert v.span == (0, 10)

    def test_violation_string_severity_conversion(self):
        v = Violation(
            rule="test_rule",
            severity="high",  # type: ignore
            message="Test",
        )
        assert v.severity == Severity.HIGH


class TestScanResult:
    """Tests for ScanResult dataclass."""

    def test_basic_scan_result(self):
        result = ScanResult(
            passed=True,
            scanner_name="test_scanner",
            scan_type=ScanType.INPUT,
        )
        assert result.passed is True
        assert result.scanner_name == "test_scanner"
        assert result.scan_type == ScanType.INPUT
        assert result.violations == []
        assert result.suggested_fix is None

    def test_scan_result_with_violations(self):
        violation = Violation(
            rule="test",
            severity=Severity.MEDIUM,
            message="Test",
        )
        result = ScanResult(
            passed=False,
            scanner_name="test_scanner",
            scan_type=ScanType.OUTPUT,
            violations=[violation],
        )
        assert result.passed is False
        assert len(result.violations) == 1

    def test_scan_result_string_scan_type_conversion(self):
        result = ScanResult(
            passed=True,
            scanner_name="test",
            scan_type="input",  # type: ignore
        )
        assert result.scan_type == ScanType.INPUT


class TestScanContext:
    """Tests for ScanContext dataclass."""

    def test_input_context(self):
        ctx = ScanContext(
            scan_type=ScanType.INPUT,
            prompt="Hello",
        )
        assert ctx.get_content() == "Hello"

    def test_output_context(self):
        ctx = ScanContext(
            scan_type=ScanType.OUTPUT,
            response="World",
        )
        assert ctx.get_content() == "World"

    def test_output_context_with_prompt(self):
        """Test that output context can access prompt for reference."""
        ctx = ScanContext(
            scan_type=ScanType.OUTPUT,
            prompt="Hello",
            response="World",
        )
        content = ctx.get_content()
        assert content == "World"
        # Prompt is still available in context for scanners that need it
        assert ctx.prompt == "Hello"

    def test_string_scan_type_conversion(self):
        ctx = ScanContext(
            scan_type="input",  # type: ignore
            prompt="Test",
        )
        assert ctx.scan_type == ScanType.INPUT


class TestAggregatedResult:
    """Tests for AggregatedResult dataclass."""

    def test_basic_aggregated_result(self):
        result = AggregatedResult(passed=True)
        assert result.passed is True
        assert result.results == []
        assert result.final_content is None
        assert result.actions_taken == []

    def test_failed_scanners_property(self):
        results = [
            ScanResult(passed=True, scanner_name="scanner1", scan_type=ScanType.INPUT),
            ScanResult(passed=False, scanner_name="scanner2", scan_type=ScanType.INPUT),
            ScanResult(passed=False, scanner_name="scanner3", scan_type=ScanType.INPUT),
        ]
        agg = AggregatedResult(passed=False, results=results)
        assert agg.failed_scanners == ["scanner2", "scanner3"]

    def test_all_violations_property(self):
        v1 = Violation(rule="r1", severity=Severity.LOW, message="m1")
        v2 = Violation(rule="r2", severity=Severity.HIGH, message="m2")
        results = [
            ScanResult(
                passed=False,
                scanner_name="scanner1",
                scan_type=ScanType.INPUT,
                violations=[v1],
            ),
            ScanResult(
                passed=False,
                scanner_name="scanner2",
                scan_type=ScanType.INPUT,
                violations=[v2],
            ),
        ]
        agg = AggregatedResult(passed=False, results=results)
        assert len(agg.all_violations) == 2
