from __future__ import annotations

from datetime import datetime, timezone
import hashlib
import unittest

from govoplan_core.core.sanctions import (
    CAPABILITY_CONNECTORS_SANCTIONS_SNAPSHOTS,
    CAPABILITY_RISK_COMPLIANCE_SANCTIONS_SCREENING,
    SanctionsScreeningEvidence,
    SanctionsScreeningFreshness,
    SanctionsScreeningFreshnessRequest,
    SanctionsScreeningPolicy,
    SanctionsScreeningProvider,
    SanctionsScreeningRequest,
    SanctionsScreeningResult,
    SanctionsScreeningSubject,
    SanctionsSnapshotPayload,
    SanctionsSnapshotProvider,
    SanctionsSnapshotReference,
    sanctions_screening_provider,
    sanctions_snapshot_provider,
)


class _Provider:
    def list_snapshots(self, session, principal, *, limit=100):
        del session, principal, limit
        return ()

    def get_snapshot(self, session, principal, *, snapshot_ref):
        del session, principal, snapshot_ref
        return None

    def read_snapshot(self, session, principal, *, snapshot_ref):
        del session, principal, snapshot_ref
        raise LookupError


class _Registry:
    def __init__(self, provider):
        self.provider = provider

    def has_capability(self, name):
        return name == CAPABILITY_CONNECTORS_SANCTIONS_SNAPSHOTS

    def capability(self, name):
        return self.provider if self.has_capability(name) else None


class _ScreeningProvider:
    def request_screening(self, session, principal, request):
        del session, principal, request
        raise NotImplementedError

    def check_freshness(self, session, principal, request):
        del session, principal, request
        raise NotImplementedError


class _ScreeningRegistry:
    def __init__(self, provider):
        self.provider = provider

    def has_capability(self, name):
        return name == CAPABILITY_RISK_COMPLIANCE_SANCTIONS_SCREENING

    def capability(self, name):
        return self.provider if self.has_capability(name) else None


class SanctionsContractTests(unittest.TestCase):
    def test_snapshot_evidence_is_versioned_and_bounded(self) -> None:
        content = b"<CONSOLIDATED_LIST/>"
        snapshot = SanctionsSnapshotReference(
            ref="sanctions-snapshot:snapshot-1",
            tenant_id="tenant-1",
            provider_id="un.security_council",
            publisher="United Nations Security Council",
            jurisdiction="UN",
            list_type="consolidated",
            source_id="unsc-consolidated",
            source_version="2026-07-22",
            publication_at=None,
            effective_at=None,
            acquired_at=datetime.now(timezone.utc),
            content_type="application/xml",
            byte_count=len(content),
            sha256=hashlib.sha256(content).hexdigest(),
            parser_version="unsc-xml-v1",
            raw_evidence_ref="connector-evidence:snapshot-1",
            connector_run_id="run-1",
        )

        payload = SanctionsSnapshotPayload(
            snapshot=snapshot,
            content=content,
        )
        provider = _Provider()

        self.assertEqual(content, payload.content)
        self.assertIsInstance(provider, SanctionsSnapshotProvider)
        self.assertIs(
            provider,
            sanctions_snapshot_provider(_Registry(provider)),
        )

    def test_screening_evidence_and_freshness_are_versioned(self) -> None:
        subject = SanctionsScreeningSubject(
            subject_type="person",
            primary_name="Example Person",
        )
        policy = SanctionsScreeningPolicy(failure_policy="review")
        request = SanctionsScreeningRequest(
            list_snapshot_id="snapshot-1",
            idempotency_key="request-1",
            subject=subject,
            policy=policy,
        )
        evidence = SanctionsScreeningEvidence(
            ref="risk-screening:run-1",
            run_id="run-1",
            outcome="clear",
            candidate_count=0,
            list_snapshot_id=request.list_snapshot_id,
            source_version="2026-07-30",
            subject_fingerprint="a" * 64,
            matcher_version="matcher-v1",
            normalization_version="normalization-v1",
            policy_version="policy-v1",
            completed_at=datetime.now(timezone.utc),
        )
        freshness_request = SanctionsScreeningFreshnessRequest(
            evidence_ref=evidence.ref,
            current_subject=subject,
            policy=policy,
        )
        freshness = SanctionsScreeningFreshness(
            evidence=evidence,
            fresh=True,
            reasons=(),
            checked_at=datetime.now(timezone.utc),
            current_list_snapshot_id=request.list_snapshot_id,
            gate_decision="allow",
            gate_reasons=("screening_clear",),
        )
        result = SanctionsScreeningResult(
            evidence=evidence,
            freshness=freshness,
            created=True,
        )
        provider = _ScreeningProvider()

        self.assertEqual(evidence.ref, freshness_request.evidence_ref)
        self.assertTrue(result.freshness.fresh)
        self.assertIsInstance(provider, SanctionsScreeningProvider)
        self.assertIs(
            provider,
            sanctions_screening_provider(
                _ScreeningRegistry(provider)
            ),
        )

    def test_screening_contract_rejects_ambiguous_subjects(self) -> None:
        with self.assertRaises(ValueError):
            SanctionsScreeningSubject(subject_type="entity")


if __name__ == "__main__":
    unittest.main()
