"""
Control Status Transition State Machine.

Enforces that controls cannot be set to "pass" without sufficient evidence.
Prevents Compliance-Theater where controls claim compliance without real proof.

Transition rules:
  planned     → in_progress : always allowed
  in_progress → pass        : requires ≥1 evidence with confidence ≥ E2 and
                              truth_status in (uploaded, observed, validated_internal)
  in_progress → partial     : requires ≥1 evidence (any level)
  pass        → fail        : always allowed (degradation)
  any         → n/a         : requires status_justification
  any         → planned     : always allowed (reset)
"""

from typing import List, Optional, Tuple

from ..db.models import EvidenceDB


# Confidence level ordering for comparisons
CONFIDENCE_ORDER = {"E0": 0, "E1": 1, "E2": 2, "E3": 3, "E4": 4}

# Truth statuses that qualify as "real" evidence for pass transitions
VALID_TRUTH_STATUSES = {"uploaded", "observed", "validated_internal", "accepted_by_auditor", "provided_to_auditor"}


def validate_transition(
    current_status: str,
    new_status: str,
    evidence_list: Optional[List[EvidenceDB]] = None,
    status_justification: Optional[str] = None,
    bypass_for_auto_updater: bool = False,
) -> Tuple[bool, List[str]]:
    """
    Validate whether a control status transition is allowed.

    Args:
        current_status: Current control status value (e.g. "planned", "pass")
        new_status: Requested new status
        evidence_list: List of EvidenceDB objects linked to this control
        status_justification: Text justification (required for n/a transitions)
        bypass_for_auto_updater: If True, skip evidence checks (used by CI/CD auto-updater
                                 which creates evidence atomically with status change)

    Returns:
        Tuple of (allowed: bool, violations: list[str])
    """
    violations: List[str] = []
    evidence_list = evidence_list or []

    # Same status → no-op, always allowed
    if current_status == new_status:
        return True, []

    # Reset to planned is always allowed
    if new_status == "planned":
        return True, []

    # n/a requires justification
    if new_status == "n/a":
        if not status_justification or not status_justification.strip():
            violations.append("Transition to 'n/a' requires a status_justification explaining why this control is not applicable.")
        return len(violations) == 0, violations

    # Degradation: pass → fail is always allowed
    if current_status == "pass" and new_status == "fail":
        return True, []

    # planned → in_progress: always allowed
    if current_status == "planned" and new_status == "in_progress":
        return True, []

    # in_progress → partial: needs at least 1 evidence
    if new_status == "partial":
        if not bypass_for_auto_updater and len(evidence_list) == 0:
            violations.append("Transition to 'partial' requires at least 1 evidence record.")
        return len(violations) == 0, violations

    # in_progress → pass: strict requirements
    if new_status == "pass":
        if bypass_for_auto_updater:
            return True, []

        if len(evidence_list) == 0:
            violations.append("Transition to 'pass' requires at least 1 evidence record.")
            return False, violations

        # Check for at least one qualifying evidence
        has_qualifying = False
        for e in evidence_list:
            conf = getattr(e, "confidence_level", None)
            truth = getattr(e, "truth_status", None)

            # Get string values from enum or string
            conf_val = conf.value if hasattr(conf, "value") else str(conf) if conf else "E1"
            truth_val = truth.value if hasattr(truth, "value") else str(truth) if truth else "uploaded"

            if CONFIDENCE_ORDER.get(conf_val, 1) >= CONFIDENCE_ORDER["E2"] and truth_val in VALID_TRUTH_STATUSES:
                has_qualifying = True
                break

        if not has_qualifying:
            violations.append(
                "Transition to 'pass' requires at least 1 evidence with confidence >= E2 "
                "and truth_status in (uploaded, observed, validated_internal, accepted_by_auditor). "
                "Current evidence does not meet this threshold."
            )

        return len(violations) == 0, violations

    # in_progress → fail: always allowed
    if new_status == "fail":
        return True, []

    # Any other transition from planned/fail to pass requires going through in_progress
    if current_status in ("planned", "fail") and new_status == "pass":
        if bypass_for_auto_updater:
            return True, []
        violations.append(
            f"Direct transition from '{current_status}' to 'pass' is not allowed. "
            f"Move to 'in_progress' first, then to 'pass' with qualifying evidence."
        )
        return False, violations

    # Default: allow other transitions (e.g. fail → partial, partial → pass)
    # For partial → pass, apply the same evidence checks
    if current_status == "partial" and new_status == "pass":
        if bypass_for_auto_updater:
            return True, []

        has_qualifying = False
        for e in evidence_list:
            conf = getattr(e, "confidence_level", None)
            truth = getattr(e, "truth_status", None)
            conf_val = conf.value if hasattr(conf, "value") else str(conf) if conf else "E1"
            truth_val = truth.value if hasattr(truth, "value") else str(truth) if truth else "uploaded"

            if CONFIDENCE_ORDER.get(conf_val, 1) >= CONFIDENCE_ORDER["E2"] and truth_val in VALID_TRUTH_STATUSES:
                has_qualifying = True
                break

        if not has_qualifying:
            violations.append(
                "Transition from 'partial' to 'pass' requires at least 1 evidence with confidence >= E2 "
                "and truth_status in (uploaded, observed, validated_internal, accepted_by_auditor)."
            )
        return len(violations) == 0, violations

    # All other transitions allowed
    return True, []
