"""
iq site commands - Manage site configurations.
"""

from __future__ import annotations

import asyncio
import os
import sys
from pathlib import Path
from typing import TYPE_CHECKING

import click
from rich.table import Table

from iq_sdk.cli import (
    console,
    load_config_file,
    save_config_file,
)

if TYPE_CHECKING:
    pass

from iq_sdk.config import (
    load_settings,
    save_env_template,
)


@click.group(context_settings={"help_option_names": ["-h", "--help"]})
def site() -> None:
    """Manage sites."""
    pass


@site.command("add")
@click.argument("site_id")
@click.option("--host", "-H", required=True, help="Socket.IO server URL")
@click.option("--api-key", "-k", default="", help="API key for authentication")
@click.option(
    "--config", "-c", "config_file", default="config/settings.yml", help="Config file to modify"
)
@click.pass_context
def site_add(
    ctx: click.Context,
    site_id: str,
    host: str,
    api_key: str,
    config_file: str,
) -> None:
    """Add a new site configuration."""
    config_path = Path(config_file)
    config_data = load_config_file(config_path)

    # Initialize sites list if not present
    if "sites" not in config_data:
        config_data["sites"] = []

    # Check if site already exists
    existing_ids = [s.get("id") for s in config_data.get("sites", [])]
    if config_data.get("site", {}).get("id") == site_id:
        console.print(
            f"[yellow]Site '{site_id}' is the default site. Use 'iq site update' to modify.[/yellow]"
        )
        return

    if site_id in existing_ids:
        if not click.confirm(f"Site '{site_id}' already exists. Overwrite?"):
            raise click.Abort()
        config_data["sites"] = [s for s in config_data["sites"] if s.get("id") != site_id]

    # Add new site
    new_site = {
        "id": site_id,
        "host": host,
        "api_key": api_key,
    }
    config_data["sites"].append(new_site)

    # Save config
    save_config_file(config_path, config_data)

    console.print(f"[bold green]✓ Site '{site_id}' added to {config_path}[/bold green]")

    # Remind about API secret
    site_id_upper = site_id.upper().replace("-", "_")
    console.print("\n[yellow]Don't forget to set the API secret:[/yellow]")
    console.print(f"  export IQ_SITE_{site_id_upper}_API_SECRET='your-secret'")
    console.print("  # or add to .env file:")
    console.print(f"  IQ_SITE_{site_id_upper}_API_SECRET=your-secret")


