"""CLI commands for brokr agent and grant management."""

import re

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

from brokr.project import resolve_project
from brokr.store import BrokrError, get_store_dir, load_store, save_store

agent_app = typer.Typer(help="Manage agents and their secret grants.")
console = Console(stderr=True)

_AGENT_NAME_PATTERN = re.compile(r"^[a-z][a-z0-9-]*$")


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 _validate_agent_name(name: str) -> str:
    if not _AGENT_NAME_PATTERN.match(name):
        raise typer.BadParameter(
            "Agent name must start with a lowercase letter and contain only "
            "lowercase letters, digits, and hyphens."
        )
    return name


@agent_app.command("add")
def agent_add(
    name: str = typer.Argument(..., callback=_validate_agent_name),
) -> None:
    """Create an agent in 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)

    agents = project_data.get("agents", {})
    if name in agents:
        console.print(
            f"[red]Agent '{name}' already exists in project '{project_name}'.[/red]"
        )
        raise typer.Exit(code=1)

    new_agents = {**agents, name: []}
    new_project_data = {**project_data, "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]Agent '{name}' added to project '{project_name}'.[/green]"
    )


@agent_app.command("rm")
def agent_rm(
    name: str = typer.Argument(..., callback=_validate_agent_name),
) -> None:
    """Remove an agent and all its 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)

    agents = project_data.get("agents", {})
    if name not in agents:
        console.print(
            f"[red]Agent '{name}' not found in project '{project_name}'.[/red]"
        )
        raise typer.Exit(code=1)

    new_agents = {k: v for k, v in agents.items() if k != name}
    new_project_data = {**project_data, "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]Agent '{name}' removed from project '{project_name}'.[/green]"
    )


@agent_app.command("list")
def agent_list() -> None:
    """List agents in 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)

    agents = project_data.get("agents", {})
    if not agents:
        console.print(f"No agents in project '{project_name}'.")
        return

    table = Table(title=f"Agents: {project_name}")
    table.add_column("Agent")
    table.add_column("Grants")
    for name in sorted(agents):
        grants = agents[name]
        table.add_row(name, str(len(grants)))
    console.print(table)


# --- Grant / Revoke (standalone commands, registered on main app) ---


def grant_cmd(
    agent: str = typer.Argument(..., help="Agent name"),
    secret: str = typer.Argument(..., callback=_validate_key_name, help="Secret key"),
) -> None:
    """Grant a secret to an agent in 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)

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

    agents = project_data.get("agents", {})
    if agent not in agents:
        console.print(
            f"[red]Agent '{agent}' not found in project '{project_name}'. "
            f"Run `brokr agent add {agent}` first.[/red]"
        )
        raise typer.Exit(code=1)

    current_grants = agents[agent]
    if secret in current_grants:
        console.print(
            f"[yellow]Secret '{secret}' already granted to agent '{agent}'.[/yellow]"
        )
        return

    new_grants = [*current_grants, secret]
    new_agents = {**agents, agent: new_grants}
    new_project_data = {**project_data, "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]Granted '{secret}' to agent '{agent}'.[/green]"
    )


def revoke_cmd(
    agent: str = typer.Argument(..., help="Agent name"),
    secret: str = typer.Argument(..., callback=_validate_key_name, help="Secret key"),
) -> None:
    """Revoke a secret from an agent in 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)

    agents = project_data.get("agents", {})
    if agent not in agents:
        console.print(
            f"[red]Agent '{agent}' not found in project '{project_name}'.[/red]"
        )
        raise typer.Exit(code=1)

    current_grants = agents[agent]
    if secret not in current_grants:
        console.print(
            f"[red]Secret '{secret}' not granted to agent '{agent}'.[/red]"
        )
        raise typer.Exit(code=1)

    new_grants = [g for g in current_grants if g != secret]
    new_agents = {**agents, agent: new_grants}
    new_project_data = {**project_data, "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]Revoked '{secret}' from agent '{agent}'.[/green]"
    )
