"""
tests/test_schema.py — Schema contract tests.

These are intentionally minimal. You will write the real tests.
The purpose here is just to catch regressions in the output contract —
if these fail, the JSON schema has broken and customer output is affected.
"""

import json
import pytest
from sichgate.schema import (
    AttackResult, ExecutionTelemetry, RunReport, RunSummary, SCHEMA_VERSION
)


def _make_telemetry(**overrides) -> ExecutionTelemetry:
    defaults = dict(
        status="completed",
        model_reachable=True,
        attacks_attempted=1,
        attacks_completed=1,
        duration_ms=100,
        reason=None,
    )
    defaults.update(overrides)
    return ExecutionTelemetry(**defaults)


def _make_result(**overrides) -> AttackResult:
    defaults = dict(
        attack_type="llm",
        attack_name="test_attack",
        model_target="test_model",
        timestamp=AttackResult.now_iso(),
        status="pass",
        severity="low",
        input_payload="test input",
        model_response="test response",
        notes="test notes",
        compliance_tags=["GDPR Art. 32"],
        execution_telemetry=_make_telemetry(),
    )
    defaults.update(overrides)
    return AttackResult(**defaults)


class TestAttackResultSchema:
    def test_required_fields_present(self):
        result = _make_result()
        d = result.to_dict()
        required = [
            "attack_type", "attack_name", "model_target", "timestamp",
            "status", "severity", "input_payload", "model_response",
            "notes", "compliance_tags", "execution_telemetry",
        ]
        for field in required:
            assert field in d, f"Missing required field: {field}"

    def test_compliance_tags_is_list_of_strings(self):
        result = _make_result(compliance_tags=["GDPR Art. 32", "HIPAA §164.312"])
        d = result.to_dict()
        assert isinstance(d["compliance_tags"], list)
        assert all(isinstance(t, str) for t in d["compliance_tags"])

    def test_status_valid_values(self):
        for status in ("pass", "fail", "warning"):
            result = _make_result(status=status)
            assert result.to_dict()["status"] == status

    def test_severity_valid_values(self):
        for severity in ("critical", "high", "medium", "low"):
            result = _make_result(severity=severity)
            assert result.to_dict()["severity"] == severity

    def test_execution_telemetry_fields(self):
        result = _make_result()
        telem = result.to_dict()["execution_telemetry"]
        assert "status" in telem
        assert "model_reachable" in telem
        assert "attacks_attempted" in telem
        assert "attacks_completed" in telem
        assert "duration_ms" in telem

    def test_internal_fields_excluded(self):
        """run_id and result_id are internal and should NOT be in public JSON."""
        result = _make_result()
        d = result.to_dict()
        assert "run_id" not in d
        assert "result_id" not in d

    def test_serializes_to_valid_json(self):
        result = _make_result()
        json_str = json.dumps(result.to_dict())
        parsed = json.loads(json_str)
        assert parsed["attack_name"] == "test_attack"

    def test_unreachable_telemetry(self):
        telem = ExecutionTelemetry.unreachable()
        assert telem.model_reachable is False
        assert telem.status == "failed"
        assert telem.reason is not None


class TestRunSummary:
    def test_from_results_counts(self):
        results = [
            _make_result(status="fail", severity="critical"),
            _make_result(status="fail", severity="high"),
            _make_result(status="warning", severity="medium"),
            _make_result(status="pass", severity="low"),
        ]
        summary = RunSummary.from_results("run-1", "test_model", results)
        assert summary.total_attacks == 4
        assert summary.failed == 2
        assert summary.warnings == 1
        assert summary.passed == 1
        assert summary.critical_count == 1
        assert summary.high_count == 1

    def test_compliance_tags_aggregated(self):
        results = [
            _make_result(status="fail", compliance_tags=["GDPR Art. 32"]),
            _make_result(status="fail", compliance_tags=["GDPR Art. 32", "HIPAA §164.312"]),
        ]
        summary = RunSummary.from_results("run-2", "test_model", results)
        assert "GDPR Art. 32" in summary.compliance_tags_hit
        assert "HIPAA §164.312" in summary.compliance_tags_hit


class TestRunReport:
    def test_schema_version_present(self):
        results = [_make_result()]
        summary = RunSummary.from_results("run-3", "test_model", results)
        report = RunReport(schema_version=SCHEMA_VERSION, summary=summary, results=results)
        d = report.to_dict()
        assert d["schema_version"] == SCHEMA_VERSION
        assert "summary" in d
        assert "results" in d
        assert len(d["results"]) == 1
