import argparse
import json
import shutil
import sys
from dataclasses import dataclass, field

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

from nebula_cert_manager.exceptions import CertManagerError, RegistryNotFoundError
from nebula_cert_manager.models import ClientInfo
from nebula_cert_manager.nebula_config import load_nebula_config
from nebula_cert_manager.registry import RegistryManager


@dataclass
class CertListEntry:
    name: str
    client: ClientInfo
    ip: str | None = None
    groups: list[str] = field(default_factory=list)


def add_parser(subparsers: argparse._SubParsersAction) -> None:
    parser = subparsers.add_parser("list", help="List certificates")
    parser.add_argument(
        "--all",
        action="store_true",
        dest="show_all",
        help="Show all (active + revoked)",
    )
    parser.add_argument("--revoked", action="store_true", help="Show only revoked")
    parser.add_argument(
        "--json", action="store_true", dest="output_json", help="Output as JSON"
    )
    parser.set_defaults(func=run)


def list_certs(
    registry_mgr: RegistryManager,
    show_all: bool = False,
    revoked_only: bool = False,
) -> list[CertListEntry]:
    """List certificates with optional filtering, enriched with IP/groups from nebula config.

    Returns filtered entries.
    Raises RegistryNotFoundError.
    """
    if not registry_mgr.exists():
        raise RegistryNotFoundError()

    registry = registry_mgr.load()
    nebula_cfg = load_nebula_config(registry_mgr.registry_dir)

    entries: list[CertListEntry] = []
    for name, certs in registry.clients.items():
        for client in certs:
            if not show_all:
                if revoked_only and not client.revoked:
                    continue
                if not revoked_only and client.revoked:
                    continue

            ip: str | None = None
            groups: list[str] = []
            if nebula_cfg is not None and nebula_cfg.hosts is not None:
                host_cfg = nebula_cfg.hosts.hosts.get(name)
                if host_cfg is not None:
                    ip = host_cfg.nebula_ip
            if nebula_cfg is not None and nebula_cfg.firewall is not None:
                groups = nebula_cfg.firewall.host_groups.get(name, [])

            entries.append(
                CertListEntry(name=name, client=client, ip=ip, groups=groups)
            )

    return entries


def run(args: argparse.Namespace) -> None:
    try:
        entries = list_certs(args.registry_mgr, args.show_all, args.revoked)
    except CertManagerError as e:
        print(f"Error: {e}", file=sys.stderr)
        sys.exit(1)

    if args.output_json:
        data: dict[str, list[dict]] = {}
        for entry in entries:
            data.setdefault(entry.name, []).append(entry.client.model_dump(mode="json"))
        print(json.dumps(data, indent=2))
        return

    if not entries:
        print("No certificates found.")
        return

    table = Table()
    table.add_column("NAME")
    table.add_column("FINGERPRINT")
    table.add_column("IP")
    table.add_column("GROUPS")
    table.add_column("STATUS")
    table.add_column("ISSUED")
    table.add_column("EXPIRES")
    table.add_column("REVOKED")

    for entry in entries:
        status = "REVOKED" if entry.client.revoked else "ACTIVE"
        ip = entry.ip or "N/A"
        groups_str = ",".join(entry.groups) if entry.groups else "N/A"
        issued = entry.client.issued_at.strftime("%Y-%m-%d")
        expires = entry.client.expires_at.strftime("%Y-%m-%d")
        revoked = (
            entry.client.revoked_at.strftime("%Y-%m-%d")
            if entry.client.revoked_at
            else ""
        )
        table.add_row(
            entry.name,
            entry.client.fingerprint,
            ip,
            groups_str,
            status,
            issued,
            expires,
            revoked,
        )

    width = shutil.get_terminal_size(fallback=(200, 24)).columns
    Console(width=width).print(table)
