"""alice recall — Browse and search past sessions from the Cloud Locker."""

from __future__ import annotations

from typing import TYPE_CHECKING

import click
from rich.table import Table

from alice_cli import personality
from alice_cli.auth import resolve_username
from alice_cli.console import (
    err_console,
    print_banner,
    print_rule,
    print_success,
    spinner,
)
from alice_cli.constants import get_memory_bucket
from alice_cli.errors import AliceCLIError
from alice_cli.locker import CloudLocker

if TYPE_CHECKING:
    from alice_cli.models import (
        IndexEntry,
    )

# ---------------------------------------------------------------------------
# Pure filtering function (testable without I/O)
# ---------------------------------------------------------------------------

_VALID_TYPES = ("chat", "invoke", "dialog", "summarize", "batch", "compare", "thread", "teaparty")


def filter_entries(
    entries: list[IndexEntry],
    *,
    since: str | None = None,
    until: str | None = None,
    model: str | None = None,
    entry_type: str | None = None,
    keyword: str | None = None,
    limit: int = 20,
) -> list[IndexEntry]:
    """Filter, sort, and limit index entries.

    All filters are applied conjunctively (AND). The result is sorted by
    timestamp descending and truncated to *limit* entries.
    """
    result = list(entries)

    # ── Date filters ──────────────────────────────────────────────────
    if since is not None:
        result = [e for e in result if e.timestamp[:10] >= since]

    if until is not None:
        result = [e for e in result if e.timestamp[:10] <= until]

    # ── Model filter (case-insensitive substring) ─────────────────────
    if model is not None:
        model_lower = model.lower()
        result = [e for e in result if model_lower in e.model.lower()]

    # ── Type filter ───────────────────────────────────────────────────
    if entry_type is not None:
        type_lower = entry_type.lower()
        result = [e for e in result if e.type.lower() == type_lower]

    # ── Keyword filter (case-insensitive on prompt_preview) ───────────
    if keyword is not None:
        kw_lower = keyword.lower()
        result = [e for e in result if kw_lower in e.prompt_preview.lower()]

    # ── Sort by timestamp descending ──────────────────────────────────
    result.sort(key=lambda e: e.timestamp, reverse=True)

    # ── Apply limit ───────────────────────────────────────────────────
    if limit > 0:
        result = result[:limit]

    return result


# ---------------------------------------------------------------------------
# Click command
# ---------------------------------------------------------------------------


@click.command("recall")
@click.option("--since", default=None, help="Show sessions on or after this date (YYYY-MM-DD)")
@click.option("--until", default=None, help="Show sessions on or before this date (YYYY-MM-DD)")
@click.option("--model", default=None, help="Filter by model alias or ID (substring match)")
@click.option(
    "--type",
    "entry_type",
    default=None,
    type=click.Choice(_VALID_TYPES, case_sensitive=False),
    help="Filter by session type",
)
@click.option("--keyword", default=None, help="Search prompt previews (case-insensitive)")
@click.option("--limit", default=20, type=int, help="Maximum number of results (default: 20)")
@click.option("--show", default=None, help="Show full session record for a given session ID")
@click.pass_context
def recall_cmd(
    ctx: click.Context,
    since: str | None,
    until: str | None,
    model: str | None,
    entry_type: str | None,
    keyword: str | None,
    limit: int,
    show: str | None,
) -> None:
    """Browse and search your session history.

    \b
    Examples:
        alice recall
        alice recall --since 2025-01-01
        alice recall --model sonnet --type chat
        alice recall --keyword "Krebs cycle" --limit 5
        alice recall --show <session-id>
    """
    config = ctx.obj["config"]

    print_banner(personality.BANNER, config.quiet)

    # ── Authenticate ──────────────────────────────────────────────────
    username = resolve_username(config)

    locker = CloudLocker(
        config=config,
        username=username,
        bucket=get_memory_bucket(config.namespace, config.environment),
    )

    # ── --show: display a full session record ─────────────────────────
    if show:
        _show_session(locker, show, config.quiet)
        return

    # ── Load index and filter ─────────────────────────────────────────
    with spinner("Loading session index…", config.quiet):
        index = locker.load_index()

    filtered = filter_entries(
        index.entries,
        since=since,
        until=until,
        model=model,
        entry_type=entry_type,
        keyword=keyword,
        limit=limit,
    )

    if not filtered:
        err_console.print("  [dim]No matching sessions found.[/dim]")
        return

    # ── Display Rich table ────────────────────────────────────────────
    table = Table(title="Sessions", show_lines=False)
    table.add_column("Timestamp", style="cyan", no_wrap=True)
    table.add_column("Type", style="magenta")
    table.add_column("Model", style="green")
    table.add_column("Prompt Preview", style="white")

    for entry in filtered:
        table.add_row(
            entry.timestamp[:19],
            entry.type,
            entry.model,
            entry.prompt_preview[:80],
        )

    err_console.print(table)
    print_success(f"Showing {len(filtered)} session(s)", config.quiet)


def _show_session(locker: CloudLocker, session_id: str, quiet: bool) -> None:
    """Retrieve and display a full session record."""
    # Try common categories until we find the session
    categories = ("chat", "invoke", "dialog", "batch", "compare", "teaparty")
    record = None
    for cat in categories:
        try:
            record = locker.load_session(session_id, cat)
            break
        except Exception:
            continue

    if record is None:
        raise AliceCLIError(f"Session {session_id} not found.")

    print_rule(quiet)
    err_console.print(f"  [bold]Session:[/bold] {record.id}")
    err_console.print(f"  [bold]Type:[/bold]    {record.type}")
    err_console.print(f"  [bold]Time:[/bold]    {record.timestamp}")
    err_console.print(f"  [bold]Model:[/bold]   {record.model}")
    print_rule(quiet)

    if record.prompt:
        err_console.print("\n[bold]Prompt:[/bold]")
        err_console.print(record.prompt)

    if record.response:
        err_console.print("\n[bold]Response:[/bold]")
        err_console.print(record.response)

    if record.tokens:
        err_console.print(
            f"\n[dim]Tokens: {record.tokens.input} in / "
            f"{record.tokens.output} out / {record.tokens.total} total[/dim]"
        )

    if record.metadata:
        err_console.print("\n[bold]Metadata:[/bold]")
        import json

        err_console.print(json.dumps(record.metadata, indent=2, ensure_ascii=False))

    if record.type == "teaparty":
        err_console.print(f"\n[dim]Download MP3: alice teaparty --get {record.id}[/dim]")

    print_rule(quiet)
