#!/usr/bin/env python3
"""DoX quality assessment helpers (STATE-WP-0077).

Canonical recording fields and parsing for Definition assessments.
See docs/work-record-quality-gates.md.
"""
from __future__ import annotations

import re
from dataclasses import dataclass
from typing import Any

POLICIES = ("DoC", "DoR", "DoD")

FIELD_FOR_POLICY = {
    "DoC": "quality_doc",
    "DoR": "quality_dor",
    "DoD": "quality_dod",
}


def _norm_policy(policy: str) -> str:
    p = policy.strip()
    upper = p.upper().replace(" ", "").replace("_", "")
    if upper in ("DOC", "COMPREHENSION", "DEFINITIONOFCOMPREHENSION"):
        return "DoC"
    if upper in ("DOR", "READY", "DEFINITIONOFREADY"):
        return "DoR"
    if upper in ("DOD", "DONE", "DEFINITIONOFDONE"):
        return "DoD"
    if p in POLICIES:
        return p
    raise ValueError(f"unknown policy {policy!r}; use DoC, DoR, or DoD")


def badge(policy: str, outcome: str) -> str:
    """Map policy + Ok|Failed (or full badge) to DoX-Ok / DoX-Failed."""
    pol = _norm_policy(policy)
    out = outcome.strip().strip('"')
    if out in (f"{pol}-Ok", f"{pol}-Failed"):
        return out
    if out.lower() in ("ok", "pass", "passed"):
        return f"{pol}-Ok"
    if out.lower() in ("failed", "fail", "no"):
        return f"{pol}-Failed"
    raise ValueError(f"unknown outcome {outcome!r} for {pol}")


def assessment_from_mapping(meta: dict, policy: str) -> str | None:
    """Read assessment badge for policy from frontmatter / YAML / task fields."""
    pol = _norm_policy(policy)
    key = FIELD_FOR_POLICY[pol]
    raw = meta.get(key)
    if raw is None:
        return None
    s = str(raw).strip().strip('"').strip("'")
    if not s or s.lower() in ("unassessed", "none", "null", "~", "-"):
        return None
    try:
        return badge(pol, s)
    except ValueError:
        return None


def is_ok(badge_value: str | None) -> bool:
    return bool(badge_value) and str(badge_value).endswith("-Ok")


@dataclass(frozen=True)
class QualityDebtItem:
    kind: str
    record_id: str
    lifecycle_status: str
    missing: str
    path: str
    detail: str = ""


def debt_for_workplan_meta(meta: dict, *, path: str) -> list[QualityDebtItem]:
    """Quality debt for a workplan frontmatter dict."""
    items: list[QualityDebtItem] = []
    wp_id = str(meta.get("id", "")).strip() or path
    status = str(meta.get("status", "")).strip().lower()
    dor = assessment_from_mapping(meta, "DoR")
    dod = assessment_from_mapping(meta, "DoD")
    if status == "ready" and not is_ok(dor):
        items.append(
            QualityDebtItem(
                kind="workplan",
                record_id=wp_id,
                lifecycle_status=status,
                missing="DoR-Ok",
                path=path,
                detail=f"quality_dor={dor or 'unassessed'}",
            )
        )
    if status in ("finished", "completed") and not is_ok(dod):
        items.append(
            QualityDebtItem(
                kind="workplan",
                record_id=wp_id,
                lifecycle_status=status,
                missing="DoD-Ok",
                path=path,
                detail=f"quality_dod={dod or 'unassessed'}",
            )
        )
    return items


def debt_for_intake_meta(meta: dict, *, path: str) -> list[QualityDebtItem]:
    """Quality debt for intake blocks in vetted/routed without DoC-Ok."""
    items: list[QualityDebtItem] = []
    iid = str(meta.get("id", "")).strip() or path
    status = str(meta.get("status", "")).strip().lower()
    doc = assessment_from_mapping(meta, "DoC")
    if status in ("vetted", "routed") and not is_ok(doc):
        items.append(
            QualityDebtItem(
                kind="intake",
                record_id=iid,
                lifecycle_status=status,
                missing="DoC-Ok",
                path=path,
                detail=f"quality_doc={doc or 'unassessed'}",
            )
        )
    return items


def intake_has_doc_ok(intake: dict) -> bool:
    """Soft check: hub intake notes/description record DoC-Ok."""
    chunks: list[str] = []
    for key in ("description", "routed_note", "title"):
        v = intake.get(key)
        if v:
            chunks.append(str(v))
    for note in intake.get("notes") or []:
        if isinstance(note, dict):
            chunks.append(str(note.get("content") or ""))
        else:
            chunks.append(str(note))
    # structured future field
    if assessment_from_mapping(intake, "DoC") and is_ok(assessment_from_mapping(intake, "DoC")):
        return True
    text = "\n".join(chunks)
    if re.search(r"\bDoC-Ok\b", text):
        return True
    if re.search(r"quality_doc\s*[:=]\s*[\"']?(DoC-Ok|Ok)\b", text, re.I):
        return True
    return False


def progress_event_body(
    *,
    policy: str,
    outcome: str,
    record_kind: str,
    record_id: str,
    assessed_by: str | None = None,
    note: str | None = None,
    topic_id: str | None = None,
    workplan_id: str | None = None,
    author: str | None = None,
) -> dict[str, Any]:
    """JSON body for POST /progress/ recording a quality assessment."""
    b = badge(policy, outcome)
    pol = b.rsplit("-", 1)[0]
    body: dict[str, Any] = {
        "event_type": "quality_assessment",
        "summary": f"{b} for {record_kind} {record_id}",
        "author": author or assessed_by or "agent",
        "detail": {
            "policy": pol,
            "outcome": "Ok" if b.endswith("-Ok") else "Failed",
            "badge": b,
            "record_kind": record_kind,
            "record_id": record_id,
            "assessed_by": assessed_by,
            "note": note,
        },
    }
    if topic_id:
        body["topic_id"] = topic_id
    if workplan_id:
        body["workplan_id"] = workplan_id
    return body
