"""Domain models for tasks and projects."""

from __future__ import annotations

import json
from datetime import datetime
from typing import TypedDict
from enum import StrEnum

from pydantic import BaseModel, Field


class TaskStatus(StrEnum):
    PENDING = "pending"
    CLAIMED = "claimed"
    DONE = "done"
    FAILED = "failed"
    BLOCKED = "blocked"
    EPIC = "epic"
    IDEA = "idea"


class Priority(StrEnum):
    P0 = "p0"
    P1 = "p1"
    P2 = "p2"


PRIORITY_SCORES: dict[str, int] = {
    Priority.P0: 300,
    Priority.P1: 200,
    Priority.P2: 100,
}

# ---------------------------------------------------------------------------
# Task type constants (used by merge-agent and future typed tasks)
# ---------------------------------------------------------------------------

TASK_TYPE_MERGE = "merge"


class MergeTaskPayload(TypedDict):
    """Typed payload for merge tasks stored in Task.context."""

    branch_name: str
    parent_task_id: str
    agent_id: str


def validate_merge_payload(payload: MergeTaskPayload) -> None:
    """Validate a merge task payload.

    Raises ValueError if branch_name does not start with 'worktree-'
    or agent_id is empty.
    """
    branch = payload.get('branch_name', '')
    if not branch.startswith('worktree-'):
        raise ValueError(
            "branch_name must start with 'worktree-', got: " + repr(branch)
        )
    agent = payload.get('agent_id', '')
    if not agent:
        raise ValueError('agent_id must not be empty')


class Task(BaseModel):
    id: str
    project_id: str
    title: str
    status: TaskStatus = TaskStatus.PENDING
    priority: Priority = Priority.P1
    assignee: str | None = None
    parent_id: str | None = None
    context: dict = Field(default_factory=dict)
    output: dict = Field(default_factory=dict)
    done_when: str | None = None
    verify_command: str | None = None
    depends_on: list[str] = Field(default_factory=list)
    created_at: datetime | None = None
    updated_at: datetime | None = None
    claimed_at: datetime | None = None
    done_at: datetime | None = None
    # Phase 3: Claim TTL
    claim_expires_at: datetime | None = None
    # Phase 3: Retry tracking
    retry_count: int = 0
    max_retries: int = 3
    last_failed_at: datetime | None = None
    retry_after: datetime | None = None
    # Phase 3: Dead letter
    dead_letter: bool = False
    dead_letter_reason: str | None = None

    @classmethod
    def from_record(cls, record: dict, depends_on: list[str] | None = None) -> Task:
        """Create a Task from an asyncpg Record (dict-like)."""
        data = dict(record)
        # asyncpg returns UUID objects — stringify project_id
        data["project_id"] = str(data["project_id"])
        # Parse JSONB strings if needed
        for field in ("context", "output"):
            val = data.get(field)
            if isinstance(val, str):
                data[field] = json.loads(val)
        data["depends_on"] = depends_on or []
        return cls(**data)


class ProjectStatus(BaseModel):
    project_id: str
    project_name: str
    total: int = 0
    pending: int = 0
    claimed: int = 0
    done: int = 0
    failed: int = 0
    blocked: int = 0
    epic: int = 0
    idea: int = 0
    ready_count: int = 0
    blocked_tasks: list[str] = Field(default_factory=list)
    open_escalations: int = 0
