"""Tickets & Sprints — data models and enums."""

from __future__ import annotations

import time
import uuid
from datetime import datetime, timezone
from enum import Enum
from typing import Any

from pydantic import BaseModel, Field


# ── Enums ─────────────────────────────────────────────────────

class TicketStatus(str, Enum):
    BACKLOG = "backlog"
    TODO = "todo"
    IN_PROGRESS = "in_progress"
    IN_REVIEW = "in_review"
    DONE = "done"
    CANCELLED = "cancelled"


class TicketPriority(str, Enum):
    CRITICAL = "critical"
    HIGH = "high"
    MEDIUM = "medium"
    LOW = "low"


class TicketType(str, Enum):
    TASK = "task"
    BUG = "bug"
    FEATURE = "feature"
    REFACTOR = "refactor"
    TEST = "test"
    DOCS = "docs"


class SprintStatus(str, Enum):
    PLANNING = "planning"
    ACTIVE = "active"
    COMPLETED = "completed"
    CANCELLED = "cancelled"


class MilestoneStatus(str, Enum):
    PLANNING = "planning"
    ACTIVE = "active"
    COMPLETED = "completed"
    CANCELLED = "cancelled"


# ── Models ────────────────────────────────────────────────────

class ToolAssignment(BaseModel):
    """Which tool is assigned to work on a ticket."""
    tool: str  # vscode, aider, claude-code, windsurf, cursor, etc.
    agent_mode: str = ""  # code, architect, debug, ask
    model: str = ""  # model alias or full name
    instance: str = ""  # tool instance name (host, docker, etc.)
    started_at: float = 0
    completed_at: float = 0


class JobExecution(BaseModel):
    """Snapshot of the latest job execution for a ticket."""
    job_id: str = ""
    tool: str = ""
    mode: str = ""
    model: str = ""
    project_dir: str = ""
    environment_id: str = ""
    environment_name: str = ""
    environment_kind: str = ""
    environment_target: str = ""
    environment_tool_instance_name: str = ""
    environment_tool_instance: dict[str, Any] = Field(default_factory=dict)
    prompt_excerpt: str = ""
    prompt_length: int = 0
    status: str = "queued"  # queued, running, done, failed, cancelled
    success: bool | None = None
    exit_code: int | None = None
    started_at: float = 0
    finished_at: float = 0
    duration_s: float = 0
    error: str = ""
    files_changed: list[str] = Field(default_factory=list)
    stdout_tail: str = ""  # last ~500 chars of output
    stderr_tail: str = ""  # last ~500 chars of error output
    stdout_lines: int = 0
    stderr_lines: int = 0
    execution_metadata: dict[str, Any] = Field(default_factory=dict)


class TicketComment(BaseModel):
    id: str = Field(default_factory=lambda: str(uuid.uuid4())[:8])
    author: str = ""  # tool name or user
    content: str = ""
    created_at: float = Field(default_factory=time.time)


class Milestone(BaseModel):
    id: str = Field(default_factory=lambda: f"MLS-{uuid.uuid4().hex[:6].upper()}")
    name: str
    goal: str = ""
    status: MilestoneStatus = MilestoneStatus.PLANNING
    start_date: str = ""  # ISO date
    end_date: str = ""
    created_at: float = Field(default_factory=time.time)
    updated_at: float = Field(default_factory=time.time)

    def summary(
        self,
        sprints: list["Sprint"] | None = None,
        tickets: list["Ticket"] | None = None,
    ) -> dict[str, Any]:
        milestone_sprints = sprints or []
        milestone_tickets = tickets or []
        total_sprints = len(milestone_sprints)
        completed_sprints = sum(1 for sprint in milestone_sprints if sprint.status == SprintStatus.COMPLETED)
        active_sprints = sum(1 for sprint in milestone_sprints if sprint.status == SprintStatus.ACTIVE)
        total_tickets = len(milestone_tickets)
        tickets_done = sum(1 for ticket in milestone_tickets if ticket.status == TicketStatus.DONE)
        tickets_in_progress = sum(1 for ticket in milestone_tickets if ticket.status == TicketStatus.IN_PROGRESS)
        return {
            "id": self.id,
            "name": self.name,
            "goal": self.goal,
            "status": self.status.value,
            "start_date": self.start_date,
            "end_date": self.end_date,
            "sprints_total": total_sprints,
            "sprints_completed": completed_sprints,
            "sprints_in_progress": active_sprints,
            "tickets_total": total_tickets,
            "tickets_done": tickets_done,
            "tickets_in_progress": tickets_in_progress,
            "progress_pct": round(completed_sprints / total_sprints * 100, 1) if total_sprints else 0,
            "created_at": self.created_at,
            "updated_at": self.updated_at,
        }


