"""CLI commands for brokr project management."""

from pathlib import Path

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

from brokr.project import (
    PROJECT_FILE_NAME,
    find_project_file,
    read_project_name,
    resolve_project,
)
from brokr.store import BrokrError, get_store_dir, load_store, save_store

project_app = typer.Typer(help="Manage project secret associations.")
console = Console(stderr=True)


def _get_passphrase() -> str:
    from brokr.cli import get_passphrase

    return get_passphrase()


def _validate_key_name(name: str) -> str:
    from brokr.cli import validate_key_name

    return validate_key_name(name)


def _mask_value(value: str) -> str:
    from brokr.cli import mask_value

    return mask_value(value)


@project_app.command("init")
def project_init(
    name: str = typer.Argument(..., help="Project name"),
    force: bool = typer.Option(False, "--force", help="Overwrite existing project"),
) -> None:
    """Register a project and create .brokr file in current directory."""
    store_dir = get_store_dir()
    store_file = store_dir / "store.enc"

    if not store_file.exists():
        console.print("[red]No store found. Run `brokr init` first.[/red]")
        raise typer.Exit(code=1)

    passphrase = _get_passphrase()

    try:
        data = load_store(passphrase, store_dir=store_dir)
    except BrokrError as exc:
        console.print(f"[red]{exc}[/red]")
        raise typer.Exit(code=1)

    projects = data.get("projects", {})

    if name in projects and not force:
        console.print(
            f"[red]Project '{name}' already exists. Use --force to overwrite.[/red]"
        )
        raise typer.Exit(code=1)

    new_projects = {**projects, name: {"secrets": [], "agents": {}}}
    new_data = {**data, "projects": new_projects}
    save_store(new_data, passphrase, store_dir=store_dir)

    project_file = Path.cwd() / PROJECT_FILE_NAME
    project_file.write_text(f"{name}\n")

    console.print(f"[green]Project '{name}' created.[/green]")
    console.print(f"[yellow]Reminder: add {PROJECT_FILE_NAME} to your .gitignore[/yellow]")


@project_app.command("add")
def project_add(
    secret: str = typer.Argument(..., callback=_validate_key_name),
) -> None:
    """Link a secret to the current project."""
    store_dir = get_store_dir()
    passphrase = _get_passphrase()

    try:
        data = load_store(passphrase, store_dir=store_dir)
        project_name, project_data = resolve_project(data)
    except BrokrError as exc:
        console.print(f"[red]{exc}[/red]")
        raise typer.Exit(code=1)

    all_secrets = data.get("secrets", {})
    if secret not in all_secrets:
        console.print(
            f"[red]Secret '{secret}' not found in store. "
            f"Run `brokr set {secret}` first.[/red]"
        )
        raise typer.Exit(code=1)

    current_secrets = project_data.get("secrets", [])
    if secret in current_secrets:
        console.print(f"[yellow]Secret '{secret}' already linked to project.[/yellow]")
        return

    new_project_data = {**project_data, "secrets": [*current_secrets, secret]}
    new_projects = {**data.get("projects", {}), project_name: new_project_data}
    new_data = {**data, "projects": new_projects}
    save_store(new_data, passphrase, store_dir=store_dir)
    console.print(f"[green]Secret '{secret}' linked to project '{project_name}'.[/green]")


