import os

import boto3
import typer
from botocore.exceptions import ClientError
from flashenv.config import KEY_NAME, PRIV_KEY_PATH, SG_NAME
from flashenv.core.aws import get_latest_ami
from flashenv.core.provision import fetch_container_logs, provision_instance
from flashenv.core.state import add_env, get_env, list_envs, remove_env
from rich.console import Console
from rich.panel import Panel
from rich.syntax import Syntax
from rich.table import Table
from flashenv.utils.init import check_init

console = Console()
app = typer.Typer(help="Handle environments.")


@app.callback()
def env_callback():
    check_init()


@app.command("create")
def create(
    name: str = typer.Argument(..., help="Name of the environment to create"),
    image: str = typer.Option(..., "--image", "-i", help="Docker image to deploy (eg. nginx)"),
    instance_type: str = typer.Option("t2.micro", "--type", "-t", help="EC2 instance type"),
    spot: bool = typer.Option(
        False, "--spot", "-s", help="Utiliser une instance Spot (moins cher, mais interruptible)"
    ),
):
    """Create a new environment on AWS."""
    if get_env(name):
        typer.secho(f"❌ Environment '{name}' already exists locally.", fg=typer.colors.RED)
        raise typer.Exit(1)

    ec2 = boto3.client("ec2")
    try:
        with console.status("[bold green]Looking for latest AMI..."):
            ami_id = get_latest_ami(ec2)
        launch_params = {
            "ImageId": ami_id,
            "InstanceType": instance_type,
            "KeyName": KEY_NAME,
            "SecurityGroups": [SG_NAME],
            "MinCount": 1,
            "MaxCount": 1,
            "TagSpecifications": [{"ResourceType": "instance", "Tags": [{"Key": "Name", "Value": f"flashenv-{name}"}]}],
        }
        if spot:
            typer.secho("Spot mode activated", fg=typer.colors.CYAN)
            launch_params["InstanceMarketOptions"] = {
                "MarketType": "spot",
                "SpotOptions": {"SpotInstanceType": "one-time", "InstanceInterruptionBehavior": "terminate"},
            }
        else:
            typer.echo("Standard Mode (On-Demand).")
        typer.echo(f"🚀  Launching {instance_type} instance ...")
        run_instances = ec2.run_instances(**launch_params)
        instance = run_instances["Instances"][0]
        instance_id = instance.get("InstanceId")
        if instance_id is None:
            typer.secho("❌ Unable to retrieve instance ID.", fg=typer.colors.RED)
            raise typer.Exit(1)

        with console.status(f"[bold yellow]Launch {instance_id} instance (it takes about 30s)..."):
            waiter = ec2.get_waiter("instance_running")
            waiter.wait(InstanceIds=[instance_id])

            instance_info = ec2.describe_instances(InstanceIds=[instance_id])
            reservations = instance_info.get("Reservations", [])

            if not reservations or not reservations[0].get("Instances"):
                typer.secho("❌ Critical error: Instance not found after creation.", fg=typer.colors.RED)
                raise typer.Exit(1)

            instances_list = reservations[0].get("Instances", [])
            if not instances_list:
                typer.secho("❌ Critical error: Missing instance data.", fg=typer.colors.RED)
                raise typer.Exit(1)
            instance_data = instances_list[0]
            public_ip = instance_data.get("PublicIpAddress", "N/A")

        add_env(name, instance_id, public_ip, image, spot=spot)
        try:
            provision_instance(public_ip, image)
            typer.secho(f"\n✅ Environment '{name}' is live and ready", fg=typer.colors.GREEN, bold=True)
            typer.echo(f"   🔹 ID   : {instance_id}")
            typer.echo(f"   🔹 IP   : {public_ip}")
            typer.launch(f"http://{public_ip}")
            typer.echo(f"\nTo delete: flashenv env delete {name}")
        except Exception as e:
            typer.secho(f"❌ Provisioning error: {e}", fg=typer.colors.RED)
            typer.secho(
                "⚠️  The instance was created but provisioning failed."
                " You can try to connect via SSH and fix it manually.",
                fg=typer.colors.YELLOW,
            )
    except ClientError as e:
        typer.secho(f"❌ AWS Error : {e}", fg=typer.colors.RED)
        raise typer.Exit(1) from None
    except Exception as e:
        typer.secho(f"❌ Unexpected error : {e}", fg=typer.colors.RED)
        raise typer.Exit(1) from None


