"""Replay — birth stories for skills, showing the full build journey."""

from __future__ import annotations

import json
import time
from dataclasses import asdict, dataclass, field
from pathlib import Path
from typing import Any

from rich.console import Console
from rich.panel import Panel
from rich.syntax import Syntax
from rich.table import Table
from rich.text import Text


@dataclass
class BuildAttemptRecord:
    """One attempt during skill building."""

    attempt: int
    code: str
    test_result: str  # "pass" or "fail"
    error: str | None = None
    fix_description: str | None = None


@dataclass
class BirthStory:
    """The full build journey of a skill."""

    skill_name: str
    original_request: str
    plan_step: str = ""
    attempts: list[BuildAttemptRecord] = field(default_factory=list)
    total_time_ms: int = 0
    final_approach: str = ""
    created_at: str = ""


# ── Recording ────────────────────────────────────────────────────────────────


class BirthStoryRecorder:
    """Records build attempts as they happen, then saves to disk."""

    def __init__(self, skill_name: str, original_request: str, plan_step: str = "") -> None:
        self._story = BirthStory(
            skill_name=skill_name,
            original_request=original_request,
            plan_step=plan_step,
        )
        self._start_time = time.monotonic()

    def record_attempt(
        self,
        attempt: int,
        code: str,
        *,
        success: bool,
        error: str | None = None,
        fix_description: str | None = None,
    ) -> None:
        """Record a single build attempt."""
        self._story.attempts.append(BuildAttemptRecord(
            attempt=attempt,
            code=code,
            test_result="pass" if success else "fail",
            error=error,
            fix_description=fix_description,
        ))

    def finalize(self, final_approach: str = "") -> BirthStory:
        """Mark the story as complete and return it."""
        from datetime import datetime, timezone

        self._story.total_time_ms = int((time.monotonic() - self._start_time) * 1000)
        self._story.final_approach = final_approach
        self._story.created_at = datetime.now(timezone.utc).isoformat()
        return self._story


def save_birth_story(story: BirthStory, skills_dir: Path | None = None) -> Path:
    """Write birth_story.json to the skill's directory."""
    from alive.config import SKILLS_DIR

    skills_dir = skills_dir or SKILLS_DIR
    story_path = skills_dir / story.skill_name / "birth_story.json"

    if not story_path.parent.exists():
        raise FileNotFoundError(
            f"Skill directory not found: {story_path.parent}\n\n"
            f"Build the skill first with: alive build \"{story.original_request}\""
        )

    data = asdict(story)
    story_path.write_text(json.dumps(data, indent=2) + "\n")
    return story_path


def load_birth_story(skill_name: str, skills_dir: Path | None = None) -> BirthStory:
    """Load a birth story from disk."""
    from alive.config import SKILLS_DIR

    skills_dir = skills_dir or SKILLS_DIR
    story_path = skills_dir / skill_name / "birth_story.json"

    if not story_path.exists():
        raise FileNotFoundError(
            f"No birth story found for '{skill_name}'.\n\n"
            f"Birth stories are recorded when skills are built.\n"
            f"Only skills built after this feature was added have stories."
        )

    data = json.loads(story_path.read_text())
    attempts = [BuildAttemptRecord(**a) for a in data.get("attempts", [])]
    return BirthStory(
        skill_name=data["skill_name"],
        original_request=data["original_request"],
        plan_step=data.get("plan_step", ""),
        attempts=attempts,
        total_time_ms=data.get("total_time_ms", 0),
        final_approach=data.get("final_approach", ""),
        created_at=data.get("created_at", ""),
    )


# ── Display ──────────────────────────────────────────────────────────────────


def display_birth_story(story: BirthStory, console: Console | None = None) -> None:
    """Render a birth story as a rich narrative."""
    console = console or Console()
    n = len(story.attempts)

    # Header
    total_secs = story.total_time_ms / 1000
    time_str = f"{total_secs:.1f}s" if total_secs < 60 else f"{total_secs / 60:.1f}m"

    failures = sum(1 for a in story.attempts if a.test_result == "fail")
    journey = "first try" if failures == 0 else f"{failures} failure{'s' if failures != 1 else ''} → success"

    console.print()
    console.print(Panel(
        f"[bold]{story.skill_name}[/]\n\n"
        f"[bold]Request:[/] {story.original_request}\n"
        f"[bold]Attempts:[/] {n}  ({journey})\n"
        f"[bold]Build time:[/] {time_str}\n"
        + (f"[bold]Plan step:[/] {story.plan_step}\n" if story.plan_step else ""),
        title="[bold magenta]⟐ Birth Story[/]",
        border_style="magenta",
        padding=(1, 2),
    ))

    # Each attempt
    for i, attempt in enumerate(story.attempts):
        is_last = i == n - 1
        passed = attempt.test_result == "pass"

        if passed:
            icon = "[bold green]✓[/]"
            status = "[green]PASSED[/]"
            border = "green"
        else:
            icon = "[bold red]✗[/]"
            status = "[red]FAILED[/]"
            border = "red"

        # Attempt header
        header = f"{icon} Attempt {attempt.attempt} — {status}"
        console.print()
        console.print(f"  {header}")

        # Show error if failed
        if not passed and attempt.error:
            error_lines = attempt.error.strip().splitlines()
            # Show last few meaningful lines
            meaningful = [l for l in error_lines if l.strip()][-5:]
            error_text = "\n".join(meaningful)
            console.print(Panel(
                error_text,
                title="[bold red]Error[/]",
                border_style="red",
                padding=(0, 1),
            ))

        # Show fix description if present
        if attempt.fix_description:
            console.print(f"    [yellow]Fix:[/] {attempt.fix_description}")

        # Show code for the final successful attempt
        if is_last and passed:
            # Truncate very long code
            code = attempt.code
            lines = code.splitlines()
            if len(lines) > 30:
                shown = "\n".join(lines[:25])
                shown += f"\n\n# ... ({len(lines) - 25} more lines)"
            else:
                shown = code

            console.print(Panel(
                Syntax(shown, "python", theme="monokai", line_numbers=True),
                title=f"[bold green]Final Code[/]",
                border_style="green",
            ))

    # Final approach summary
    if story.final_approach:
        console.print()
        console.print(Panel(
            story.final_approach,
            title="[bold cyan]What Worked[/]",
            border_style="cyan",
            padding=(1, 2),
        ))

    console.print()
