import os
import subprocess
import time
from pathlib import Path

import typer

from . import config as cfg_module

DEFAULT_RELAYER_URL = "wss://relayer.sixtysix.pro"
DEFAULT_IMAGE = "sixtysix/backend:latest"


def _ensure_ssh_key(config_dir: Path) -> tuple[str, str]:
    """Generate RSA 4096-bit SSH key if it doesn't exist. Returns (private_path, public_key)."""
    from cryptography.hazmat.backends import default_backend
    from cryptography.hazmat.primitives import serialization
    from cryptography.hazmat.primitives.asymmetric import rsa

    key_path = config_dir / "id_rsa"
    pub_path = config_dir / "id_rsa.pub"

    if key_path.exists():
        return str(key_path), pub_path.read_text()

    config_dir.mkdir(mode=0o700, parents=True, exist_ok=True)
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=4096,
        backend=default_backend(),
    )

    pem = private_key.private_bytes(
        encoding=serialization.Encoding.PEM,
        format=serialization.PrivateFormat.TraditionalOpenSSL,
        encryption_algorithm=serialization.NoEncryption(),
    )
    key_path.write_bytes(pem)
    key_path.chmod(0o600)

    pub_bytes = private_key.public_key().public_bytes(
        encoding=serialization.Encoding.OpenSSH,
        format=serialization.PublicFormat.OpenSSH,
    )
    pub_key = pub_bytes.decode() + "\n"
    pub_path.write_text(pub_key)

    return str(key_path), pub_key


def _generate_cloud_init(relayer_url: str = DEFAULT_RELAYER_URL, image: str = DEFAULT_IMAGE) -> str:
    return f"""#cloud-config
packages:
  - docker.io
  - docker-compose-plugin

write_files:
  - path: /opt/sixtysix/docker-compose.yml
    content: |
      services:
        backend:
          image: {image}
          restart: always
          volumes:
            - ./data:/app/__data__
          environment:
            - RELAYER_URL={relayer_url}

runcmd:
  - ufw allow 22/tcp
  - ufw --force enable
  - systemctl enable docker
  - cd /opt/sixtysix && docker compose up -d
"""


