"""Firmware/profile compatibility decisions that fail closed for active control."""

from __future__ import annotations

from enum import StrEnum
from typing import Literal

from pydantic import AwareDatetime, Field, model_validator

from .common import API_VERSION, ApiVersion, ContractModel, Identifier, LongText, ShortText
from .identity import DeviceInstanceRef


class CompatibilityDecision(StrEnum):
    COMPATIBLE = "compatible"
    LIMITED = "limited"
    UNKNOWN = "unknown"
    INCOMPATIBLE = "incompatible"


class PermittedMode(StrEnum):
    BLOCKED = "blocked"
    EVIDENCE_ONLY = "evidence-only"
    READ_ONLY = "read-only"
    ACTIVE_CONTROL = "active-control"


class RuleOutcome(StrEnum):
    PASS = "pass"
    FAIL = "fail"
    UNKNOWN = "unknown"


class CompatibilityRuleResult(ContractModel):
    rule_id: Identifier
    outcome: RuleOutcome
    blocking: bool
    detail: LongText
    evidence_ids: tuple[Identifier, ...] = Field(default_factory=tuple)


class CompatibilityAssessment(ContractModel):
    api_version: ApiVersion = API_VERSION
    kind: Literal["CompatibilityAssessment"] = "CompatibilityAssessment"
    assessment_id: Identifier
    device: DeviceInstanceRef
    evaluated_at: AwareDatetime
    observed_firmware: ShortText | None = None
    profile_id: Identifier | None = None
    decision: CompatibilityDecision
    permitted_mode: PermittedMode
    rules: tuple[CompatibilityRuleResult, ...] = Field(min_length=1)
    supported_capabilities: tuple[Identifier, ...] = Field(default_factory=tuple)
    supported_actions: tuple[Identifier, ...] = Field(default_factory=tuple)
    notes: LongText | None = None

    @model_validator(mode="after")
    def compatibility_decision_must_fail_closed(self) -> CompatibilityAssessment:
        blocking_failures = [
            rule for rule in self.rules if rule.blocking and rule.outcome is RuleOutcome.FAIL
        ]
        unresolved_blockers = [
            rule for rule in self.rules if rule.blocking and rule.outcome is RuleOutcome.UNKNOWN
        ]
        if self.decision is CompatibilityDecision.INCOMPATIBLE and (
            self.permitted_mode is not PermittedMode.BLOCKED or not blocking_failures
        ):
            raise ValueError("incompatible requires blocked mode and a failed blocking rule")
        if self.permitted_mode is PermittedMode.ACTIVE_CONTROL:
            if self.decision is not CompatibilityDecision.COMPATIBLE:
                raise ValueError("active control requires a compatible decision")
            if self.profile_id is None:
                raise ValueError("active control requires a compatibility profile")
            if blocking_failures or unresolved_blockers:
                raise ValueError("active control requires every blocking rule to pass")
        if self.decision is CompatibilityDecision.UNKNOWN and self.permitted_mode not in {
            PermittedMode.BLOCKED,
            PermittedMode.EVIDENCE_ONLY,
        }:
            raise ValueError("unknown compatibility permits only blocked or evidence-only mode")
        for label, values in (
            ("capabilities", self.supported_capabilities),
            ("actions", self.supported_actions),
        ):
            if len(values) != len(set(values)):
                raise ValueError(f"supported {label} must be unique")
        return self
