"""Models for internal task tracking and user acceptance."""

from __future__ import annotations

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


ValidationStatus = Literal["accepted", "rejected", "insufficient_evidence", "validator_error"]

VALIDATION_STATUSES = {"accepted", "rejected", "insufficient_evidence", "validator_error"}
TASK_OPEN_STATUSES = {"open", "running", "awaiting_acceptance", "needs_revision"}
LEGACY_STATUS_MAP = {
    "validating": "running",
    "awaiting_feedback": "awaiting_acceptance",
    "needs_review": "awaiting_acceptance",
}


@dataclass(slots=True)
class ValidationResult:
    status: ValidationStatus = "rejected"
    score: float = 0.0
    issues: list[str] = field(default_factory=list)
    missing_requirements: list[str] = field(default_factory=list)
    evidence_gaps: list[str] = field(default_factory=list)
    recommended_revision_prompt: str = ""
    validator: str = "heuristic"

    def __init__(
        self,
        *,
        status: ValidationStatus | None = None,
        passed: bool | None = None,
        score: float = 0.0,
        issues: list[str] | None = None,
        missing_requirements: list[str] | None = None,
        evidence_gaps: list[str] | None = None,
        recommended_revision_prompt: str = "",
        validator: str = "heuristic",
    ) -> None:
        if status is not None and status not in VALIDATION_STATUSES:
            raise ValueError(f"unknown validation status: {status}")
        self.status = status or ("accepted" if passed and score >= 0.75 else "rejected")
        self.score = max(0.0, min(1.0, float(score or 0.0)))
        self.issues = list(issues or [])
        self.missing_requirements = list(missing_requirements or [])
        self.evidence_gaps = list(evidence_gaps or [])
        self.recommended_revision_prompt = recommended_revision_prompt
        self.validator = validator

    @property
    def passed(self) -> bool:
        return self.status == "accepted"

    @property
    def accepted(self) -> bool:
        return self.status == "accepted"

    def to_dict(self) -> dict[str, Any]:
        return {
            "status": self.status,
            "passed": self.passed,
            "score": self.score,
            "issues": list(self.issues),
            "missing_requirements": list(self.missing_requirements),
            "evidence_gaps": list(self.evidence_gaps),
            "recommended_revision_prompt": self.recommended_revision_prompt,
            "validator": self.validator,
            "accepted": self.accepted,
        }

    @classmethod
    def from_dict(cls, payload: dict[str, Any] | None) -> "ValidationResult | None":
        if not isinstance(payload, dict):
            return None
        raw_status = payload.get("status")
        if "status" in payload and raw_status not in VALIDATION_STATUSES:
            raise ValueError(f"unknown validation status: {raw_status}")
        status: ValidationStatus | None = raw_status if "status" in payload else None
        return cls(
            status=status,
            passed=bool(payload.get("passed")) if "status" not in payload else None,
            score=float(payload.get("score", 0.0) or 0.0),
            issues=[str(item) for item in payload.get("issues") or []],
            missing_requirements=[str(item) for item in payload.get("missing_requirements") or []],
            evidence_gaps=[str(item) for item in payload.get("evidence_gaps") or []],
            recommended_revision_prompt=str(payload.get("recommended_revision_prompt") or ""),
            validator=str(payload.get("validator") or "unknown"),
        )


