"""CLI interface for Clore.ai SDK."""

import json
import os
import subprocess
import sys
from pathlib import Path
from typing import Optional

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

from clore_ai import CloreAI
from clore_ai.exceptions import CloreAPIError

console = Console()
CONFIG_DIR = Path.home() / ".clore"
CONFIG_FILE = CONFIG_DIR / "config.json"


def load_config() -> dict:
    """Load configuration from ~/.clore/config.json."""
    if not CONFIG_FILE.exists():
        return {}
    try:
        with open(CONFIG_FILE, "r") as f:
            return json.load(f)
    except Exception:
        return {}


def save_config(config: dict) -> None:
    """Save configuration to ~/.clore/config.json."""
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)
    with open(CONFIG_FILE, "w") as f:
        json.dump(config, f, indent=2)


def get_api_key() -> Optional[str]:
    """Get API key from environment or config."""
    return os.getenv("CLORE_API_KEY") or load_config().get("api_key")


def get_client() -> CloreAI:
    """Get configured Clore.ai client."""
    api_key = get_api_key()
    return CloreAI(api_key=api_key)


@click.group()
@click.version_option(version="0.1.0", prog_name="clore")
def cli():
    """Clore.ai GPU marketplace CLI."""
    pass


@cli.group()
def config():
    """Manage configuration."""
    pass


@config.command("set")
@click.argument("key")
@click.argument("value")
def config_set(key: str, value: str):
    """Set configuration value."""
    cfg = load_config()
    cfg[key] = value
    save_config(cfg)
    console.print(f"[green]✓[/green] Set {key} = {value}")


@config.command("get")
@click.argument("key")
def config_get(key: str):
    """Get configuration value."""
    cfg = load_config()
    value = cfg.get(key)
    if value:
        console.print(f"{key} = {value}")
    else:
        console.print(f"[yellow]![/yellow] {key} not set")


@config.command("show")
def config_show():
    """Show all configuration."""
    cfg = load_config()
    if cfg:
        console.print_json(data=cfg)
    else:
        console.print("[yellow]No configuration found[/yellow]")


@cli.command()
def wallets():
    """Show wallet balances."""
    try:
        client = get_client()
        wallets_list = client.wallets()

        table = Table(title="💰 Wallet Balances")
        table.add_column("Currency", style="cyan")
        table.add_column("Balance", style="green")
        table.add_column("Deposit Address", style="dim")

        for wallet in wallets_list:
            balance_str = f"{wallet.balance:.8f}" if wallet.balance is not None else "N/A"
            table.add_row(
                wallet.name, balance_str, wallet.deposit or "N/A"
            )

        console.print(table)

    except CloreAPIError as e:
        console.print(f"[red]✗[/red] {e}")
        sys.exit(1)


@cli.command()
@click.option("--gpu", help="Filter by GPU model (e.g., 'RTX 4090')")
@click.option("--min-gpu", type=int, help="Minimum GPU count")
@click.option("--min-ram", type=float, help="Minimum RAM in GB")
@click.option("--max-price", type=float, help="Maximum price in USD/hour")
@click.option("--sort", type=click.Choice(["price", "gpu", "ram"]), default="price")
@click.option("--limit", type=int, default=20, help="Limit results")
def search(
    gpu: Optional[str],
    min_gpu: Optional[int],
    min_ram: Optional[float],
    max_price: Optional[float],
    sort: str,
    limit: int,
):
    """Search GPU marketplace."""
    try:
        client = get_client()
        servers = client.marketplace(
            gpu=gpu, min_gpu_count=min_gpu, min_ram_gb=min_ram, max_price_usd=max_price
        )

        # Sort using convenience properties
        if sort == "price":
            servers = sorted(servers, key=lambda s: s.price_usd or float("inf"))
        elif sort == "gpu":
            servers = sorted(servers, key=lambda s: s.gpu_count, reverse=True)
        elif sort == "ram":
            servers = sorted(servers, key=lambda s: s.ram_gb or 0, reverse=True)

        servers = servers[:limit]

        table = Table(title="🔍 GPU Marketplace")
        table.add_column("ID", style="cyan")
        table.add_column("GPU", style="yellow")
        table.add_column("Count", justify="right")
        table.add_column("RAM (GB)", justify="right")
        table.add_column("CPU", style="dim", max_width=30)
        table.add_column("Price/h (USD)", justify="right", style="green")
        table.add_column("Location", style="dim")

        for server in servers:
            cpu_str = server.specs.cpu[:30] if server.specs and server.specs.cpu else "N/A"
            table.add_row(
                str(server.id),
                server.gpu_model or "Unknown",
                str(server.gpu_count),
                f"{server.ram_gb or 0:.1f}",
                cpu_str,
                f"${server.price_usd or 0:.4f}",
                server.location or "N/A",
            )

        console.print(table)
        console.print(f"\n[dim]Showing {len(servers)} servers[/dim]")

    except CloreAPIError as e:
        console.print(f"[red]✗[/red] {e}")
        sys.exit(1)