def deploy() -> None:
    """Deploy backend to a cloud provider."""
    from hcloud import Client

    typer.echo()
    typer.echo(typer.style("  SixtySix Cloud Deployer", bold=True, fg=typer.colors.MAGENTA))
    typer.echo()

    typer.echo("Cloud Provider:")
    typer.echo("  1. Hetzner - EUR 4.35/mo (Recommended)")
    typer.echo("  2. DigitalOcean (Coming soon)")
    typer.echo("  3. Vultr (Coming soon)")
    choice = typer.prompt("Select provider", default="1")
    if choice != "1":
        typer.echo()
        typer.echo(typer.style("This provider is not yet available.", fg=typer.colors.CYAN))
        typer.echo("Currently only Hetzner is supported.")
        return

    cfg = cfg_module.load()
    if cfg.server_ip:
        typer.echo(typer.style("You already have a deployment:", fg=typer.colors.CYAN))
        typer.echo(f"  Server IP: {cfg.server_ip}")
        typer.echo(f"  Server ID: {cfg.server_id}")
        typer.echo()
        if not typer.confirm("Do you want to create a new deployment? This will NOT destroy the existing server."):
            return

    if cfg.hetzner_token:
        api_token = cfg.hetzner_token
        typer.echo(typer.style("Using saved Hetzner API token", fg=typer.colors.CYAN))
    else:
        typer.echo("Create a Hetzner account at: https://accounts.hetzner.com/signUp")
        typer.echo("Then generate an API token at: Cloud Console → Security → API Tokens")
        typer.echo()
        api_token = typer.prompt("Hetzner API Token", hide_input=True)

    typer.echo(typer.style("Validating token...", fg=typer.colors.CYAN))
    client = Client(token=api_token)
    try:
        client.datacenters.get_all()
        typer.echo(typer.style("✓ Token valid", fg=typer.colors.GREEN))
    except Exception as e:
        typer.echo(typer.style(f"Invalid API token: {e}", fg=typer.colors.RED), err=True)
        raise typer.Exit(1)

    typer.echo()
    locations = client.locations.get_all()
    typer.echo("Server Location:")
    for i, loc in enumerate(locations, 1):
        typer.echo(f"  {i}. {loc.city} - {loc.country}")
    loc_idx = int(typer.prompt("Select location", default="1")) - 1
    selected_location = locations[loc_idx].name

    server_types = [
        ("cx22", "CX22 - 2 vCPU, 4GB RAM, 40GB (EUR 4.35/mo) - Recommended"),
        ("cx32", "CX32 - 4 vCPU, 8GB RAM, 80GB (EUR 8.09/mo)"),
    ]
    typer.echo()
    typer.echo("Server Size:")
    for i, (_, label) in enumerate(server_types, 1):
        typer.echo(f"  {i}. {label}")
    type_idx = int(typer.prompt("Select size", default="1")) - 1
    selected_type = server_types[type_idx][0]

    typer.echo()
    typer.echo(typer.style("  Summary", bold=True, fg=typer.colors.MAGENTA))
    typer.echo("  Provider: Hetzner Cloud")
    typer.echo(f"  Location: {selected_location}")
    typer.echo(f"  Size:     {selected_type}")
    typer.echo()
    if not typer.confirm("Deploy now?"):
        typer.echo("Deployment cancelled.")
        return

    typer.echo()
    typer.echo(typer.style("Generating SSH key...", fg=typer.colors.CYAN))
    key_path, public_key = _ensure_ssh_key(cfg_module.config_dir())

    typer.echo(typer.style("Uploading SSH key to Hetzner...", fg=typer.colors.CYAN))
    key_name = f"sixtysix-{int(time.time())}"
    try:
        ssh_key = client.ssh_keys.create(name=key_name, public_key=public_key.strip())
        typer.echo(typer.style("✓ SSH key ready", fg=typer.colors.GREEN))
    except Exception:
        existing = client.ssh_keys.get_all()
        ssh_key = next((k for k in existing if k.public_key.strip() == public_key.strip()), None)
        if ssh_key is None:
            typer.echo(typer.style("Failed to upload SSH key", fg=typer.colors.RED), err=True)
            raise typer.Exit(1)
        typer.echo(typer.style("✓ SSH key ready (existing)", fg=typer.colors.GREEN))

    from hcloud.images.domain import Image
    from hcloud.locations.domain import Location
    from hcloud.server_types.domain import ServerType

    typer.echo(typer.style("Creating server...", fg=typer.colors.CYAN))
    server_name = f"sixtysix-{int(time.time())}"
    try:
        response = client.servers.create(
            name=server_name,
            server_type=ServerType(name=selected_type),
            image=Image(name="ubuntu-24.04"),
            location=Location(name=selected_location),
            ssh_keys=[ssh_key],
            user_data=_generate_cloud_init(),
        )
        server = response.server
        typer.echo(typer.style(f"✓ Server created (ID: {server.id})", fg=typer.colors.GREEN))
    except Exception as e:
        typer.echo(typer.style(f"Failed to create server: {e}", fg=typer.colors.RED), err=True)
        raise typer.Exit(1)

    typer.echo(typer.style("Waiting for server to start...", fg=typer.colors.CYAN))
    server_ip = None
    while True:
        s = client.servers.get_by_id(server.id)
        if s.status == "running":
            server_ip = s.public_net.ipv4.ip
            break
        time.sleep(2)
    typer.echo(typer.style("✓ Server is running", fg=typer.colors.GREEN))

    typer.echo(typer.style("Waiting for Docker setup (this may take 2-3 minutes)...", fg=typer.colors.CYAN))
    time.sleep(60)

    cfg.provider = "hetzner"
    cfg.hetzner_token = api_token
    cfg.server_id = server.id
    cfg.server_ip = server_ip
    cfg.ssh_key_path = key_path
    cfg_module.save(cfg)

    typer.echo()
    typer.echo(typer.style("✓ Deployment complete!", fg=typer.colors.GREEN))
    typer.echo()
    typer.echo(typer.style("  Your backend is running", bold=True, fg=typer.colors.MAGENTA))
    typer.echo()
    typer.echo(f"  Server IP: {server_ip}")
    typer.echo(f"  SSH:       ssh -i {key_path} root@{server_ip}")
    typer.echo()
    typer.echo("  Next steps:")
    typer.echo("    1. Open sixtysix.pro in your browser")
    typer.echo("    2. Configure your broker API keys in Settings")
    typer.echo()
    typer.echo("  Commands:")
    typer.echo("    sixtysix cloud status   - Check server status")
    typer.echo("    sixtysix cloud logs     - View container logs")
    typer.echo("    sixtysix cloud ssh      - SSH into server")
    typer.echo("    sixtysix cloud destroy  - Remove server")


