from __future__ import annotations

import json
from typing import Any

from reliability_core.models import DocumentResult, EvidenceValue
from reliability_core.storage import Store
from reliability_core.tools import FaultInjector


FAULT_MODES = {"none", "transient_before_commit", "timeout_after_commit"}
REQUIRED_EXTRACTION_FIELDS = ("vendor", "invoice_number", "amount_krw", "document_date", "requested_by")


class DemoDocumentAdapter:
    """Converts Solar output into evidence-linked fields for the verified runtime."""

    def adapt(self, documents: list[dict[str, Any]], current_day: int = 50) -> DocumentResult:
        result = DocumentResult()
        if len(documents) != 1:
            result.problems.append({"kind": "STRUCTURE", "field": "document", "evidence": []})
            return result
        document = documents[0]
        doc_id = str(document.get("document_id", "unknown"))
        fields = document.get("fields")
        if not isinstance(fields, dict):
            result.problems.append({"kind": "STRUCTURE", "field": "fields", "evidence": doc_id})
            return result
        for name in REQUIRED_EXTRACTION_FIELDS:
            entry = fields.get(name)
            if not isinstance(entry, dict) or set(entry) != {"value", "location"}:
                result.problems.append({"kind": "MISSING", "field": name, "evidence": []})
                continue
            value = entry["value"]
            if value is None or value == "":
                result.problems.append({"kind": "MISSING", "field": name, "evidence": []})
                continue
            if name == "amount_krw" and type(value) is not int:
                result.problems.append({"kind": "UNIT_MISMATCH", "field": name, "evidence": entry["location"]})
                continue
            result.fields[name] = EvidenceValue(value, doc_id, str(entry["location"]))
        return result


def extraction_to_documents(case_id: str, extraction: dict[str, Any]) -> list[dict[str, Any]]:
    return [
        {
            "document_id": f"{case_id}-expense-document",
            "fields": {
                name: {"value": extraction.get(name), "location": f"solar:{name}"}
                for name in REQUIRED_EXTRACTION_FIELDS
            },
        }
    ]


def fault_injector(mode: str) -> FaultInjector:
    if mode not in FAULT_MODES:
        raise ValueError("unknown fault mode")
    plans = {} if mode == "none" else {"apply_mock_update": mode}
    return FaultInjector(seed=17, plans=plans)


def _detail(value: str) -> dict[str, Any]:
    try:
        parsed = json.loads(value)
        return parsed if isinstance(parsed, dict) else {"value": parsed}
    except (TypeError, json.JSONDecodeError):
        return {"value": str(value)}


def case_snapshot(store: Store, case_id: str, run_id: str, result: dict[str, Any] | None = None) -> dict[str, Any]:
    transitions = store.rows_for_case("transitions", case_id, run_id)
    journal = store.rows_for_case("journal", case_id, run_id)
    approvals = store.rows_for_case("approvals", case_id, run_id)
    business = store.business(case_id, run_id, "synthetic-review-record")
    return {
        "case_id": case_id,
        "run_id": run_id,
        "state": store.state(case_id, run_id),
        "transitions": [
            {"from": row["from_state"], "to": row["to_state"], "reason": row["reason"]}
            for row in transitions
        ],
        "journal": [
            {
                "phase": row["phase"],
                "tool": row["tool"],
                "request_id": row["request_id"],
                "detail": _detail(row["detail"]),
            }
            for row in journal[-80:]
        ],
        "approvals": [
            {"id": row["id"], "status": row["status"], "actor": row["actor"], "used": bool(row["used"])}
            for row in approvals
        ],
        "business_state": business,
        "effect_count": store.effect_count(case_id, run_id),
        "duplicate_effect_count": store.duplicate_effect_count(case_id, run_id),
        "result_reuse_count": sum(row["phase"] == "RESULT_REUSED" for row in journal),
        "retry_event_count": sum(
            row["phase"] in {"FAILED", "DRAFT_RETRY_PENDING"} for row in journal
        ) + sum(row["to_state"] == "RETRY_PENDING" for row in transitions),
        "runtime_result": result,
    }