@app.command("list")
def list_cmd():
    """Lists all active environments managed locally."""
    envs = list_envs()

    if not envs:
        console.print("📭 No active environments found.", style="bold yellow")
        console.print("💡 Create one using: [green]flashenv env create <name> --image <image>[/green]")
        raise typer.Exit()

    table = Table(title="⚡ flashenv Environments")
    table.add_column("Name", style="cyan", no_wrap=True)
    table.add_column("Instance ID", style="magenta")
    table.add_column("Public IP", style="green")
    table.add_column("Docker Image", style="blue")
    table.add_column("Created At", style="dim")

    for name, data in envs.items():
        created_at = data.get("created_at", "N/A")
        if created_at != "N/A":
            created_at = created_at.replace("T", " ")[:16]

        table.add_row(
            name, data.get("id", "Unknown"), data.get("ip", "Pending..."), data.get("image", "N/A"), created_at
        )
    console.print(table)


@app.command("delete")
def delete(
    name: str = typer.Argument(..., help="Name of the environment to delete"),
    force: bool = typer.Option(False, "--force", "-f", help="Force deletion without confirmation"),
):
    """Delete an environment both locally and on AWS."""
    env_data = get_env(name)
    if not env_data:
        typer.secho(f"❌ Environnement '{name}' not found in local registry.", fg=typer.colors.RED)
        raise typer.Exit(1)

    instance_id = env_data["id"]

    if not force:
        delete_confirm = typer.confirm(f"⚠️  Are you sure you want to destroy '{name}' ({instance_id}) ?")
        if not delete_confirm:
            typer.echo("Cancelled.")
            raise typer.Exit()

    ec2 = boto3.client("ec2")

    try:
        with console.status(f"[bold red] Instance {instance_id} termination on AWS..."):
            try:
                ec2.terminate_instances(InstanceIds=[instance_id])
                console.log(f"✅ Termination order sent for {instance_id}.")
            except ClientError as e:
                error_code = e.response.get("Error", {}).get("Code")
                if error_code == "InvalidInstanceID":
                    console.log(f"⚠️  Instance {instance_id} does not exist on AWS (already deleted ?).")
                else:
                    raise e
        remove_env(name)
        typer.secho(f"🗑️  Environnement '{name}' deleted from local registry.", fg=typer.colors.GREEN)
    except ClientError as e:
        typer.secho(f"❌ Erreur AWS critique : {e}", fg=typer.colors.RED)
        raise typer.Exit(1) from None
    except Exception as e:
        typer.secho(f"❌ Erreur inattendue : {e}", fg=typer.colors.RED)
        raise typer.Exit(1) from None


@app.command("ssh")
def ssh(
    name: str = typer.Argument(..., help="Name of the environment to connect to"),
    user: str = typer.Option("ec2-user", "--user", "-u", help="SSH User (default: ec2-user for Amazon Linux)"),
):
    """Connects to the environment via SSH."""
    env_data = get_env(name)
    if not env_data:
        typer.secho(f"❌ Environment '{name}' not found locally.", fg=typer.colors.RED)
        raise typer.Exit(1)

    ip = env_data.get("ip")
    if not ip or ip == "Pending...":
        typer.secho(f"❌ IP address for '{name}' is not ready yet.", fg=typer.colors.RED)
        raise typer.Exit(1)

    key_path = PRIV_KEY_PATH
    if not key_path.exists():
        typer.secho(f"❌ Private key not found at {key_path}.", fg=typer.colors.RED)
        typer.echo("Did you run 'flashenv init'?")
        raise typer.Exit(1)

    ssh_cmd = [
        "ssh",
        "-i",
        str(key_path),
        "-o",
        "StrictHostKeyChecking=no",
        "-o",
        "UserKnownHostsFile=/dev/null",
        "-o",
        "LogLevel=ERROR",
        f"{user}@{ip}",
    ]
    typer.secho(f"🔌 Connecting to {name} ({ip})...", fg=typer.colors.CYAN)

    try:
        os.execvp("ssh", ssh_cmd)
    except FileNotFoundError:
        typer.secho("❌ Error: 'ssh' command not found on your system.", fg=typer.colors.RED)
        raise typer.Exit(1) from None


@app.command("logs")
def logs(name: str = typer.Argument(..., help="Name of the environment")):
    """
    Fetches and displays the remote Docker container logs.
    """
    env_data = get_env(name)
    if not env_data:
        typer.secho(f"❌ Environment '{name}' not found.", fg=typer.colors.RED)
        raise typer.Exit(1)

    ip = env_data.get("ip")

    with console.status(f"[bold cyan]🔌 Connecting to {name} ({ip})..."):
        log_content = fetch_container_logs(str(ip), container_name="app")

    if log_content.startswith("❌"):
        console.print(log_content, style="bold red")
    else:
        console.print(
            Panel(
                Syntax(log_content, "log", theme="monokai", word_wrap=True),
                title=f"🐳 Logs: {name} ({ip})",
                border_style="cyan",
                expand=False,
            )
        )
