"""Core data models for learning and feedback capture."""
from __future__ import annotations

from dataclasses import dataclass, field
from datetime import UTC, datetime
from typing import Any, Literal


FeedbackKind = Literal["thumbs_up", "thumbs_down", "numeric_rating", "edited_answer", "suggestion"]
SuggestionOutcome = Literal["accepted", "rejected"]
OutcomeKind = Literal["conversion", "resolution", "ctr", "reply_rate", "escalation", "human_takeover"]
FailureType = Literal[
    "hallucination",
    "style_miss",
    "policy_violation",
    "wrong_tool",
    "bad_retrieval",
    "latency",
    "too_expensive",
]

FAILURE_TYPES: tuple[FailureType, ...] = (
    "hallucination",
    "style_miss",
    "policy_violation",
    "wrong_tool",
    "bad_retrieval",
    "latency",
    "too_expensive",
)
FEEDBACK_KINDS: tuple[FeedbackKind, ...] = (
    "thumbs_up",
    "thumbs_down",
    "numeric_rating",
    "edited_answer",
    "suggestion",
)
OUTCOME_KINDS: tuple[OutcomeKind, ...] = (
    "conversion",
    "resolution",
    "ctr",
    "reply_rate",
    "escalation",
    "human_takeover",
)
SUGGESTION_OUTCOMES: tuple[SuggestionOutcome, ...] = ("accepted", "rejected")


def utcnow() -> str:
    """Return the current UTC timestamp in ISO 8601 format."""
    return datetime.now(UTC).isoformat()


def validate_failure_types(values: list[str] | tuple[str, ...] | None) -> list[FailureType]:
    """Validate and normalize failure taxonomy labels."""
    if not values:
        return []
    normalized: list[FailureType] = []
    allowed = set(FAILURE_TYPES)
    for value in values:
        if value not in allowed:
            raise ValueError(f"Unsupported failure type: {value}")
        normalized.append(value)
    return normalized


@dataclass
class RunRecord:
    """Captured production run used to build learning datasets."""

    id: str
    trace_id: str
    request_id: str | None
    agent_name: str
    input_text: str
    output_text: str
    created_at: str = field(default_factory=utcnow)
    session_id: str | None = None
    workspace_id: str | None = None
    prompt_version: str = "unversioned"
    model: str | None = None
    config_fingerprint: str | None = None
    usage: dict[str, Any] | None = None
    tool_calls: list[dict[str, Any]] = field(default_factory=list)
    metadata: dict[str, Any] = field(default_factory=dict)


@dataclass
class FeedbackRecord:
    """User or operator feedback attached to a specific run."""

    id: str
    kind: FeedbackKind
    trace_id: str
    created_at: str = field(default_factory=utcnow)
    run_id: str | None = None
    request_id: str | None = None
    session_id: str | None = None
    workspace_id: str | None = None
    agent_name: str | None = None
    prompt_version: str = "unversioned"
    model: str | None = None
    config_fingerprint: str | None = None
    rating: float | None = None
    suggestion_outcome: SuggestionOutcome | None = None
    corrected_output: str | None = None
    correction_notes: str | None = None
    expected_output: str | None = None
    failure_types: list[FailureType] = field(default_factory=list)
    metadata: dict[str, Any] = field(default_factory=dict)


@dataclass
class OutcomeRecord:
    """Business or operational outcome attached to a run."""

    id: str
    kind: OutcomeKind
    trace_id: str
    created_at: str = field(default_factory=utcnow)
    run_id: str | None = None
    request_id: str | None = None
    session_id: str | None = None
    workspace_id: str | None = None
    agent_name: str | None = None
    prompt_version: str = "unversioned"
    model: str | None = None
    config_fingerprint: str | None = None
    value: float | int | bool | str | None = None
    metadata: dict[str, Any] = field(default_factory=dict)


@dataclass
class LearningExample:
    """Joined run + feedback + outcome example for evaluation or optimization."""

    run_id: str
    trace_id: str
    agent_name: str
    input_text: str
    output_text: str
    created_at: str
    session_id: str | None = None
    workspace_id: str | None = None
    prompt_version: str = "unversioned"
    model: str | None = None
    config_fingerprint: str | None = None
    feedback: list[FeedbackRecord] = field(default_factory=list)
    outcomes: list[OutcomeRecord] = field(default_factory=list)
    failure_types: list[FailureType] = field(default_factory=list)
    corrected_output: str | None = None
    metadata: dict[str, Any] = field(default_factory=dict)