class Ticket(BaseModel):
    id: str = Field(default_factory=lambda: f"TKT-{uuid.uuid4().hex[:6].upper()}")
    task: str = ""                                 # "co zrobić" — główne pole
    title: str = ""                                # auto-generated z task[:60] jeśli puste
    project_id: str | None = None                  # powiązanie z projektem
    description: str = ""
    status: TicketStatus = TicketStatus.TODO
    priority: TicketPriority = TicketPriority.MEDIUM
    type: TicketType = TicketType.TASK
    sprint_id: str = ""
    milestone_id: str = ""
    assigned_tool: ToolAssignment | None = None
    last_execution: JobExecution | None = None
    tags: list[str] = Field(default_factory=list)
    files: list[str] = Field(default_factory=list)  # affected file paths
    context_files: list[str] = Field(default_factory=list)  # files AI should read for context
    depends_on: list[str] = Field(default_factory=list)  # ticket IDs this depends on
    comments: list[TicketComment] = Field(default_factory=list)
    estimated_hours: float = 0
    actual_hours: float = 0
    created_at: float = Field(default_factory=time.time)
    updated_at: float = Field(default_factory=time.time)
    completed_at: float = 0

    def __init__(self, **data: Any) -> None:
        # Backward compat: if title provided but no task, use title as task
        if data.get("title") and not data.get("task"):
            data["task"] = data["title"]
        if "last_job" in data and "last_execution" not in data:
            data["last_execution"] = data["last_job"]
        # Auto-assign project by date (DDMMYYYY) when not provided
        if not data.get("project_id"):
            created = data.get("created_at")
            if created and isinstance(created, (int, float)) and created > 0:
                dt = datetime.fromtimestamp(created, tz=timezone.utc)
            else:
                dt = datetime.now(tz=timezone.utc)
            data["project_id"] = dt.strftime("%d%m%Y")
        super().__init__(**data)
        # Auto-generate title from task if not provided
        if not self.title and self.task:
            truncated = self.task[:60].strip()
            if len(self.task) > 60:
                truncated += "\u2026"
            self.title = truncated

    def summary(self) -> dict[str, Any]:
        return {
            "id": self.id,
            "task": self.task,
            "title": self.title,
            "project_id": self.project_id,
            "description": self.description,
            "status": self.status.value,
            "priority": self.priority.value,
            "type": self.type.value,
            "sprint_id": self.sprint_id,
            "milestone_id": self.milestone_id,
            "assigned_tool": self.assigned_tool.model_dump() if self.assigned_tool else None,
            "last_job": self.last_execution.model_dump() if self.last_execution else None,
            "tags": self.tags,
            "files": self.files,
            "context_files": self.context_files,
            "depends_on": self.depends_on,
            "comments_count": len(self.comments),
            "estimated_hours": self.estimated_hours,
            "actual_hours": self.actual_hours,
            "created_at": self.created_at,
            "updated_at": self.updated_at,
            "completed_at": self.completed_at,
        }


class Sprint(BaseModel):
    id: str = Field(default_factory=lambda: f"SPR-{uuid.uuid4().hex[:6].upper()}")
    name: str
    goal: str = ""
    milestone_id: str = ""
    status: SprintStatus = SprintStatus.PLANNING
    start_date: str = ""  # ISO date
    end_date: str = ""
    created_at: float = Field(default_factory=time.time)
    updated_at: float = Field(default_factory=time.time)

    def summary(self, tickets: list[Ticket] | None = None) -> dict[str, Any]:
        sprint_tickets = tickets or []
        total = len(sprint_tickets)
        done = sum(1 for t in sprint_tickets if t.status == TicketStatus.DONE)
        in_progress = sum(1 for t in sprint_tickets if t.status == TicketStatus.IN_PROGRESS)
        return {
            "id": self.id,
            "name": self.name,
            "goal": self.goal,
            "milestone_id": self.milestone_id,
            "status": self.status.value,
            "start_date": self.start_date,
            "end_date": self.end_date,
            "tickets_total": total,
            "tickets_done": done,
            "tickets_in_progress": in_progress,
            "progress_pct": round(done / total * 100, 1) if total else 0,
            "created_at": self.created_at,
            "updated_at": self.updated_at,
        }
