"""
Phase tracker for progression tracking.

Tracks phase progression through workflow states
for spec-driven execution (EXEC-10, EXEC-11, EXEC-12).

Usage:
    from gsd_rlm.workflow.phase_tracker import PhaseTracker, PhaseStatus

    tracker = PhaseTracker(Path(".planning"))
    tracker.load()
    tracker.start_phase("05")
    tracker.complete_plan("05", "05-01")
    tracker.save()
"""

from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import StrEnum
from pathlib import Path
from typing import Optional
import re


class PhaseStatus(StrEnum):
    """Status of a phase in the workflow."""

    NOT_STARTED = "not_started"
    IN_PROGRESS = "in_progress"
    COMPLETE = "complete"
    BLOCKED = "blocked"


def _utcnow() -> datetime:
    """Get current UTC time (timezone-aware)."""
    return datetime.now(timezone.utc)


def _parse_iso_datetime(s: str) -> Optional[datetime]:
    """Parse ISO datetime string to datetime object."""
    if not s:
        return None
    try:
        # Handle various ISO formats
        if s.endswith("Z"):
            s = s[:-1] + "+00:00"
        return datetime.fromisoformat(s)
    except (ValueError, TypeError):
        return None


@dataclass
class PhaseProgress:
    """Progress tracking for a single phase."""

    phase_number: str
    status: PhaseStatus = PhaseStatus.NOT_STARTED
    plans_total: int = 0
    plans_complete: int = 0
    current_plan: Optional[str] = None
    blocker: Optional[str] = None
    started_at: Optional[datetime] = None
    completed_at: Optional[datetime] = None

    @property
    def progress_percent(self) -> float:
        """Calculate progress percentage."""
        if self.plans_total == 0:
            return 0.0
        return (self.plans_complete / self.plans_total) * 100

    @property
    def is_complete(self) -> bool:
        """Check if phase is complete."""
        return self.status == PhaseStatus.COMPLETE

    @property
    def is_blocked(self) -> bool:
        """Check if phase is blocked."""
        return self.status == PhaseStatus.BLOCKED


