"""Sphinx Lookup CLI — lightweight attack database query tool.

Connects directly to Supabase REST API. No Docker, Redis, or Elasticsearch required.
"""

from __future__ import annotations

from typing import Optional

import typer
from rich.console import Console
from rich.panel import Panel
from rich.progress import Progress, SpinnerColumn, TextColumn
from rich.table import Table

from sphinx_lookup.client import LookupClient
from sphinx_lookup.config import CONFIG_FILE, LookupConfig

console = Console()
app = typer.Typer(
    name="sphinx-lookup",
    help="Lightweight LLM Attack Database lookup — connects directly to Supabase.",
    no_args_is_help=True,
)


def _get_client() -> LookupClient:
    """Load config and return a client, or exit with guidance."""
    cfg = LookupConfig.load()
    if not cfg.is_configured:
        console.print(
            "[red]Not configured.[/red] Run [bold]sphinx-lookup configure[/bold] "
            "or set environment variables:\n"
            "  - SUPABASE_URL + SUPABASE_KEY (admin mode)\n"
            "  - SUPABASE_URL + SUPABASE_ANON_KEY + SPHINX_API_KEY (team member mode)"
        )
        raise typer.Exit(1)
    return LookupClient(cfg)


# ------------------------------------------------------------------
# configure
# ------------------------------------------------------------------


@app.command()
def configure(
    team_member: bool = typer.Option(
        False, "--team", "-t", help="Configure as team member (use API key instead of service_role)"
    ),
) -> None:
    """Interactive setup — writes credentials to ~/.sphinx-lookup.env.

    Use --team if you received an API key from your admin.
    """
    existing = LookupConfig.load()

    if team_member:
        console.print(
            Panel(
                "[bold]Sphinx Lookup - Team Member Setup[/bold]\n\n"
                "Enter the credentials provided by your admin.\n"
                "These are saved to [cyan]~/.sphinx-lookup.env[/cyan] (mode 600).",
                title="Setup",
            )
        )

        url = typer.prompt(
            "Supabase URL (https://<project>.supabase.co)",
            default=existing.supabase_url or "",
        )
        anon_key = typer.prompt(
            "Supabase anon key (public key from your admin)",
            default=existing.anon_key or "",
        )
        api_key = typer.prompt(
            "Your personal API Key (sk-sphinx-...)",
            default=existing.api_key or "",
            hide_input=True,
        )

        cfg = LookupConfig(supabase_url=url, supabase_key="", anon_key=anon_key, api_key=api_key)
        cfg.save()
    else:
        console.print(
            Panel(
                "[bold]Sphinx Lookup - Admin Setup[/bold]\n\n"
                "Enter your Supabase project credentials (service_role key).\n"
                "These are saved to [cyan]~/.sphinx-lookup.env[/cyan] (mode 600).",
                title="Setup",
            )
        )

        url = typer.prompt(
            "Supabase URL (https://<project>.supabase.co)",
            default=existing.supabase_url or "",
        )
        key = typer.prompt(
            "Supabase service_role key",
            default=existing.supabase_key or "",
            hide_input=True,
        )

        cfg = LookupConfig(supabase_url=url, supabase_key=key, anon_key="", api_key="")
        cfg.save()

    console.print(f"\n[green]Saved to {CONFIG_FILE}[/green]")
    console.print(f"[dim]Auth mode: {cfg.auth_mode}[/dim]")

    # Quick connection test
    try:
        client = LookupClient(cfg)
        total = client.count()
        console.print(f"[green]Connected — {total} attacks in database.[/green]")
    except Exception as exc:
        console.print(f"[yellow]Warning: connection test failed: {exc}[/yellow]")


# ------------------------------------------------------------------
# search
# ------------------------------------------------------------------


