"""Tests for Company Intelligence (Phase 2A) — Company Capability Profile.

Acceptance: from a CompanyContext (certifications, declarations, evidence) the
engine derives operational capabilities with a four-state trust model and a HARD
RULE: a certification is NEVER auto-treated as "erfuellt" — at most INFERRED.

The Certification->Capability mapping is Execution's domain. It is injected here as
a MOCK (the yaml-like dict below lives ONLY in tests); product code ships no table.
"""

from __future__ import annotations

from compliance.company import (
    CapabilityMappingEntry,
    Certification,
    CompanyContext,
    Declaration,
    ExistingEvidence,
    VerificationStatus,
    build_company_profile,
)
from compliance.reasoning.enums import Confidence

# --- MOCK mapping (Execution-owned in reality; here only for the tests) -------
# mapping:
#   ISO27001 -> [cap_patch_management, cap_supplier_management]
MOCK_MAPPING = {
    "ISO27001": CapabilityMappingEntry(
        capability_ids=["cap_patch_management", "cap_supplier_management"],
        confidence=Confidence.MEDIUM,
    )
}


def _candidate(profile, capability_id):
    return [c for c in profile.candidate_capabilities if c.capability_id == capability_id]


def _confirmed_ids(profile):
    return {c.capability_id for c in profile.confirmed_capabilities}


# A certification yields INFERRED candidates via the injected mapping.
def test_certification_infers_candidates_via_injected_mapping():
    ctx = CompanyContext(company_id="acme", certifications=[Certification(certification_id="ISO27001")])
    profile = build_company_profile(ctx, MOCK_MAPPING)
    ids = {c.capability_id for c in profile.candidate_capabilities}
    assert ids == {"cap_patch_management", "cap_supplier_management"}
    for c in profile.candidate_capabilities:
        assert c.verification_status == VerificationStatus.INFERRED
        assert c.source == "certification:ISO27001"


# Without an injected mapping there are NO inferred capabilities — only the claim.
# This is the architectural guarantee that the table lives only in Execution.
def test_no_mapping_no_inferred_capabilities():
    ctx = CompanyContext(company_id="acme", certifications=[Certification(certification_id="ISO27001")])
    profile = build_company_profile(ctx)  # default EMPTY mapping
    assert profile.candidate_capabilities == []
    # the certification still produced evidence-of-claim (refinement 1)
    assert len(profile.capability_evidence) == 1
    assert profile.capability_evidence[0].source == "certification:ISO27001"
    assert profile.capability_evidence[0].certification_id == "ISO27001"


# A customer declaration yields a DECLARED candidate.
def test_declaration_yields_declared_candidate():
    ctx = CompanyContext(company_id="acme", declarations=[Declaration(capability_id="cap_patch_management")])
    profile = build_company_profile(ctx, MOCK_MAPPING)
    cands = _candidate(profile, "cap_patch_management")
    assert len(cands) == 1
    assert cands[0].verification_status == VerificationStatus.DECLARED


# declared + inferred coexist as distinct signals for the same capability.
def test_declared_and_inferred_coexist():
    ctx = CompanyContext(
        company_id="acme",
        certifications=[Certification(certification_id="ISO27001")],
        declarations=[Declaration(capability_id="cap_patch_management")],
    )
    profile = build_company_profile(ctx, MOCK_MAPPING)
    statuses = {c.verification_status for c in _candidate(profile, "cap_patch_management")}
    assert statuses == {VerificationStatus.DECLARED, VerificationStatus.INFERRED}


# HARD RULE: a certification alone NEVER yields a confirmed capability.
def test_hard_rule_certification_never_confirmed():
    ctx = CompanyContext(company_id="acme", certifications=[Certification(certification_id="ISO27001")])
    profile = build_company_profile(ctx, MOCK_MAPPING)
    assert _confirmed_ids(profile) == set()
    for c in profile.candidate_capabilities:
        assert c.verification_status != VerificationStatus.CONFIRMED


# Only real evidence confirms a capability — and it leaves the candidate list.
def test_evidence_confirms_capability():
    ctx = CompanyContext(
        company_id="acme",
        certifications=[Certification(certification_id="ISO27001")],
        evidence=[ExistingEvidence(evidence_id="pol-1", evidence_type="policy", proves_capability_id="cap_patch_management")],
    )
    profile = build_company_profile(ctx, MOCK_MAPPING)
    assert "cap_patch_management" in _confirmed_ids(profile)
    confirmed = [c for c in profile.confirmed_capabilities if c.capability_id == "cap_patch_management"][0]
    assert confirmed.verification_status == VerificationStatus.CONFIRMED
    assert confirmed.confidence == Confidence.HIGH
    assert confirmed.sources == ["pol-1"]
    # a confirmed capability is no longer a mere candidate
    assert _candidate(profile, "cap_patch_management") == []
    # the un-proven capability stays an inferred candidate
    assert _candidate(profile, "cap_supplier_management")[0].verification_status == VerificationStatus.INFERRED


# The four-state vocabulary exists and is ordered declared->inferred->confirmed (+unknown).
def test_four_states_present():
    assert {s.value for s in VerificationStatus} == {"declared", "inferred", "confirmed", "unknown"}


# verification_status is a FOURTH vocabulary, disjoint from ClaimCoverage and DeltaType.
def test_verification_status_distinct_vocabulary():
    from compliance.rci.schemas import DeltaType
    from compliance.reasoning.enums import ClaimCoverage

    verif = {s.value for s in VerificationStatus}
    assert verif.isdisjoint({c.value for c in ClaimCoverage})
    assert verif.isdisjoint({d.value for d in DeltaType})
