"""v3.6 Runtime Harness — finish-task profile contract JSON schema & validation.

chair_authorization_id=CHAIR-AUTH-TASK-2706-V36-FINISH-TASK-PROFILE-LAYER-P1B-260529

Defines constants, enum sets, and the validate_profile() function for the
§11 output schema of the finish-task profile contract (P1-B).

Public API
----------
- ``SCHEMA_VERSION``            — canonical schema version string
- ``VALID_TASK_MODES``          — frozenset of valid task_mode values
- ``VALID_GRADES``              — frozenset of valid gate/overall grades
- ``VALID_DIRTY_CLASSES``       — frozenset of valid dirty_workspace_classification values
- ``VALID_ANU_ACTIONS``         — frozenset of valid anu_action_recommended values
- ``VALID_CONFIDENCE``          — frozenset of valid classification confidence values
- ``GATE_KEYS``                 — ordered list of gate field names
- ``validate_profile(record)``  — (bool, list[str])

Safe-fail: validate_profile never raises; all exceptions return (False, [error]).
"""
from __future__ import annotations

from typing import Any

# ---------------------------------------------------------------------------
# Schema version
# ---------------------------------------------------------------------------
SCHEMA_VERSION = "v36.finish_task_profile.v1"

# ---------------------------------------------------------------------------
# Enum constants
# ---------------------------------------------------------------------------
VALID_TASK_MODES = frozenset({
    "code",
    "system_hook",
    "local_runtime",
    "read_only",
    "callback_only",
    "closeout_marker_only",
    "UNKNOWN",
})

VALID_GRADES = frozenset({"PASS", "WARN", "FAIL", "ESCALATE", "N/A"})

VALID_DIRTY_CLASSES = frozenset({
    "OWN_DIRTY",
    "INHERITED_DIRTY",
    "EXTERNAL_DIRTY",
    "DAEMON_RECURRENCE",
    "UNKNOWN_DIRTY",
    "CLEAN",
})

VALID_ANU_ACTIONS = frozenset({
    "PROCEED",
    "CAVEAT_PROCEED",
    "BLOCK",
    "ESCALATE_CHAIR",
})

VALID_CONFIDENCE = frozenset({"HIGH", "MEDIUM", "LOW", "UNKNOWN"})

GATE_KEYS = [
    "G1_git_evidence",
    "G2_L1_smoke",
    "G3_scope_guard",
    "G4_dirty_workspace",
    "G5_qc_verification",
    "G6_lineage_preservation",
    "G7_actor_attribution",
    "G8_callback_enforcement",
]

# Top-level required fields
_REQUIRED_TOP = {
    "schema_version",
    "task_id",
    "task_mode",
    "task_mode_classification_evidence",
    "gates",
    "overall_result",
    "overall_rationale",
    "ts",
}


def validate_profile(record: Any) -> tuple[bool, list[str]]:
    """Validate a finish-task profile dict against the §11 JSON schema.

    Returns:
        (valid: bool, errors: list[str])

    Safe-fail: never raises; any exception returns (False, [error_message]).
    """
    try:
        errors: list[str] = []

        if not isinstance(record, dict):
            return False, ["profile record must be a dict"]

        # --- top-level required fields ---
        for field in _REQUIRED_TOP:
            if field not in record:
                errors.append(f"missing required field: {field!r}")

        # --- schema_version ---
        sv = record.get("schema_version")
        if sv is not None and sv != SCHEMA_VERSION:
            errors.append(f"schema_version mismatch: got {sv!r}, expected {SCHEMA_VERSION!r}")

        # --- task_mode ---
        tm = record.get("task_mode")
        if tm is not None and tm not in VALID_TASK_MODES:
            errors.append(f"invalid task_mode: {tm!r}; must be one of {sorted(VALID_TASK_MODES)}")

        # --- task_mode_classification_evidence ---
        tce = record.get("task_mode_classification_evidence")
        if tce is not None:
            if not isinstance(tce, dict):
                errors.append("task_mode_classification_evidence must be a dict")
            else:
                conf = tce.get("confidence")
                if conf is not None and conf not in VALID_CONFIDENCE:
                    errors.append(
                        f"invalid classification confidence: {conf!r}; "
                        f"must be one of {sorted(VALID_CONFIDENCE)}"
                    )

        # --- gates ---
        gates = record.get("gates")
        if gates is not None:
            if not isinstance(gates, dict):
                errors.append("gates must be a dict")
            else:
                for gk in GATE_KEYS:
                    if gk not in gates:
                        errors.append(f"missing gate key: {gk!r}")
                    else:
                        gate_val = gates[gk]
                        if not isinstance(gate_val, dict):
                            errors.append(f"gate {gk!r} must be a dict")
                        else:
                            result = gate_val.get("result")
                            if result is not None and result not in VALID_GRADES:
                                errors.append(
                                    f"gate {gk!r} has invalid result: {result!r}; "
                                    f"must be one of {sorted(VALID_GRADES)}"
                                )

        # --- overall_result ---
        overall = record.get("overall_result")
        if overall is not None and overall not in (VALID_GRADES - {"N/A"}):
            errors.append(
                f"invalid overall_result: {overall!r}; "
                f"must be one of PASS/WARN/FAIL/ESCALATE"
            )

        # --- anu_action_recommended (optional but validated if present) ---
        anu_action = record.get("anu_action_recommended")
        if anu_action is not None and anu_action not in VALID_ANU_ACTIONS:
            errors.append(
                f"invalid anu_action_recommended: {anu_action!r}; "
                f"must be one of {sorted(VALID_ANU_ACTIONS)}"
            )

        # --- dirty_workspace_classification (optional but validated if present) ---
        dwc = record.get("dirty_workspace_classification")
        if dwc is not None and dwc not in VALID_DIRTY_CLASSES:
            errors.append(
                f"invalid dirty_workspace_classification: {dwc!r}; "
                f"must be one of {sorted(VALID_DIRTY_CLASSES)}"
            )

        # --- ts (non-empty string if present) ---
        ts = record.get("ts")
        if ts is not None:
            if not isinstance(ts, str):
                errors.append(f"field 'ts' must be a string (ISO-8601), got {type(ts).__name__}")
            elif not ts.strip():
                errors.append("field 'ts' must not be empty")

        valid = len(errors) == 0
        return valid, errors

    except Exception as exc:
        return False, [f"validate_profile internal error: {exc}"]
