"""Shared request/response contract helpers for Codex GraphRAG actions."""

from __future__ import annotations

import json
from typing import Any, Dict, Optional, Tuple

VALID_ACTIONS = (
    "build",
    "search",
    "workflow",
    "review",
    "ingest_discrepancy",
    "closure_loop",
    "closure_metrics",
    "propose_fix",
    "trust_metrics",
    "trust_explain",
    "retention_status",
)


def error_payload(
    *,
    code: str,
    message: str,
    action: str = "",
    details: Optional[Dict[str, Any]] = None,
    retriable: bool = False,
    valid_actions: Optional[list[str]] = None,
) -> Dict[str, Any]:
    payload: Dict[str, Any] = {
        "ok": False,
        "error": message,
        "error_code": code,
        "error_info": {
            "code": code,
            "message": message,
            "action": action or "",
            "details": details or {},
            "retriable": bool(retriable),
        },
    }
    if valid_actions:
        payload["valid_actions"] = valid_actions
    return payload


def parse_event_json(event_json: str, *, action: str) -> Tuple[Optional[Dict[str, Any]], Optional[Dict[str, Any]]]:
    if not event_json:
        return None, error_payload(
            code="missing_event_json",
            message=f"event_json is required for action='{action}'",
            action=action,
            details={"required_field": "event_json"},
        )
    try:
        event = json.loads(event_json)
    except Exception:
        return None, error_payload(
            code="invalid_json",
            message="event_json must be valid JSON",
            action=action,
            details={"field": "event_json"},
        )
    if not isinstance(event, dict):
        return None, error_payload(
            code="invalid_event_type",
            message="event_json must decode to a JSON object",
            action=action,
            details={"field": "event_json", "expected": "object"},
        )
    return event, None


def validate_discrepancy_event(
    event: Dict[str, Any],
    *,
    action: str,
    strict_contract: bool = False,
) -> Optional[Dict[str, Any]]:
    if not isinstance(event, dict):
        return error_payload(
            code="invalid_event_type",
            message="event must be a JSON object",
            action=action,
            details={"field": "event", "expected": "object"},
        )
    if not event:
        return error_payload(
            code="empty_event",
            message="event must not be empty",
            action=action,
            details={"field": "event"},
        )

    metric = str(event.get("metric") or event.get("metric_name") or "").strip()
    symptom = str(event.get("symptom") or event.get("mismatch") or "").strip()
    source_system = str(event.get("source_system") or event.get("source") or "").strip()
    if not metric or not symptom:
        return error_payload(
            code="invalid_event_shape",
            message="event must include metric/metric_name and symptom/mismatch",
            action=action,
            details={"missing": [k for k, ok in {"metric": bool(metric), "symptom": bool(symptom)}.items() if not ok]},
        )
    if strict_contract and not source_system:
        return error_payload(
            code="invalid_event_shape",
            message="event must include source_system/source in strict mode",
            action=action,
            details={"missing": ["source_system"]},
        )
    return None