@site.command("ls")
@click.argument("site_id", required=False)
@click.option("--remote", "-r", is_flag=True, help="Check remote connectivity and sync status")
@click.pass_context
def site_ls(ctx: click.Context, site_id: str | None, remote: bool) -> None:
    """List site configuration(s)."""
    from iq_sdk.cli import require_project

    # Require valid project directory
    require_project(ctx)

    config_file = ctx.obj.get("config_file")
    settings = load_settings(config_file)

    if site_id:
        # Show specific site
        site_config = settings.get_site(site_id)
        if not site_config:
            console.print(f"[red]Site '{site_id}' not found[/red]")
            sys.exit(1)

        console.print(f"\n[bold]Site: {site_config.id}[/bold]")
        console.print(f"  Host:       {site_config.get_host()}")
        console.print(f"  API Key:    {site_config.get_api_key() or '(not set)'}")

        # Show custom env vars
        env_vars = site_config.get_env()
        if env_vars:
            console.print(f"  Env vars:   {len(env_vars)} defined")
            for k, v in env_vars.items():
                console.print(f"    {k}={v}")

        # Show tools
        tools = site_config.get_tools()
        if tools:
            console.print(f"  Tools:      {len(tools)} configured")
            for t in tools:
                console.print(f"    {t.name} → queue:{t.get_queue()} threads:{t.threads}")

        # Check API secret status
        site_id_upper = site_config.id.upper().replace("-", "_")
        has_env_secret = f"IQ_SITE_{site_id_upper}_API_SECRET" in os.environ
        has_file_secret = f"IQ_SITE_{site_id_upper}_API_SECRET_FILE" in os.environ
        has_config_secret = bool(site_config.api_secret)

        if has_env_secret:
            console.print("  API Secret: [green]✓ Set via ENV[/green]")
        elif has_file_secret:
            console.print("  API Secret: [green]✓ Set via FILE[/green]")
        elif has_config_secret:
            console.print("  API Secret: [yellow]⚠ Set in config (not recommended)[/yellow]")
        else:
            console.print("  API Secret: [red]✗ Not set[/red]")

        if remote:
            _check_site_connectivity(site_config, settings.project_name)
    else:
        # Show all sites
        table = Table(title="Configured Sites")
        table.add_column("ID", style="cyan")
        table.add_column("Host", style="dim")
        table.add_column("API Key")
        table.add_column("Secret")
        table.add_column("Tools")
        table.add_column("Worker ID", style="magenta")

        if remote:
            table.add_column("Reachable")
            table.add_column("Auth")

        all_sites = [settings.site] + settings.sites
        project_name = settings.project_name

        # Gather check results if needed (includes tool sync status)
        check_results: dict = {}
        if remote:
            check_results = _check_sites_connectivity(all_sites, project_name, settings)

        for s in all_sites:
            site_id_upper = s.id.upper().replace("-", "_")
            has_env_secret = f"IQ_SITE_{site_id_upper}_API_SECRET" in os.environ
            has_file_secret = f"IQ_SITE_{site_id_upper}_API_SECRET_FILE" in os.environ
            has_config_secret = bool(s.api_secret)

            if has_env_secret or has_file_secret:
                secret_status = "[green]✓[/green]"
            elif has_config_secret:
                secret_status = "[yellow]⚠[/yellow]"
            else:
                secret_status = "[red]✗[/red]"

            is_default = " (default)" if s.id == settings.site.id else ""

            # Tools status - show sync status when checking, otherwise local count
            if remote:
                result = check_results.get(s.id, {})
                tools_str = _format_tool_status(result.get("tools"))
            else:
                tools = s.get_tools()
                tools_str = str(len(tools)) if tools else "-"

            # Worker ID from settings
            worker_id = settings.get_worker_id()

            row = [
                f"{s.id}{is_default}",
                s.get_host(),
                s.get_api_key() or "-",
                secret_status,
                tools_str,
                worker_id,
            ]

            if remote:
                result = check_results.get(s.id, {})
                reachable = "[green]✓[/green]" if result.get("reachable") else "[red]✗[/red]"
                auth = "[green]✓[/green]" if result.get("auth") else "[red]✗[/red]"
                if result.get("error"):
                    reachable = f"[red]✗[/red] {result['error'][:20]}"
                row.extend([reachable, auth])

            table.add_row(*row)

        console.print(table)


def _check_site_connectivity(site_config, project_name: str | None = None) -> None:
    """Check connectivity for a single site."""
    from iq_sdk.client import IQApiClient

    console.print("\n[bold]Connectivity Check:[/bold]")

    async def _check():
        async with IQApiClient.from_site(site_config) as client:
            status = await client.check_status(namespace=project_name)

            if status.reachable:
                latency = f" ({status.latency_ms}ms)" if status.latency_ms else ""
                console.print(f"  Reachable: [green]✓{latency}[/green]")
            else:
                console.print(f"  Reachable: [red]✗ {status.error}[/red]")
                return

            if status.authenticated:
                console.print("  Auth:      [green]✓[/green]")
                console.print(f"    Can read:             {'✓' if status.can_read else '✗'}")
                console.print(f"    Can create:           {'✓' if status.can_create else '✗'}")
                console.print(f"    Can update:           {'✓' if status.can_update else '✗'}")
                console.print(f"    Can delete:           {'✓' if status.can_delete else '✗'}")
                console.print(
                    f"    Can register worker:  {'✓' if status.can_register_worker else '✗'}"
                )
            else:
                console.print(f"  Auth:      [red]✗ {status.error}[/red]")

    try:
        asyncio.run(_check())
    except Exception as e:
        console.print(f"  [red]Check failed: {e}[/red]")


