"""PLAN.md data model and I/O."""

from __future__ import annotations

import re
from dataclasses import dataclass, field
from datetime import datetime, timezone
from pathlib import Path


@dataclass
class OrchestrateTask:
    id: str
    title: str
    persona: str
    agent: str
    fallback: str | None
    worktree: str
    prompt_file: str
    depends_on: list[str]
    status: str  # pending | waiting | running | done | failed | blocked


@dataclass
class OrchestratePlan:
    run_id: str
    created: str
    task_description: str
    tasks: list[OrchestrateTask] = field(default_factory=list)


def _parse_field(line: str, name: str) -> str | None:
    """Extract value from a '- **name**: value' line."""
    pattern = rf"^\s*-\s+\*\*{re.escape(name)}\*\*:\s*(.+)$"
    m = re.match(pattern, line)
    return m.group(1).strip() if m else None


def parse_plan(content: str) -> OrchestratePlan:
    """Parse PLAN.md content into an OrchestratePlan."""
    lines = content.splitlines()

    run_id = ""
    created = ""
    task_description_lines: list[str] = []
    tasks: list[OrchestrateTask] = []

    section = ""
    i = 0
    while i < len(lines):
        line = lines[i]

        if line.strip() == "## Meta":
            section = "meta"
        elif line.strip() == "## Task":
            section = "task"
        elif line.strip() == "## Sub-tasks":
            section = "subtasks"
        elif line.startswith("### [") and section == "subtasks":
            # Parse task header: ### [TASK-1] Title
            m = re.match(r"^### \[([A-Z0-9_-]+)\]\s+(.+)$", line)
            if m:
                task_id = m.group(1)
                title = m.group(2).strip()
                persona = ""
                agent = ""
                fallback: str | None = None
                worktree = ""
                prompt_file = ""
                depends_on: list[str] = []
                status = "pending"

                i += 1
                while i < len(lines) and not lines[i].startswith("###"):
                    l = lines[i]
                    if v := _parse_field(l, "persona"):
                        persona = v
                    elif v := _parse_field(l, "agent"):
                        # Format: "codex (fallback: claude)" or "codex"
                        fm = re.match(r"^(\S+)\s+\(fallback:\s*(\S+)\)$", v)
                        if fm:
                            agent = fm.group(1)
                            fallback = fm.group(2)
                        else:
                            agent = v
                    elif v := _parse_field(l, "worktree"):
                        worktree = v
                    elif v := _parse_field(l, "prompt-file"):
                        prompt_file = v
                    elif v := _parse_field(l, "depends-on"):
                        if v.lower() != "none":
                            depends_on = [d.strip() for d in v.split(",")]
                    elif v := _parse_field(l, "status"):
                        status = v
                    i += 1

                tasks.append(OrchestrateTask(
                    id=task_id,
                    title=title,
                    persona=persona,
                    agent=agent,
                    fallback=fallback,
                    worktree=worktree,
                    prompt_file=prompt_file,
                    depends_on=depends_on,
                    status=status,
                ))
                continue
        elif section == "meta":
            if v := _parse_field(line, "run-id"):
                run_id = v
            elif v := _parse_field(line, "created"):
                created = v
        elif section == "task" and line.strip() and not line.startswith("#"):
            task_description_lines.append(line.strip())

        i += 1

    return OrchestratePlan(
        run_id=run_id,
        created=created,
        task_description=" ".join(task_description_lines),
        tasks=tasks,
    )


def write_plan(plan: OrchestratePlan, path: Path) -> None:
    """Write an OrchestratePlan to a PLAN.md file."""
    lines = [
        "# VibePod Orchestration Plan",
        "",
        "## Meta",
        f"- **run-id**: {plan.run_id}",
        f"- **created**: {plan.created}",
        "",
        "## Task",
        plan.task_description,
        "",
        "## Sub-tasks",
        "",
    ]

    for task in plan.tasks:
        agent_str = task.agent
        if task.fallback:
            agent_str = f"{task.agent} (fallback: {task.fallback})"
        depends_str = ", ".join(task.depends_on) if task.depends_on else "none"

        lines += [
            f"### [{task.id}] {task.title}",
            f"- **persona**: {task.persona}",
            f"- **agent**: {agent_str}",
            f"- **worktree**: {task.worktree}",
            f"- **prompt-file**: {task.prompt_file}",
            f"- **depends-on**: {depends_str}",
            f"- **status**: {task.status}",
            "",
        ]

    path.write_text("\n".join(lines), encoding="utf-8")


def update_task_status(plan_path: Path, task_id: str, status: str) -> None:
    """Update a single task's status in PLAN.md in-place."""
    plan = parse_plan(plan_path.read_text(encoding="utf-8"))

    found = False
    for task in plan.tasks:
        if task.id == task_id:
            task.status = status
            found = True
            break

    if not found:
        raise ValueError(f"Task {task_id} not found in plan")

    write_plan(plan, plan_path)


def new_plan(task_description: str, run_id: str | None = None) -> OrchestratePlan:
    """Create a new empty OrchestratePlan with generated run-id and timestamp."""
    import uuid
    rid = run_id or uuid.uuid4().hex[:8]
    created = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
    return OrchestratePlan(run_id=rid, created=created, task_description=task_description)