@app.command()
def search(
    query: str = typer.Argument(..., help="Search term (matches name and description)"),
    limit: int = typer.Option(20, "--limit", "-l", help="Maximum results"),
) -> None:
    """Search attacks by name or description."""
    client = _get_client()

    with Progress(
        SpinnerColumn(),
        TextColumn("[progress.description]{task.description}"),
        console=console,
        transient=True,
    ) as progress:
        progress.add_task("Searching...", total=None)
        results = client.search(query, limit=limit)

    if not results:
        console.print(f"[yellow]No attacks found for '{query}'[/yellow]")
        return

    table = Table(title=f"Search Results for '{query}' ({len(results)} found)")
    table.add_column("ID", style="dim", max_width=8)
    table.add_column("Name", style="cyan", max_width=45)
    table.add_column("Category", style="green")
    table.add_column("CVSS", justify="right")
    table.add_column("Confidence", justify="right")
    table.add_column("Date")

    for atk in results:
        cvss = atk.get("cvss_estimate")
        cvss_str = f"{cvss:.1f}" if cvss is not None else "-"
        conf = atk.get("confidence_score")
        conf_str = f"{conf:.2f}" if conf is not None else "-"
        name = atk.get("name", "")
        first_seen = (atk.get("first_seen") or "")[:10]

        table.add_row(
            str(atk["id"])[:8],
            name[:45] + "..." if len(name) > 45 else name,
            atk.get("category", "-"),
            cvss_str,
            conf_str,
            first_seen,
        )

    console.print(table)


# ------------------------------------------------------------------
# list
# ------------------------------------------------------------------


@app.command("list")
def list_attacks(
    category: Optional[str] = typer.Option(None, "--category", "-c", help="Filter by category"),
    modality: Optional[str] = typer.Option(
        None, "--modality", "-M", help="Filter by modality (text, reasoning, vision, audio, multimodal, code)"
    ),
    limit: int = typer.Option(20, "--limit", "-l", help="Maximum results"),
    offset: int = typer.Option(0, "--offset", "-o", help="Offset for pagination"),
) -> None:
    """List attacks with optional filters."""
    client = _get_client()

    with Progress(
        SpinnerColumn(),
        TextColumn("[progress.description]{task.description}"),
        console=console,
        transient=True,
    ) as progress:
        progress.add_task("Loading attacks...", total=None)
        results = client.list_attacks(category=category, modality=modality, limit=limit, offset=offset)

    if not results:
        console.print("[yellow]No attacks found[/yellow]")
        return

    table = Table(title=f"LLM Attacks ({len(results)} shown)")
    table.add_column("ID", style="dim", max_width=8)
    table.add_column("Name", style="cyan", max_width=40)
    table.add_column("Category", style="green")
    table.add_column("Modality", style="blue")
    table.add_column("Models", style="magenta")
    table.add_column("CVSS", justify="right")
    table.add_column("Date")

    for atk in results:
        name = atk.get("name", "")
        modality_tags = ", ".join(atk.get("modality_tags") or ["text"])[:15]
        models = ", ".join(atk.get("target_model_families") or ["Generic"])[:20]
        cvss = atk.get("cvss_estimate")
        cvss_str = f"{cvss:.1f}" if cvss is not None else "-"
        first_seen = (atk.get("first_seen") or "")[:10]

        table.add_row(
            str(atk["id"])[:8],
            name[:40] + "..." if len(name) > 40 else name,
            atk.get("category", "-"),
            modality_tags,
            models,
            cvss_str,
            first_seen,
        )

    console.print(table)

    if len(results) == limit:
        console.print(
            f"\n[dim]Showing {offset + 1}-{offset + len(results)}. "
            f"Use --offset {offset + limit} to see more.[/dim]"
        )


# ------------------------------------------------------------------
# show
# ------------------------------------------------------------------


