"""CLI command for listing recent branches or branch events."""

from __future__ import annotations

import time
from concurrent.futures import ThreadPoolExecutor, as_completed
from dataclasses import dataclass
from datetime import datetime
from typing import Optional

import typer
from rich.console import Console
from rich.table import Table

from terminaluse.core import ApiError
from terminaluse.lib.cli.utils.cli_utils import (
    format_git_hash,
    format_git_info,
    format_relative_time,
    format_status,
    format_version_id,
    get_agent_name,
    parse_agent_name,
)
from terminaluse.lib.cli.utils.client import get_authenticated_client
from terminaluse.lib.cli.utils.errors import CLIError, ErrorCode, ExitCode
from terminaluse.lib.client import TerminalUse
from terminaluse.lib.utils.logging import make_logger
from terminaluse.types import BranchResponse

logger = make_logger(__name__)
console = Console()


@dataclass
class VersionDisplay:
    """Version data for display."""

    id: str
    git_branch: str
    git_hash: str
    git_message: str | None
    status: str
    deployed_at: datetime
    author_name: str
    is_dirty: bool


@dataclass
class EventDisplay:
    """Event data for display."""

    created_at: datetime
    event_type: str
    version_id: str
    triggered_by: str | None
    actor_email: str | None


def _format_event_type(event_type: str) -> str:
    """Format event type with color."""
    # Remove VERSION_ prefix for cleaner display
    display_type = event_type.replace("VERSION_", "")

    if display_type in ("CREATED", "ACTIVATED"):
        return f"[green]{display_type}[/green]"
    elif display_type == "FAILED":
        return f"[red]{display_type}[/red]"
    elif display_type in ("ROLLED_BACK_FROM", "ROLLED_BACK_TO"):
        return f"[yellow]{display_type}[/yellow]"
    elif display_type in ("DRAINING", "RETIRED"):
        return f"[dim]{display_type}[/dim]"
    elif display_type == "REDEPLOYED":
        return f"[cyan]{display_type}[/cyan]"
    return display_type