@cli.command()
def servers():
    """List your hosted servers."""
    try:
        client = get_client()
        servers_list = client.my_servers()

        table = Table(title="🖥️  My Servers")
        table.add_column("ID", style="cyan")
        table.add_column("Name")
        table.add_column("GPU", style="yellow")
        table.add_column("GPUs", justify="right")
        table.add_column("Status", style="green")

        for server in servers_list:
            table.add_row(
                str(server.id),
                server.name or "Unnamed",
                server.gpu_model or "Unknown",
                str(server.gpu_count or 0),
                server.status,
            )

        console.print(table)

    except CloreAPIError as e:
        console.print(f"[red]✗[/red] {e}")
        sys.exit(1)


@cli.command("server-config")
@click.argument("name")
def server_config(name: str):
    """Get server configuration."""
    try:
        client = get_client()
        cfg = client.server_config(name)

        console.print(f"\n[bold]Server:[/bold] {cfg.name}")
        console.print(f"[bold]ID:[/bold] {cfg.id}")
        console.print(f"[bold]Connected:[/bold] {cfg.connected}")
        console.print(f"[bold]Online:[/bold] {cfg.online}")
        console.print(f"[bold]Visibility:[/bold] {cfg.visibility}")
        if cfg.mrl:
            console.print(f"[bold]Min Rental Length:[/bold] {cfg.mrl}")
        if cfg.gpu_array:
            console.print(f"[bold]GPUs:[/bold] {', '.join(cfg.gpu_array)}")
        if cfg.on_demand_price is not None:
            console.print(f"[bold]On-Demand Price (USD):[/bold] ${cfg.on_demand_price}")
        if cfg.spot_price is not None:
            console.print(f"[bold]Spot Price (USD):[/bold] ${cfg.spot_price}")
        if cfg.specs:
            console.print(f"[bold]CPU:[/bold] {cfg.specs.cpu}")
            console.print(f"[bold]RAM:[/bold] {cfg.specs.ram} GB")
            console.print(f"[bold]GPU:[/bold] {cfg.specs.gpu}")

    except CloreAPIError as e:
        console.print(f"[red]✗[/red] {e}")
        sys.exit(1)


@cli.command()
@click.option("--completed", is_flag=True, help="Include completed orders")
def orders(completed: bool):
    """List your orders."""
    try:
        client = get_client()
        orders_list = client.my_orders(include_completed=completed)

        table = Table(title="📦 My Orders")
        table.add_column("ID", style="cyan")
        table.add_column("Server ID")
        table.add_column("Type")
        table.add_column("Status", style="yellow")
        table.add_column("Image", style="dim")
        table.add_column("IP", style="green")

        for order in orders_list:
            table.add_row(
                str(order.id),
                str(order.server_id or "N/A"),
                order.type or "N/A",
                order.status or "Active",
                order.image or "N/A",
                order.pub_cluster or "N/A",
            )

        console.print(table)
        console.print(f"\n[dim]Total: {len(orders_list)} orders[/dim]")

    except CloreAPIError as e:
        console.print(f"[red]✗[/red] {e}")
        sys.exit(1)