@app.command()
def show(
    attack_id: str = typer.Argument(..., help="Attack ID (full UUID or prefix)"),
) -> None:
    """Show the full attack Steckbrief (card)."""
    client = _get_client()

    with Progress(
        SpinnerColumn(),
        TextColumn("[progress.description]{task.description}"),
        console=console,
        transient=True,
    ) as progress:
        progress.add_task("Loading attack...", total=None)
        attack = client.get_attack(attack_id)

    if not attack:
        console.print(f"[red]Attack not found: {attack_id}[/red]")
        raise typer.Exit(1)

    # Header panel
    console.print(Panel(
        f"[bold cyan]{attack.get('name', 'Unnamed')}[/bold cyan]\n\n"
        f"{attack.get('description', 'No description.')}",
        title=f"Attack: {str(attack['id'])[:8]}",
        subtitle=f"Category: {attack.get('category', '-')}",
    ))

    # Target information
    models = attack.get("target_model_families") or []
    components = attack.get("target_components") or []
    contexts = attack.get("target_deployment_contexts") or []
    if models or components or contexts:
        console.print("\n[bold]Target:[/bold]")
        if models:
            console.print(f"  Models: {', '.join(models)}")
        if components:
            console.print(f"  Components: {', '.join(components)}")
        if contexts:
            console.print(f"  Contexts: {', '.join(contexts)}")

    # Attack steps
    steps = attack.get("steps") or []
    if steps:
        console.print("\n[bold]Attack Steps:[/bold]")
        for i, step in enumerate(steps, 1):
            console.print(f"  {i}. {step}")

    # Prerequisites
    prereqs = attack.get("prerequisites") or []
    if prereqs:
        console.print("\n[bold]Prerequisites:[/bold]")
        for p in prereqs:
            console.print(f"  - {p}")

    # Example payloads
    payloads = attack.get("example_payloads") or []
    if payloads:
        console.print("\n[bold]Example Payloads:[/bold] [dim](copy-paste ready)[/dim]")
        for payload in payloads[:5]:
            console.print(Panel(payload, style="dim", padding=(0, 2)))

    # Severity
    console.print("\n[bold]Severity:[/bold]")
    cvss = attack.get("cvss_estimate")
    if cvss is not None:
        cvss_color = "green" if cvss < 4 else "yellow" if cvss < 7 else "red"
        console.print(f"  CVSS: [{cvss_color}]{cvss:.1f}[/{cvss_color}]")
    console.print(f"  Exploitability: {attack.get('exploitability_level', '-')}")

    conf = attack.get("confidence_score")
    if conf is not None:
        conf_color = "green" if conf >= 0.7 else "yellow" if conf >= 0.4 else "red"
        console.print(f"  Confidence: [{conf_color}]{conf:.2f}[/{conf_color}]")

    # Detection signatures
    sigs = attack.get("detection_signatures") or []
    if sigs:
        console.print("\n[bold]Detection Signatures:[/bold]")
        for sig in sigs[:5]:
            console.print(f"  - {sig}")

    # Behavioral indicators
    indicators = attack.get("behavioral_indicators") or []
    if indicators:
        console.print("\n[bold]Behavioral Indicators:[/bold]")
        for ind in indicators[:5]:
            console.print(f"  - {ind}")

    # Preventive measures
    preventive = attack.get("preventive_measures") or []
    if preventive:
        console.print("\n[bold]Preventive Measures:[/bold]")
        for m in preventive[:5]:
            console.print(f"  - {m}")

    # Corrective measures
    corrective = attack.get("corrective_measures") or []
    if corrective:
        console.print("\n[bold]Corrective Measures:[/bold]")
        for m in corrective[:5]:
            console.print(f"  - {m}")

    # Sources
    attack_sources = attack.get("attack_sources") or []
    sources = [s["sources"] for s in attack_sources if s.get("sources")]
    if sources:
        console.print("\n[bold]Sources:[/bold]")
        for src in sources[:5]:
            title = src.get("title") or "Untitled"
            url = src.get("url") or ""
            console.print(f"  [cyan]{title}[/cyan]")
            if url:
                console.print(f"    [dim]{url}[/dim]")
            authors = src.get("authors") or []
            if authors:
                console.print(f"    Authors: {', '.join(authors[:3])}")
            pub_date = (src.get("published_date") or "")[:10]
            if pub_date:
                console.print(f"    Published: {pub_date}")

    # Modality tags
    modality_tags = attack.get("modality_tags") or []
    if modality_tags:
        console.print(f"\n[bold]Modality:[/bold] {', '.join(modality_tags)}")

    # Timestamps
    first_seen = (attack.get("first_seen") or "")[:10]
    last_updated = (attack.get("last_updated") or "")[:10]
    if first_seen:
        console.print(f"\n[dim]First seen: {first_seen}  |  Last updated: {last_updated}[/dim]")