def cloud_status() -> None:
    """Show cloud server status."""
    from hcloud import Client

    cfg = cfg_module.load()
    if not cfg.server_id:
        typer.echo(
            typer.style("No cloud deployment found. Run 'sixtysix cloud deploy' first.", fg=typer.colors.RED),
            err=True,
        )
        raise typer.Exit(1)

    client = Client(token=cfg.hetzner_token)
    server = client.servers.get_by_id(cfg.server_id)
    if server is None:
        typer.echo(typer.style("Server not found. It may have been deleted.", fg=typer.colors.RED))
        return

    typer.echo()
    typer.echo(f"Server: {server.name}")
    typer.echo(f"ID:     {server.id}")
    typer.echo(f"IP:     {server.public_net.ipv4.ip}")
    typer.echo()

    if server.status == "running":
        typer.echo("Status: " + typer.style("Running", fg=typer.colors.GREEN))
    elif server.status == "off":
        typer.echo("Status: " + typer.style("Off", fg=typer.colors.YELLOW))
    else:
        typer.echo(f"Status: {server.status}")

    typer.echo(f"Type:   {server.server_type.name}")
    typer.echo(f"Location: {server.datacenter.location.city}, {server.datacenter.location.country}")
    typer.echo()
    typer.echo(f"SSH: ssh -i {cfg.ssh_key_path} root@{server.public_net.ipv4.ip}")


def cloud_logs(lines: int = 50, follow: bool = False) -> None:
    """View container logs from cloud server."""
    cfg = cfg_module.load()
    if not cfg.server_ip:
        typer.echo(
            typer.style("No cloud deployment found. Run 'sixtysix cloud deploy' first.", fg=typer.colors.RED),
            err=True,
        )
        raise typer.Exit(1)

    docker_cmd = f"cd /opt/sixtysix && docker compose logs --tail={lines}"
    if follow:
        docker_cmd += " -f"

    ssh_args = [
        "ssh",
        "-i", cfg.ssh_key_path,
        "-o", "StrictHostKeyChecking=no",
        "-o", "UserKnownHostsFile=/dev/null",
        f"root@{cfg.server_ip}",
        docker_cmd,
    ]
    os.execvp("ssh", ssh_args)


def cloud_restart() -> None:
    """Restart the container on cloud server."""
    cfg = cfg_module.load()
    if not cfg.server_ip:
        typer.echo(
            typer.style("No cloud deployment found. Run 'sixtysix cloud deploy' first.", fg=typer.colors.RED),
            err=True,
        )
        raise typer.Exit(1)

    typer.echo(typer.style("Restarting container...", fg=typer.colors.CYAN))
    result = subprocess.run([
        "ssh",
        "-i", cfg.ssh_key_path,
        "-o", "StrictHostKeyChecking=no",
        "-o", "UserKnownHostsFile=/dev/null",
        f"root@{cfg.server_ip}",
        "cd /opt/sixtysix && docker compose restart",
    ])
    if result.returncode != 0:
        typer.echo(typer.style("Failed to restart container", fg=typer.colors.RED), err=True)
        raise typer.Exit(1)

    typer.echo()
    typer.echo(typer.style("✓ Container restarted", fg=typer.colors.GREEN))