class PhaseTracker:
    """
    Track phase progression through workflow states.

    Reads STATE.md for current position and scans phase directories
    for plan status.

    Attributes:
        planning_dir: Path to .planning directory
        progress: Dictionary of phase_number -> PhaseProgress

    Example:
        >>> tracker = PhaseTracker(Path(".planning"))
        >>> tracker.load()
        >>> tracker.start_phase("05")
        >>> tracker.complete_plan("05", "05-01")
        >>> progress = tracker.get_progress("05")
        >>> print(progress.plans_complete)
        1
    """

    def __init__(self, planning_dir: Path):
        """
        Initialize PhaseTracker.

        Args:
            planning_dir: Path to .planning directory
        """
        self.planning_dir = Path(planning_dir)
        self._progress: dict[str, PhaseProgress] = {}

    @property
    def progress(self) -> dict[str, PhaseProgress]:
        """Get progress dictionary."""
        return self._progress

    def load(self) -> None:
        """
        Load phase progress from STATE.md and scan phase directories.

        Reads STATE.md for current position and initializes progress
        for all phases found in phase directories.
        """
        self._progress.clear()

        # Scan phase directories
        self._scan_phase_directories()

        # Read STATE.md for current position
        self._read_state_md()

    def _scan_phase_directories(self) -> None:
        """Scan phases directory for all phases."""
        phases_dir = self.planning_dir / "phases"
        if not phases_dir.exists():
            return

        for phase_path in phases_dir.iterdir():
            if not phase_path.is_dir():
                continue

            # Extract phase number from directory name (e.g., "05-opencode-...")
            match = re.match(r"^(\d+)-", phase_path.name)
            if match:
                phase_num = match.group(1).zfill(2)

                # Count plans in directory
                plans = list(phase_path.glob("*-PLAN.md"))
                summaries = list(phase_path.glob("*-SUMMARY.md"))

                progress = PhaseProgress(
                    phase_number=phase_num,
                    plans_total=len(plans),
                    plans_complete=len(summaries),
                )
                self._progress[phase_num] = progress

    def _read_state_md(self) -> None:
        """Read STATE.md for current position and status."""
        state_path = self.planning_dir / "STATE.md"
        if not state_path.exists():
            return

        content = state_path.read_text(encoding="utf-8")
        lines = content.split("\n")

        current_phase = None
        current_plan = None

        for line in lines:
            # Parse "Phase: X of Y" line
            match = re.match(r"Phase:\s*(\d+)(?:\s+of\s+\d+)?", line)
            if match:
                current_phase = match.group(1).zfill(2)

            # Parse "Plan: X of Y" or "Plan: X of Y in current phase"
            match = re.search(r"Plan:\s*(\d+)(?:\s+of\s+\d+)?", line)
            if match:
                current_plan = int(match.group(1))

            # Parse status line
            if "Status:" in line:
                status_str = line.split("Status:")[1].strip().lower()
                if current_phase and current_phase in self._progress:
                    if "in progress" in status_str:
                        self._progress[current_phase].status = PhaseStatus.IN_PROGRESS
                    elif "complete" in status_str:
                        self._progress[current_phase].status = PhaseStatus.COMPLETE
                    elif "blocked" in status_str:
                        self._progress[current_phase].status = PhaseStatus.BLOCKED

        # Update current plan info
        if current_phase and current_phase in self._progress:
            progress = self._progress[current_phase]
            if current_plan:
                # Construct plan ID (e.g., "05-01")
                progress.current_plan = f"{current_phase}-{current_plan:02d}"

    def start_phase(self, phase_number: str) -> None:
        """
        Mark phase as IN_PROGRESS.

        Args:
            phase_number: Phase number (e.g., "05" or "5")
        """
        normalized = phase_number.zfill(2)
        if normalized not in self._progress:
            self._progress[normalized] = PhaseProgress(phase_number=normalized)

        self._progress[normalized].status = PhaseStatus.IN_PROGRESS
        self._progress[normalized].started_at = _utcnow()

    def complete_plan(self, phase_number: str, plan_id: str) -> None:
        """
        Mark a plan as complete within a phase.

        Args:
            phase_number: Phase number (e.g., "05")
            plan_id: Plan identifier (e.g., "05-01")
        """
        normalized = phase_number.zfill(2)
        if normalized not in self._progress:
            return

        progress = self._progress[normalized]
        progress.plans_complete += 1
        progress.current_plan = plan_id

        # Auto-complete phase if all plans done
        if progress.plans_complete >= progress.plans_total:
            progress.status = PhaseStatus.COMPLETE
            progress.completed_at = _utcnow()

    def complete_phase(self, phase_number: str) -> None:
        """
        Mark phase as COMPLETE.

        Args:
            phase_number: Phase number (e.g., "05")
        """
        normalized = phase_number.zfill(2)
        if normalized not in self._progress:
            return

        self._progress[normalized].status = PhaseStatus.COMPLETE
        self._progress[normalized].completed_at = _utcnow()

    def block_phase(self, phase_number: str, reason: str) -> None:
        """
        Mark phase as BLOCKED.

        Args:
            phase_number: Phase number (e.g., "05")
            reason: Reason for blocking
        """
        normalized = phase_number.zfill(2)
        if normalized not in self._progress:
            self._progress[normalized] = PhaseProgress(phase_number=normalized)

        self._progress[normalized].status = PhaseStatus.BLOCKED
        self._progress[normalized].blocker = reason

    def get_progress(self, phase_number: str) -> Optional[PhaseProgress]:
        """
        Get progress for a specific phase.

        Args:
            phase_number: Phase number (e.g., "05")

        Returns:
            PhaseProgress if found, None otherwise
        """
        normalized = phase_number.zfill(2)
        return self._progress.get(normalized)

    def save(self) -> None:
        """
        Update STATE.md with current progress.

        Writes progress information back to STATE.md file.
        """
        state_path = self.planning_dir / "STATE.md"
        if not state_path.exists():
            return

        content = state_path.read_text(encoding="utf-8")
        lines = content.split("\n")

        # Find and update relevant sections
        new_lines = []
        in_progress_section = False

        for line in lines:
            if line.startswith("## Current Position"):
                in_progress_section = True
                new_lines.append(line)
                continue

            if in_progress_section and line.startswith("## "):
                in_progress_section = False

            if in_progress_section:
                # Update Status line
                if line.startswith("Status:"):
                    # Find current phase progress
                    for phase_num, progress in self._progress.items():
                        if progress.status == PhaseStatus.IN_PROGRESS:
                            status_str = "In progress"
                            if progress.blocker:
                                status_str = f"Blocked: {progress.blocker}"
                            line = f"Status: {status_str}"
                            break
                        elif progress.status == PhaseStatus.COMPLETE:
                            line = "Status: Complete"
                            break

            new_lines.append(line)

        state_path.write_text("\n".join(new_lines), encoding="utf-8")

    def list_phases(self) -> list[str]:
        """
        List all tracked phases.

        Returns:
            List of phase numbers in order
        """
        return sorted(self._progress.keys())

    def get_active_phase(self) -> Optional[str]:
        """
        Get the currently active (in progress) phase.

        Returns:
            Phase number if found, None otherwise
        """
        for phase_num, progress in self._progress.items():
            if progress.status == PhaseStatus.IN_PROGRESS:
                return phase_num
        return None

    def get_blocked_phases(self) -> list[str]:
        """
        Get all blocked phases.

        Returns:
            List of blocked phase numbers
        """
        return [
            phase_num
            for phase_num, progress in self._progress.items()
            if progress.status == PhaseStatus.BLOCKED
        ]
