"""CLI entry points for the Unfamiliar tool. Uses Typer for command routing."""

from pathlib import Path

import typer

from unfamiliar import __version__
from unfamiliar.campaign import (
    CampaignExistsError,
    CampaignOrchestrationError,
    MissionStatusError,
    create_campaign,
    discover_campaigns,
    get_campaign_progress,
    reopen_campaign,
    resume_campaign,
)
from unfamiliar.config import ConfigError, get_campaign_dir, load_config
from unfamiliar.constants import CampaignStatus
from unfamiliar.db import open_campaign_db
from unfamiliar.tui import run_tui
from unfamiliar.tui.themes.film_noir.theme import FILM_NOIR_THEME
from unfamiliar.tui.themes.registry import ThemeRegistry

app = typer.Typer(
    name="unfamiliar",
    help="AI-powered codebase onboarding tool with gamified investigation missions.",
)


def _version_callback(value: bool) -> None:
    if value:
        typer.echo(f"unfamiliar {__version__}")
        raise typer.Exit()


@app.callback()
def main(
    version: bool | None = typer.Option(
        None,
        "--version",
        "-V",
        callback=_version_callback,
        is_eager=True,
        help="Show version and exit.",
    ),
) -> None:
    """AI-powered codebase onboarding tool with gamified investigation missions."""


@app.command()
def investigate(
    repo_path: Path = typer.Argument(  # noqa: B008 — Typer requires function call as default for Argument
        ...,
        exists=True,
        file_okay=False,
        dir_okay=True,
        resolve_path=True,
        help="Path to the codebase repository to investigate.",
    ),
) -> None:
    """Start a new investigation or resume an existing one.

    Analyzes the codebase and generates gamified missions to guide you
    through understanding the code. Use 'unfamiliar campaigns' to list
    all saved campaigns.
    """
    _validate_git_repo(repo_path)

    try:
        config = load_config()
    except ConfigError as exc:
        typer.secho(f"Configuration error: {exc}", fg=typer.colors.RED, err=True)
        raise typer.Exit(1) from exc

    registry = ThemeRegistry()
    registry.register(FILM_NOIR_THEME)
    theme = registry.get(config.theme)

    def progress_callback(message: str) -> None:
        """Print progress updates to stderr."""
        typer.secho(f"  {message}", fg=typer.colors.CYAN, err=True)

    typer.secho(f"Starting investigation for: {repo_path}", fg=typer.colors.GREEN)

    try:
        campaign = create_campaign(
            repo_path=repo_path,
            config=config,
            progress_callback=progress_callback,
            theme=theme,
        )
    except CampaignExistsError as exc:
        # Prompt user to confirm overwrite
        typer.secho(
            f"\nExisting investigation found with "
            f"{exc.completed_count}/{exc.total_count} missions completed.",
            fg=typer.colors.YELLOW,
        )
        if typer.confirm("Overwrite and start fresh?"):
            typer.secho("Starting fresh investigation...", fg=typer.colors.CYAN)
            campaign = create_campaign(
                repo_path=repo_path,
                config=config,
                progress_callback=progress_callback,
                force=True,
                theme=theme,
            )
        else:
            typer.secho("Cancelled.", fg=typer.colors.YELLOW)
            raise typer.Exit(0) from None
    except CampaignOrchestrationError as exc:
        typer.secho(f"Error: {exc}", fg=typer.colors.RED, err=True)
        raise typer.Exit(1) from exc

    # Get progress stats
    campaign_dir = get_campaign_dir(repo_path)
    with open_campaign_db(campaign_dir) as db:
        progress = get_campaign_progress(db, campaign.id)

    # Show clear mission status breakdown
    typer.secho(
        "\nInvestigation ready:",
        fg=typer.colors.GREEN,
    )
    typer.secho(
        f"  {progress.available_missions} available to start",
        fg=typer.colors.GREEN,
    )
    if progress.locked_missions > 0:
        typer.secho(
            f"  {progress.locked_missions} locked (complete prerequisites to unlock)",
            fg=typer.colors.YELLOW,
        )
    if progress.completed_missions > 0:
        typer.secho(
            f"  {progress.completed_missions} completed",
            fg=typer.colors.CYAN,
        )

    typer.secho("\nLaunching investigation terminal...", fg=typer.colors.CYAN)
    _launch_tui(campaign_dir, campaign.id)


@app.command()
def campaigns() -> None:
    """List all saved campaigns.

    Shows campaign name, repository, status, missions completed/total,
    and last activity date.
    """
    try:
        load_config()  # validate config exists
    except ConfigError as exc:
        typer.secho(f"Configuration error: {exc}", fg=typer.colors.RED, err=True)
        raise typer.Exit(1) from exc

    discovered = discover_campaigns()

    if not discovered:
        typer.secho("No campaigns found.", fg=typer.colors.YELLOW)
        typer.secho(
            "Run 'unfamiliar investigate <repo>' to start a new investigation.",
            fg=typer.colors.CYAN,
        )
        return

    typer.secho("\nSaved Campaigns:", fg=typer.colors.GREEN, bold=True)
    typer.secho("")

    for info in discovered:
        # Format status with color
        if info.is_orphaned:
            status_str = typer.style("ORPHANED", fg=typer.colors.RED)
        elif info.status.value == CampaignStatus.ACTIVE:
            status_str = typer.style("ACTIVE", fg=typer.colors.GREEN)
        elif info.status.value == CampaignStatus.INITIALIZING:
            status_str = typer.style("INITIALIZING", fg=typer.colors.YELLOW)
        else:
            status_str = typer.style(info.status.value.upper(), fg=typer.colors.WHITE)

        # Format repo name
        repo_name = info.repo_path.name if info.repo_path else "(unknown)"

        # Format progress
        progress_str = (
            f"{info.completed_count}/{info.mission_count} missions"
            if info.mission_count > 0
            else "no missions"
        )

        # Format timestamp
        updated_str = (
            info.updated_at.strftime("%Y-%m-%d %H:%M") if info.updated_at else "unknown"
        )

        typer.echo(f"  {repo_name}")
        typer.secho(f"    Status: {status_str}  •  {progress_str}  •  {updated_str}")

        if info.is_orphaned:
            typer.secho(
                "    Warning: Repository path no longer exists",
                fg=typer.colors.RED,
            )

    typer.secho(
        f"\n{len(discovered)} campaign(s) found",
        fg=typer.colors.CYAN,
    )