def _list_versions(
    client: TerminalUse,
    agent_name: str,
    limit: int,
    include_retired: bool,
    json_output: bool = False,
) -> None:
    """List recent versions across all branches."""
    import json

    start_time = time.time()
    namespace_slug, short_name = parse_agent_name(agent_name)

    # Step 1: Get all branches
    branches_response = client.agents.branches.list(
        namespace_slug=namespace_slug,
        agent_name=short_name,
        include_retired=include_retired,
    )

    if not branches_response.branches:
        if json_output:
            typer.echo(json.dumps([]))
        else:
            console.print(f"No branches found for agent '[bold]{agent_name}[/bold]'.")
            console.print("\nDeploy with: [cyan]tu deploy[/cyan]")
        return

    # Step 2: Fetch versions from each branch in parallel
    all_versions: list[VersionDisplay] = []
    versions_per_branch = max(3, limit // len(branches_response.branches) + 1)

    def fetch_versions(branch: BranchResponse):
        try:
            response = client.branches.versions.list(
                branch_id=branch.id,
                limit=versions_per_branch,
            )
            return [
                VersionDisplay(
                    id=v.id,
                    git_branch=v.git_branch,
                    git_hash=v.git_hash,
                    git_message=v.git_message,
                    status=v.status,
                    deployed_at=v.deployed_at,
                    author_name=v.author_name,
                    is_dirty=v.is_dirty or False,
                )
                for v in response.versions
            ]
        except Exception as e:
            logger.warning(f"Failed to fetch versions for {branch.git_branch}: {e}")
            return []

    with ThreadPoolExecutor(max_workers=5) as executor:
        futures = [executor.submit(fetch_versions, branch) for branch in branches_response.branches]
        for future in as_completed(futures):
            all_versions.extend(future.result())

    if not all_versions:
        if json_output:
            typer.echo(json.dumps([]))
        else:
            console.print(f"No versions found for agent '[bold]{agent_name}[/bold]'.")
        return

    # Step 3: Sort by deployed_at and limit
    all_versions.sort(key=lambda v: v.deployed_at, reverse=True)
    display_versions = all_versions[:limit]

    # JSON output mode
    if json_output:
        output = [
            {
                "id": ver.id,
                "git_branch": ver.git_branch,
                "git_hash": ver.git_hash,
                "git_message": ver.git_message,
                "status": ver.status,
                "deployed_at": ver.deployed_at.isoformat() if ver.deployed_at else None,
                "author_name": ver.author_name,
                "is_dirty": ver.is_dirty,
            }
            for ver in display_versions
        ]
        typer.echo(json.dumps(output, default=str))
        return

    # Step 4: Display
    elapsed_ms = int((time.time() - start_time) * 1000)
    console.print(f"> Versions for [bold]{agent_name}[/bold] [{elapsed_ms}ms]\n")

    table = Table(show_header=True, header_style="bold")
    table.add_column("AGE", style="dim")
    table.add_column("BRANCH", style="cyan")
    table.add_column("VERSION", style="dim")
    table.add_column("STATUS")
    table.add_column("GIT")
    table.add_column("AUTHOR")

    for ver in display_versions:
        table.add_row(
            format_relative_time(ver.deployed_at),
            ver.git_branch,
            format_version_id(ver.id),
            format_status(ver.status),
            format_git_info(ver.git_hash, ver.git_message, ver.is_dirty),
            ver.author_name,
        )

    console.print(table)

    if len(all_versions) > limit:
        console.print(f"\n[dim]Showing {limit} of {len(all_versions)}. Use -n to see more.[/dim]")


def _list_events(
    client: TerminalUse,
    agent_name: str,
    branch: str,
    limit: int,
    json_output: bool = False,
) -> None:
    """List events for a specific branch."""
    import json

    start_time = time.time()
    namespace_slug, short_name = parse_agent_name(agent_name)

    # Step 1: Get the branch
    try:
        branch_info = client.agents.branches.retrieve(
            namespace_slug=namespace_slug,
            agent_name=short_name,
            branch=branch,
        )
    except CLIError:
        raise
    except ApiError:
        raise
    except Exception as e:
        logger.debug(f"Failed to get branch: {e}")
        raise CLIError(
            message=f"Failed to retrieve branch '{branch}': {e}",
            code=ErrorCode.UNKNOWN_ERROR,
            exit_code=ExitCode.INTERNAL_ERROR,
            hint="This may be a temporary issue. Try again or check your network connection.",
        ) from e

    # Step 2: Fetch events
    try:
        events_response = client.branches.events.list(branch_id=branch_info.id, limit=limit)
    except CLIError:
        raise
    except ApiError:
        raise
    except Exception as e:
        raise CLIError(
            message=f"Failed to fetch events: {e}",
            code=ErrorCode.UNKNOWN_ERROR,
            exit_code=ExitCode.INTERNAL_ERROR,
        ) from e

    if not events_response.events:
        if json_output:
            typer.echo(json.dumps([]))
        else:
            console.print(f"No events found for [bold]{agent_name}[/bold]@{branch}.")
        return

    # Step 3: Build display data and sort chronologically (most recent first)
    events: list[EventDisplay] = []
    for ev in events_response.events:
        events.append(
            EventDisplay(
                created_at=ev.created_at,
                event_type=ev.event_type,
                version_id=ev.version_id,
                triggered_by=ev.content.triggered_by if ev.content else None,
                actor_email=ev.content.actor_email if ev.content else None,
            )
        )
    events.sort(key=lambda e: e.created_at, reverse=True)

    # Step 4: Fetch version details for git hash display
    unique_version_ids = {ev.version_id for ev in events if ev.version_id}
    version_info: dict[str, tuple[str, bool]] = {}  # version_id -> (git_hash, is_dirty)

    def fetch_version(version_id: str) -> tuple[str, str, bool] | None:
        try:
            version = client.versions.retrieve(version_id)
            return (version_id, version.git_hash, version.is_dirty or False)
        except Exception as e:
            logger.debug(f"Failed to fetch version {version_id}: {e}")
            return None

    with ThreadPoolExecutor(max_workers=5) as executor:
        futures = [executor.submit(fetch_version, vid) for vid in unique_version_ids]
        for future in as_completed(futures):
            result = future.result()
            if result:
                vid, git_hash, is_dirty = result
                version_info[vid] = (git_hash, is_dirty)

    # JSON output mode
    if json_output:
        output = [
            {
                "created_at": ev.created_at.isoformat() if ev.created_at else None,
                "event_type": ev.event_type,
                "version_id": ev.version_id,
                "git_hash": version_info.get(ev.version_id, (None, False))[0] if ev.version_id else None,
                "triggered_by": ev.triggered_by,
                "actor_email": ev.actor_email,
            }
            for ev in events
        ]
        typer.echo(json.dumps(output, default=str))
        return

    # Step 5: Display
    elapsed_ms = int((time.time() - start_time) * 1000)
    console.print(f"> Events for [bold]{agent_name}[/bold]@{branch} [{elapsed_ms}ms]\n")

    table = Table(show_header=True, header_style="bold")
    table.add_column("AGE", style="dim")
    table.add_column("EVENT")
    table.add_column("VERSION", style="dim")
    table.add_column("GIT")
    table.add_column("TRIGGER", style="dim")

    for ev in events:
        git_display = "-"
        if ev.version_id and ev.version_id in version_info:
            git_hash, is_dirty = version_info[ev.version_id]
            git_display = format_git_hash(git_hash, is_dirty)

        table.add_row(
            format_relative_time(ev.created_at),
            _format_event_type(ev.event_type),
            format_version_id(ev.version_id) if ev.version_id else "-",
            git_display,
            ev.triggered_by.lower() if ev.triggered_by else "-",
        )

    console.print(table)

    if events_response.has_more:
        console.print(f"\n[dim]Showing {len(events)} events. Use -n to see more.[/dim]")


def ls(
    branch: Optional[str] = typer.Argument(None, help="Branch name to show events for"),
    limit: int = typer.Option(10, "--limit", "-n", help="Number of items to show"),
    agent: Optional[str] = typer.Option(None, "--agent", "-a", help="Agent name (defaults to config)"),
    config: Optional[str] = typer.Option(None, "--config", "-c", help="Path to config file"),
    include_retired: bool = typer.Option(False, "--all", help="Include retired branches (versions mode only)"),
    json_output: bool = typer.Option(False, "--json", "-j", help="Output as JSON"),
):
    """
    List recent branches or events for a branch.

    Without arguments, shows recent versions across all branches.
    With a branch name, shows lifecycle events for that branch.

    Examples:
        tu ls              # List recent versions
        tu ls main         # List events for 'main' branch
        tu ls -n 20        # Show 20 most recent versions
        tu ls --json       # Output as JSON for scripting
    """
    agent_name = get_agent_name(agent, config)
    client = get_authenticated_client()

    try:
        if branch:
            # Show events for specific branch
            _list_events(client, agent_name, branch, limit, json_output)
        else:
            # Show versions across all branches
            _list_versions(client, agent_name, limit, include_retired, json_output)

    except typer.Exit:
        raise
    except CLIError:
        raise
    except ApiError as e:
        # Let global handler format API errors consistently
        raise CLIError.from_api_error(e, resource_type="agent") from e
    except Exception as e:
        logger.exception("Failed to list branches")
        raise CLIError(
            message=f"Failed to list branches: {e}",
            code=ErrorCode.UNKNOWN_ERROR,
            exit_code=ExitCode.INTERNAL_ERROR,
        ) from e
