"""
Pydantic models for Screen Test state management.

Tracks per-character, per-wardrobe-phase approval status during the
Screen Test feature — where the director reviews generated character
images and locks approved looks.

Status lifecycle: empty → generating → generated → held | locked | rejected
"""

from __future__ import annotations

import json
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional

from pydantic import BaseModel, Field

from recoil.core.paths import STATE_NAMESPACE
from recoil.pipeline._lib.schema_versions import RENDER_SCHEMA_VERSION


# ---------------------------------------------------------------------------
# State file path
# ---------------------------------------------------------------------------

_STATE_FILENAME = "screen_test_state.json"
_STATE_SUBDIR = Path("state") / STATE_NAMESPACE


# ---------------------------------------------------------------------------
# Models
# ---------------------------------------------------------------------------


class GenerationRecord(BaseModel):
    """One generation attempt for a wardrobe phase."""
    image_path: str = Field(..., description="Path to the generated image")
    prompt: str = Field(..., description="Prompt used for generation")
    note: Optional[str] = Field(default=None, description="Director note on this generation")
    timestamp: datetime = Field(
        default_factory=lambda: datetime.now(timezone.utc),
        description="When this generation was created",
    )
    verdict: Optional[str] = Field(
        default=None,
        description="Verdict applied: lock, hold, or reject",
    )


class PhaseState(BaseModel):
    """State for one wardrobe phase of a character screen test."""
    phase_id: str = Field(..., description="Wardrobe phase ID, e.g. jinx_salvager")
    status: str = Field(
        default="empty",
        description="Current status: empty, generating, generated, held, locked, rejected",
    )
    locked_image: Optional[str] = Field(
        default=None,
        description="Path to the approved/locked image",
    )
    director_note: Optional[str] = Field(
        default=None,
        description="Standing director note for this phase",
    )
    enriched_prompt: Optional[str] = Field(
        default=None,
        description="Enriched prompt for generation",
    )
    held_images: list[str] = Field(
        default_factory=list,
        description="Paths to images held for comparison",
    )
    generation_history: list[GenerationRecord] = Field(
        default_factory=list,
        description="Full history of generation attempts",
    )
    bible_synced: bool = Field(
        default=False,
        description="Whether bible text has been synced to match the locked image",
    )


class CharacterScreenTest(BaseModel):
    """Screen test state for one character across all wardrobe phases."""
    anchor_phase: Optional[str] = Field(
        default=None,
        description="Phase ID designated as the anchor/primary look",
    )
    phases: dict[str, PhaseState] = Field(
        default_factory=dict,
        description="Keyed by phase_id",
    )


class ScreenTestState(BaseModel):
    """Top-level screen test state for all characters."""
    schema_version: int = Field(
        default=RENDER_SCHEMA_VERSION,
        description="Persisted-shape schema version. Bump on breaking change.",
    )
    characters: dict[str, CharacterScreenTest] = Field(
        default_factory=dict,
        description="Keyed by char_id",
    )


# ---------------------------------------------------------------------------
# Persistence
# ---------------------------------------------------------------------------


def load_screen_test_state(project_dir: Path) -> ScreenTestState:
    """Load screen test state from disk. Returns empty state if file missing."""
    state_path = Path(project_dir) / _STATE_SUBDIR / _STATE_FILENAME
    if not state_path.exists():
        return ScreenTestState()
    data = json.loads(state_path.read_text())
    return ScreenTestState.model_validate(data)


def save_screen_test_state(project_dir: Path, state: ScreenTestState) -> None:
    """Save screen test state to disk, creating directories as needed."""
    state_dir = Path(project_dir) / _STATE_SUBDIR
    state_dir.mkdir(parents=True, exist_ok=True)
    state_path = state_dir / _STATE_FILENAME
    state_path.write_text(state.model_dump_json(indent=2))


# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------


def record_generation(
    phase: PhaseState,
    image_path: str,
    prompt: str,
    note: Optional[str] = None,
) -> None:
    """Append a generation record and set status to 'generated'."""
    record = GenerationRecord(
        image_path=image_path,
        prompt=prompt,
        note=note,
    )
    phase.generation_history.append(record)
    phase.status = "generated"


def apply_verdict(phase: PhaseState, action: str) -> None:
    """Apply lock/hold/reject verdict to the latest generation.

    Raises ValueError if there is no generation history.
    """
    if not phase.generation_history:
        raise ValueError(
            f"Cannot apply verdict '{action}' — no generation history for phase '{phase.phase_id}'"
        )

    latest = phase.generation_history[-1]
    latest.verdict = action

    if action == "lock":
        phase.locked_image = latest.image_path
        phase.status = "locked"
    elif action == "hold":
        phase.held_images.append(latest.image_path)
        phase.status = "held"
    elif action == "reject":
        phase.status = "rejected"
    else:
        raise ValueError(f"Unknown verdict action: {action}")