def _check_sites_connectivity(sites: list, project_name: str, settings=None) -> dict:
    """Check connectivity and tool sync status for multiple sites."""
    from iq_sdk.cli.tool import get_local_tools, get_remote_tools
    from iq_sdk.client import IQApiClient
    from iq_sdk.config import get_project_root

    results: dict = {}

    # Get local tools once (use settings path or project root)
    if settings:
        tools_path = settings.get_tools_path()
    else:
        tools_path = get_project_root() / "tools"
    local_tools = get_local_tools(tools_path) if tools_path.exists() else {}

    # Build local lookup: {tool_name:version -> digest}
    local_lookup: dict[str, str] = {}
    for base_name, tool_versions in local_tools.items():
        for info in tool_versions:
            key = f"{base_name}:{info.version}"
            local_lookup[key] = info.digest

    async def _check_all():
        for site in sites:
            site_result: dict = {
                "reachable": False,
                "auth": False,
                "error": None,
                "tools": None,
            }

            try:
                async with IQApiClient.from_site(site) as client:
                    status = await client.check_status(namespace=project_name)

                    site_result["reachable"] = status.reachable
                    site_result["auth"] = status.authenticated
                    site_result["error"] = status.error

                    # If authenticated, get tool sync status
                    if status.authenticated and status.can_read:
                        try:
                            _, remote_tools = await get_remote_tools(site, project_name)
                            site_result["tools"] = _aggregate_tool_status(
                                local_lookup, remote_tools
                            )
                        except Exception:
                            site_result["tools"] = None

            except Exception as e:
                site_result["error"] = str(e)[:30]

            results[site.id] = site_result

    try:
        asyncio.run(_check_all())
    except Exception:
        pass

    return results


def _aggregate_tool_status(local_lookup: dict[str, str], remote_tools: list) -> dict[str, int]:
    """Aggregate tool sync status between local and remote.

    Returns dict with counts: sync, new, update, deleted
    """
    stats = {"sync": 0, "new": 0, "update": 0, "deleted": 0}

    # Copy local lookup to track which are processed
    remaining_local = set(local_lookup.keys())

    for rt in remote_tools:
        key = f"{rt.tool_name}:{rt.version}"

        if key in local_lookup:
            remaining_local.discard(key)
            if local_lookup[key] == rt.digest:
                stats["sync"] += 1
            else:
                stats["update"] += 1
        else:
            # Remote only = deleted locally
            stats["deleted"] += 1

    # Remaining local = new (not on remote)
    stats["new"] = len(remaining_local)

    return stats


def _format_tool_status(stats: dict[str, int] | None) -> str:
    """Format tool status for table display."""
    if stats is None:
        return "-"

    total = sum(stats.values())
    if total == 0:
        return "[dim]0[/dim]"

    parts = []
    if stats["sync"]:
        parts.append(f"[green]{stats['sync']}✓[/green]")
    if stats["new"]:
        parts.append(f"[blue]{stats['new']}+[/blue]")
    if stats["update"]:
        parts.append(f"[yellow]{stats['update']}~[/yellow]")
    if stats["deleted"]:
        parts.append(f"[red]{stats['deleted']}-[/red]")

    return " ".join(parts) if parts else "[dim]0[/dim]"


@site.command("rm")
@click.argument("site_id")
@click.option(
    "--config", "-c", "config_file", default="config/settings.yml", help="Config file to modify"
)
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation")
def site_rm(site_id: str, config_file: str, yes: bool) -> None:
    """Remove a site configuration."""
    config_path = Path(config_file)
    config_data = load_config_file(config_path)

    # Check if it's the default site
    if config_data.get("site", {}).get("id") == site_id:
        console.print(f"[red]Cannot drop default site '{site_id}'[/red]")
        sys.exit(1)

    # Find and remove site
    sites = config_data.get("sites", [])
    original_count = len(sites)
    config_data["sites"] = [s for s in sites if s.get("id") != site_id]

    if len(config_data["sites"]) == original_count:
        console.print(f"[yellow]Site '{site_id}' not found[/yellow]")
        return

    if not yes and not click.confirm(f"Remove site '{site_id}'?"):
        raise click.Abort()

    save_config_file(config_path, config_data)
    console.print(f"[bold green]✓ Site '{site_id}' removed from {config_path}[/bold green]")


@site.command("env-template")
@click.option("--output", "-o", default=".env.template", help="Output file")
@click.pass_context
def site_env_template(ctx: click.Context, output: str) -> None:
    """Generate .env template with site password entries."""
    config_file = ctx.obj.get("config_file")
    settings = load_settings(config_file)

    output_path = save_env_template(settings, output)
    console.print(f"[bold green]✓ Environment template created: {output_path}[/bold green]")