# ------------------------------------------------------------------
# categories
# ------------------------------------------------------------------


@app.command()
def categories() -> None:
    """Show category breakdown with counts."""
    client = _get_client()

    with Progress(
        SpinnerColumn(),
        TextColumn("[progress.description]{task.description}"),
        console=console,
        transient=True,
    ) as progress:
        progress.add_task("Loading categories...", total=None)
        cats = client.get_categories()

    if not cats:
        console.print("[yellow]No categories found[/yellow]")
        return

    total = sum(c["count"] for c in cats)
    max_count = max(c["count"] for c in cats)

    table = Table(title=f"Attack Categories ({total} total)")
    table.add_column("Category", style="cyan")
    table.add_column("Count", justify="right", style="green")
    table.add_column("", min_width=25)

    for cat in cats:
        bar_len = int((cat["count"] / max_count) * 20) if max_count else 0
        bar = "[green]" + "\u2588" * bar_len + "[/green]"
        table.add_row(cat["category"], str(cat["count"]), bar)

    console.print(table)


# ------------------------------------------------------------------
# stats
# ------------------------------------------------------------------


@app.command()
def stats() -> None:
    """Show database statistics."""
    client = _get_client()

    with Progress(
        SpinnerColumn(),
        TextColumn("[progress.description]{task.description}"),
        console=console,
        transient=True,
    ) as progress:
        progress.add_task("Loading statistics...", total=None)
        data = client.get_stats()

    console.print(Panel(
        f"[bold]Total Attacks:[/bold] [cyan]{data['total']}[/cyan]\n"
        f"[bold]Avg Confidence:[/bold] [cyan]{data['avg_confidence']:.3f}[/cyan]",
        title="Database Statistics",
    ))

    # Category breakdown
    if data["categories"]:
        cat_table = Table(title="Categories")
        cat_table.add_column("Category", style="cyan")
        cat_table.add_column("Count", justify="right", style="green")

        for cat, cnt in data["categories"].items():
            cat_table.add_row(cat, str(cnt))
        console.print(cat_table)

    # CVSS distribution
    if data["cvss_distribution"]:
        cvss_table = Table(title="CVSS Distribution")
        cvss_table.add_column("Range", style="cyan")
        cvss_table.add_column("Count", justify="right", style="green")
        cvss_table.add_column("", min_width=25)

        max_c = max(data["cvss_distribution"].values()) if data["cvss_distribution"] else 1
        for bucket in sorted(data["cvss_distribution"]):
            cnt = data["cvss_distribution"][bucket]
            bar_len = int((cnt / max_c) * 20)
            bar = "[green]" + "\u2588" * bar_len + "[/green]"
            cvss_table.add_row(bucket, str(cnt), bar)
        console.print(cvss_table)

    # Modality breakdown
    if data["modalities"]:
        mod_table = Table(title="Modalities")
        mod_table.add_column("Modality", style="blue")
        mod_table.add_column("Count", justify="right", style="green")

        for mod, cnt in data["modalities"].items():
            mod_table.add_row(mod, str(cnt))
        console.print(mod_table)


# ------------------------------------------------------------------
# Entry point
# ------------------------------------------------------------------

if __name__ == "__main__":
    app()