def cloud_update() -> None:
    """Pull the latest image and restart on cloud server."""
    cfg = cfg_module.load()
    if not cfg.server_ip:
        typer.echo(
            typer.style("No cloud deployment found. Run 'sixtysix cloud deploy' first.", fg=typer.colors.RED),
            err=True,
        )
        raise typer.Exit(1)

    typer.echo(typer.style("Pulling latest image...", fg=typer.colors.CYAN))
    result = subprocess.run([
        "ssh",
        "-i", cfg.ssh_key_path,
        "-o", "StrictHostKeyChecking=no",
        "-o", "UserKnownHostsFile=/dev/null",
        f"root@{cfg.server_ip}",
        "cd /opt/sixtysix && docker compose pull && docker compose up -d",
    ])
    if result.returncode != 0:
        typer.echo(typer.style("Failed to update", fg=typer.colors.RED), err=True)
        raise typer.Exit(1)

    typer.echo()
    typer.echo(typer.style("✓ Updated to latest version", fg=typer.colors.GREEN))


def cloud_ssh() -> None:
    """SSH into the cloud server."""
    cfg = cfg_module.load()
    if not cfg.server_ip:
        typer.echo(
            typer.style("No cloud deployment found. Run 'sixtysix cloud deploy' first.", fg=typer.colors.RED),
            err=True,
        )
        raise typer.Exit(1)

    ssh_args = [
        "ssh",
        "-i", cfg.ssh_key_path,
        "-o", "StrictHostKeyChecking=no",
        "-o", "UserKnownHostsFile=/dev/null",
        f"root@{cfg.server_ip}",
    ]
    os.execvp("ssh", ssh_args)


def cloud_destroy() -> None:
    """Delete the Hetzner VPS and all data on it."""
    from hcloud import Client

    cfg = cfg_module.load()
    if not cfg.server_id:
        typer.echo(typer.style("No cloud deployment found.", fg=typer.colors.RED), err=True)
        raise typer.Exit(1)

    client = Client(token=cfg.hetzner_token)
    server = client.servers.get_by_id(cfg.server_id)
    if server is None:
        typer.echo(typer.style("Server not found. Clearing local config.", fg=typer.colors.CYAN))
        cfg.server_id = 0
        cfg.server_ip = ""
        cfg_module.save(cfg)
        return

    typer.echo()
    typer.echo(typer.style("⚠ This will permanently delete:", fg=typer.colors.YELLOW))
    typer.echo(f"  Server: {server.name} (ID: {server.id})")
    typer.echo(f"  IP: {cfg.server_ip}")
    typer.echo()
    typer.echo(typer.style("All data on the server will be lost!", fg=typer.colors.YELLOW))
    typer.echo()

    if not typer.confirm("Are you sure you want to destroy this server?"):
        typer.echo("Cancelled.")
        return

    typer.echo(typer.style("Deleting server...", fg=typer.colors.CYAN))
    try:
        client.servers.delete(server)
    except Exception as e:
        typer.echo(typer.style(f"Failed to delete server: {e}", fg=typer.colors.RED), err=True)
        raise typer.Exit(1)

    cfg.server_id = 0
    cfg.server_ip = ""
    cfg_module.save(cfg)

    typer.echo()
    typer.echo(typer.style("✓ Server destroyed", fg=typer.colors.GREEN))
    typer.echo()
    typer.echo("Run 'sixtysix cloud deploy' to create a new server.")