@cli.command()
@click.argument("server_id", type=int)
@click.option("--image", required=True, help="Docker image")
@click.option(
    "--type", "order_type", type=click.Choice(["on-demand", "spot"]), required=True
)
@click.option("--currency", required=True, help="Payment currency (e.g., bitcoin)")
@click.option("--ssh-password", help="SSH password")
@click.option("--ssh-key", help="SSH public key")
@click.option("--port", multiple=True, help="Port mapping (e.g., 22:tcp)")
@click.option("--env", multiple=True, help="Environment variable (KEY=VALUE)")
@click.option("--spot-price", type=float, help="Spot price")
def deploy(
    server_id: int,
    image: str,
    order_type: str,
    currency: str,
    ssh_password: Optional[str],
    ssh_key: Optional[str],
    port: tuple,
    env: tuple,
    spot_price: Optional[float],
):
    """Create a new order."""
    try:
        # Parse ports
        ports_dict = {}
        for p in port:
            if ":" in p:
                port_num, protocol = p.split(":", 1)
                ports_dict[port_num] = protocol

        # Parse env vars
        env_dict = {}
        for e in env:
            if "=" in e:
                key, value = e.split("=", 1)
                env_dict[key] = value

        client = get_client()
        order = client.create_order(
            server_id=server_id,
            image=image,
            type=order_type,
            currency=currency,
            ssh_password=ssh_password,
            ssh_key=ssh_key,
            ports=ports_dict or None,
            env=env_dict or None,
            spot_price=spot_price,
        )

        console.print(f"[green]✓[/green] Order created!")
        console.print(f"[bold]Order ID:[/bold] {order.id}")
        if order.pub_cluster:
            console.print(f"[bold]IP:[/bold] {order.pub_cluster}")
        if order.tcp_ports:
            console.print(f"[bold]Ports:[/bold] {order.tcp_ports}")

    except CloreAPIError as e:
        console.print(f"[red]✗[/red] {e}")
        sys.exit(1)


@cli.command()
@click.argument("order_id", type=int)
@click.option("--issue", help="Cancellation reason")
def cancel(order_id: int, issue: Optional[str]):
    """Cancel an order."""
    try:
        client = get_client()
        result = client.cancel_order(order_id, issue=issue)

        console.print(f"[green]✓[/green] Order {order_id} cancelled")
        if result.get("message"):
            console.print(f"[dim]{result['message']}[/dim]")

    except CloreAPIError as e:
        console.print(f"[red]✗[/red] {e}")
        sys.exit(1)


@cli.command()
@click.argument("server_id", type=int)
def spot(server_id: int):
    """Show spot market for a server."""
    try:
        client = get_client()
        market = client.spot_marketplace(server_id)

        table = Table(title=f"📊 Spot Market - Server {server_id}")
        table.add_column("Order ID", style="cyan")
        table.add_column("Price", style="green")

        if market.offers:
            for offer in market.offers:
                table.add_row(
                    str(offer.order_id or "N/A"),
                    str(offer.price or "N/A"),
                )
        else:
            console.print("[dim]No active spot offers[/dim]")

        console.print(table)

        if market.server:
            console.print(f"\n[bold]Server Online:[/bold] {market.server.online}")
            console.print(f"[bold]Visibility:[/bold] {market.server.visibility}")

    except CloreAPIError as e:
        console.print(f"[red]✗[/red] {e}")
        sys.exit(1)


@cli.command("spot-price")
@click.argument("order_id", type=int)
@click.argument("price", type=float)
def spot_price(order_id: int, price: float):
    """Set spot price for an order."""
    try:
        client = get_client()
        result = client.set_spot_price(order_id, price)

        console.print(f"[green]✓[/green] Spot price updated to {price}")
        if result.get("message"):
            console.print(f"[dim]{result['message']}[/dim]")

    except CloreAPIError as e:
        console.print(f"[red]✗[/red] {e}")
        sys.exit(1)


@cli.command()
@click.argument("order_id", type=int)
@click.option("--user", default="root", help="SSH user")
def ssh(order_id: int, user: str):
    """SSH into an order."""
    try:
        client = get_client()
        orders_list = client.my_orders(include_completed=False)

        order = next((o for o in orders_list if o.id == order_id), None)
        if not order:
            console.print(f"[red]✗[/red] Order {order_id} not found")
            sys.exit(1)

        if not order.pub_cluster:
            console.print(f"[red]✗[/red] Order has no public IP yet")
            sys.exit(1)

        # Find SSH port
        ssh_port = 22
        if order.tcp_ports:
            ssh_port = order.tcp_ports.get("22", 22)

        host = order.pub_cluster
        console.print(f"[cyan]→[/cyan] Connecting to {user}@{host}:{ssh_port}")

        # Execute SSH
        cmd = ["ssh", f"{user}@{host}", "-p", str(ssh_port)]
        subprocess.run(cmd)

    except CloreAPIError as e:
        console.print(f"[red]✗[/red] {e}")
        sys.exit(1)
    except KeyboardInterrupt:
        console.print("\n[yellow]Connection closed[/yellow]")


def main():
    """Entry point for CLI."""
    try:
        cli()
    except KeyboardInterrupt:
        console.print("\n[yellow]Aborted[/yellow]")
        sys.exit(130)


if __name__ == "__main__":
    main()