@project_app.command("rm")
def project_rm(
    secret: str = typer.Argument(..., callback=_validate_key_name),
) -> None:
    """Unlink a secret from the current project. Cascades to agent grants."""
    store_dir = get_store_dir()
    passphrase = _get_passphrase()

    try:
        data = load_store(passphrase, store_dir=store_dir)
        project_name, project_data = resolve_project(data)
    except BrokrError as exc:
        console.print(f"[red]{exc}[/red]")
        raise typer.Exit(code=1)

    current_secrets = project_data.get("secrets", [])
    if secret not in current_secrets:
        console.print(
            f"[red]Secret '{secret}' not linked to project '{project_name}'.[/red]"
        )
        raise typer.Exit(code=1)

    new_secrets = [s for s in current_secrets if s != secret]

    # Cascade: remove from all agent grants
    old_agents = project_data.get("agents", {})
    cascaded = []
    new_agents = {}
    for agent_name, grants in old_agents.items():
        if secret in grants:
            cascaded.append(agent_name)
            new_agents[agent_name] = [g for g in grants if g != secret]
        else:
            new_agents[agent_name] = grants

    new_project_data = {**project_data, "secrets": new_secrets, "agents": new_agents}
    new_projects = {**data.get("projects", {}), project_name: new_project_data}
    new_data = {**data, "projects": new_projects}
    save_store(new_data, passphrase, store_dir=store_dir)

    console.print(
        f"[green]Secret '{secret}' removed from project '{project_name}'.[/green]"
    )
    if cascaded:
        console.print(
            f"[yellow]Revoked from agents: {', '.join(sorted(cascaded))}[/yellow]"
        )


@project_app.command("list")
def project_list() -> None:
    """List all projects, or secrets in current project."""
    store_dir = get_store_dir()
    passphrase = _get_passphrase()

    try:
        data = load_store(passphrase, store_dir=store_dir)
    except BrokrError as exc:
        console.print(f"[red]{exc}[/red]")
        raise typer.Exit(code=1)

    projects = data.get("projects", {})

    # Try to detect project context
    project_file = find_project_file()
    if project_file is not None:
        try:
            project_name = read_project_name(project_file)
            if project_name in projects:
                project_data = projects[project_name]
                secret_names = project_data.get("secrets", [])
                all_secrets = data.get("secrets", {})

                if not secret_names:
                    console.print(f"Project '{project_name}': no secrets linked.")
                    return

                table = Table(title=f"Project: {project_name}")
                table.add_column("Key")
                table.add_column("Value")
                for name in sorted(secret_names):
                    val = all_secrets.get(name)
                    if val is not None:
                        table.add_row(name, _mask_value(val))
                    else:
                        table.add_row(name, "[red](deleted)[/red]")
                console.print(table)
                return
        except (ValueError, BrokrError) as exc:
            console.print(
                f"[dim yellow]Note: could not read project context ({exc})[/dim yellow]"
            )

    # No project context — list all projects
    if not projects:
        console.print("No projects registered.")
        return

    table = Table()
    table.add_column("Project")
    table.add_column("Secrets")
    table.add_column("Agents")
    for name in sorted(projects):
        p = projects[name]
        table.add_row(
            name,
            str(len(p.get("secrets", []))),
            str(len(p.get("agents", {}))),
        )
    console.print(table)


@project_app.command("show")
def project_show() -> None:
    """Show current project details."""
    store_dir = get_store_dir()
    passphrase = _get_passphrase()

    try:
        data = load_store(passphrase, store_dir=store_dir)
        project_name, project_data = resolve_project(data)
    except BrokrError as exc:
        console.print(f"[red]{exc}[/red]")
        raise typer.Exit(code=1)

    all_secrets = data.get("secrets", {})
    secret_names = project_data.get("secrets", [])
    agents = project_data.get("agents", {})

    console.print(f"[bold]Project: {project_name}[/bold]\n")

    # Secrets table
    if secret_names:
        sec_table = Table(title="Secrets")
        sec_table.add_column("Key")
        sec_table.add_column("Value")
        for name in sorted(secret_names):
            val = all_secrets.get(name)
            if val is not None:
                sec_table.add_row(name, _mask_value(val))
            else:
                sec_table.add_row(name, "[red](deleted)[/red]")
        console.print(sec_table)
    else:
        console.print("No secrets linked.\n")

    # Agents table
    if agents:
        agent_table = Table(title="Agents")
        agent_table.add_column("Agent")
        agent_table.add_column("Grants")
        for name in sorted(agents):
            grants = agents[name]
            agent_table.add_row(name, ", ".join(sorted(grants)) if grants else "(none)")
        console.print(agent_table)
    else:
        console.print("No agents registered.")