@dataclass(slots=True)
class TaskRecord:
    task_id: str
    session_id: str
    description: str
    goal: str
    constraints: list[str]
    priority: int
    status: str
    creator: str
    created_at: str
    updated_at: str
    parent_task_id: str | None = None
    closed_at: str | None = None
    close_reason: str | None = None
    satisfaction: float | None = None
    run_ids: list[str] = field(default_factory=list)
    skill_names: list[str] = field(default_factory=list)
    feedback: list[dict[str, Any]] = field(default_factory=list)
    validation_result: dict[str, Any] | None = None
    metadata: dict[str, Any] = field(default_factory=dict)

    @property
    def is_open(self) -> bool:
        return self.status in TASK_OPEN_STATUSES

    @property
    def is_execution_active(self) -> bool:
        return self.status == "running"

    @property
    def requires_user_action(self) -> bool:
        return self.status in {"awaiting_acceptance", "needs_revision"}

    def to_dict(self) -> dict[str, Any]:
        return {
            "task_id": self.task_id,
            "session_id": self.session_id,
            "parent_task_id": self.parent_task_id,
            "description": self.description,
            "goal": self.goal,
            "constraints": list(self.constraints),
            "priority": self.priority,
            "status": self.status,
            "creator": self.creator,
            "created_at": self.created_at,
            "updated_at": self.updated_at,
            "closed_at": self.closed_at,
            "close_reason": self.close_reason,
            "satisfaction": self.satisfaction,
            "run_ids": list(self.run_ids),
            "skill_names": list(self.skill_names),
            "acceptance": list(self.feedback),
            "feedback": list(self.feedback),
            "validation_result": self.validation_result,
            "metadata": dict(self.metadata),
        }

    @classmethod
    def from_dict(cls, payload: dict[str, Any]) -> "TaskRecord":
        return cls(
            task_id=str(payload["task_id"]),
            session_id=str(payload["session_id"]),
            parent_task_id=_optional_str(payload.get("parent_task_id")),
            description=str(payload.get("description") or ""),
            goal=str(payload.get("goal") or payload.get("description") or ""),
            constraints=[str(item) for item in payload.get("constraints") or []],
            priority=int(payload.get("priority", 0) or 0),
            status=LEGACY_STATUS_MAP.get(str(payload.get("status") or "open"), str(payload.get("status") or "open")),
            creator=str(payload.get("creator") or "main-agent"),
            created_at=str(payload.get("created_at") or ""),
            updated_at=str(payload.get("updated_at") or ""),
            closed_at=_optional_str(payload.get("closed_at")),
            close_reason=_optional_str(payload.get("close_reason")),
            satisfaction=_optional_float(payload.get("satisfaction")),
            run_ids=[str(item) for item in payload.get("run_ids") or []],
            skill_names=[str(item) for item in payload.get("skill_names") or []],
            feedback=[
                _normalize_acceptance_entry(dict(item))
                for item in (payload.get("acceptance") or payload.get("feedback") or [])
                if isinstance(item, dict)
            ],
            validation_result=dict(payload["validation_result"]) if isinstance(payload.get("validation_result"), dict) else None,
            metadata=dict(payload.get("metadata") or {}),
        )


@dataclass(slots=True)
class TaskEvent:
    event_id: str
    task_id: str
    session_id: str
    event_type: str
    created_at: str
    run_id: str | None = None
    payload: dict[str, Any] = field(default_factory=dict)

    def to_dict(self) -> dict[str, Any]:
        return {
            "event_id": self.event_id,
            "task_id": self.task_id,
            "session_id": self.session_id,
            "run_id": self.run_id,
            "event_type": self.event_type,
            "created_at": self.created_at,
            "payload": dict(self.payload),
        }

    @classmethod
    def from_dict(cls, payload: dict[str, Any]) -> "TaskEvent":
        return cls(
            event_id=str(payload["event_id"]),
            task_id=str(payload["task_id"]),
            session_id=str(payload["session_id"]),
            run_id=_optional_str(payload.get("run_id")),
            event_type=str(payload.get("event_type") or ""),
            created_at=str(payload.get("created_at") or ""),
            payload=dict(payload.get("payload") or {}),
        )


@dataclass(slots=True)
class MainAgentDecision:
    mode: str
    reason: str
    starts_new_task: bool = False
    closes_task: bool = False
    abandons_task: bool = False
    short_title: str | None = None
    action: str = ""

    @property
    def is_task(self) -> bool:
        return self.mode == "task"


def _optional_str(value: Any) -> str | None:
    if value in (None, ""):
        return None
    return str(value)


def _optional_float(value: Any) -> float | None:
    if value in (None, ""):
        return None
    return float(value)


def _normalize_acceptance_entry(entry: dict[str, Any]) -> dict[str, Any]:
    if entry.get("acceptance_type") is None and entry.get("feedback_type") is not None:
        feedback_type = str(entry.get("feedback_type") or "")
        entry["acceptance_type"] = "accept" if feedback_type == "satisfied" else feedback_type
    if entry.get("feedback_type") is None and entry.get("acceptance_type") is not None:
        acceptance_type = str(entry.get("acceptance_type") or "")
        entry["feedback_type"] = "satisfied" if acceptance_type == "accept" else acceptance_type
    return entry