@app.command()
def resume() -> None:
    """Resume the most recent active campaign.

    Opens the TUI for the last campaign that was in progress.
    """
    try:
        load_config()
    except ConfigError as exc:
        typer.secho(f"Configuration error: {exc}", fg=typer.colors.RED, err=True)
        raise typer.Exit(1) from exc

    campaign = resume_campaign()

    if campaign is None:
        typer.secho("No active campaigns found.", fg=typer.colors.YELLOW)
        typer.secho(
            "Run 'unfamiliar investigate <repo>' to start a new investigation.",
            fg=typer.colors.CYAN,
        )
        return

    # Get progress
    campaign_dir = get_campaign_dir(Path(campaign.repo_path))
    with open_campaign_db(campaign_dir) as db:
        progress = get_campaign_progress(db, campaign.id)

    typer.secho(f"Resuming: {campaign.repo_path}", fg=typer.colors.GREEN)
    typer.secho(
        f"  Progress: {progress.completed_missions}/{progress.total_missions} missions",
        fg=typer.colors.CYAN,
    )
    typer.secho(
        f"  Available: {progress.available_missions}  •  "
        f"Locked: {progress.locked_missions}  •  "
        f"In Progress: {progress.in_progress_missions}",
        fg=typer.colors.CYAN,
    )

    typer.secho(
        "\nLaunching investigation terminal...",
        fg=typer.colors.CYAN,
    )
    _launch_tui(campaign_dir, campaign.id)


@app.command()
def reopen(
    repo_path: Path = typer.Argument(  # noqa: B008 — Typer requires function call as default for Argument
        ...,
        exists=True,
        file_okay=False,
        dir_okay=True,
        resolve_path=True,
        help="Path to the repository with a closed campaign.",
    ),
) -> None:
    """Reopen a closed campaign.

    Restores a CLOSED campaign to ACTIVE status so you can continue
    your investigation. Use 'unfamiliar campaigns' to see which
    campaigns are closed.
    """
    try:
        load_config()
    except ConfigError as exc:
        typer.secho(f"Configuration error: {exc}", fg=typer.colors.RED, err=True)
        raise typer.Exit(1) from exc

    campaign_dir = get_campaign_dir(repo_path)

    with open_campaign_db(campaign_dir) as db:
        campaign = db.campaign_repo.get_by_repo_path(str(repo_path.resolve()))
        if campaign is None:
            typer.secho(
                f"No campaign found for: {repo_path}",
                fg=typer.colors.RED,
                err=True,
            )
            raise typer.Exit(1)

        try:
            reopen_campaign(db, campaign.id)
            typer.secho(
                f"Case reopened: {repo_path}",
                fg=typer.colors.GREEN,
            )
            typer.secho(
                "Continue your investigation with 'unfamiliar resume' or "
                "'unfamiliar investigate <repo>'.",
                fg=typer.colors.CYAN,
            )
        except MissionStatusError as exc:
            typer.secho(f"Cannot reopen: {exc}", fg=typer.colors.RED, err=True)
            raise typer.Exit(1) from exc



@app.command()
def upgrade(
    campaign: str | None = typer.Argument(  # noqa: B008 — Typer requires function call as default for Argument
        None, help="Campaign name or ID to upgrade. If omitted, upgrades all."
    ),
) -> None:
    """Upgrade an older campaign to the current schema version.

    Ensures compatibility when the data model changes between releases.
    """
    try:
        load_config()
    except ConfigError as exc:
        typer.secho(f"Configuration error: {exc}", fg=typer.colors.RED, err=True)
        raise typer.Exit(1) from exc

    # All campaigns use schema 1.0 for MVP
    typer.secho(
        "Schema upgrade not needed for v1.0 — all campaigns use schema 1.0",
        fg=typer.colors.GREEN,
    )


def _validate_git_repo(path: Path) -> None:
    """Verify that a path is a git repository."""
    git_dir = path / ".git"
    if not git_dir.exists():
        typer.secho(
            f"Error: '{path}' is not a git repository.",
            fg=typer.colors.RED,
            err=True,
        )
        raise typer.Exit(1)


def _launch_tui(campaign_dir: Path, campaign_id: int) -> None:
    """Launch the TUI application.

    Args:
        campaign_dir: Path to the campaign directory.
        campaign_id: ID of the campaign to load.
    """

    run_tui(campaign_dir, campaign_id)
