"""CLI commands for auto-scaling worker pools.

Provides commands for managing auto-scaling configuration and operations.
"""

from __future__ import annotations

import logging
import signal
import time
from pathlib import Path

import click

from oclawma.cli_ui import (
    accent,
    header,
    highlight,
    key_value,
    print_error,
    print_info,
    print_success,
    print_warning,
    subheader,
)

logger = logging.getLogger(__name__)


@click.group(name="autoscale")
def autoscale_cli():
    """Manage auto-scaling worker pools.

    Automatically scale workers based on queue depth and processing rate.
    Supports local, Kubernetes, AWS, and GCP backends.

    \b
    Examples:
        oclawma autoscale run --backend local
        oclawma autoscale status
        oclawma autoscale scale-up --count 3
        oclawma autoscale recommend
    """
    pass


@autoscale_cli.command(name="run")
@click.option(
    "--backend",
    "-b",
    type=click.Choice(["local", "kubernetes", "aws", "gcp"], case_sensitive=False),
    default="local",
    help="Backend type for worker scaling",
    show_default=True,
)
@click.option(
    "--queue-db",
    "-q",
    type=click.Path(exists=False),
    default="~/.oclawma/queue.db",
    help="Path to queue database",
)
@click.option(
    "--scale-up-threshold",
    type=int,
    default=50,
    help="Queue depth to trigger scale-up",
    show_default=True,
)
@click.option(
    "--scale-down-threshold",
    type=int,
    default=5,
    help="Queue depth to trigger scale-down",
    show_default=True,
)
@click.option(
    "--max-workers",
    type=int,
    default=10,
    help="Maximum number of workers",
    show_default=True,
)
@click.option(
    "--min-workers",
    type=int,
    default=1,
    help="Minimum number of workers",
    show_default=True,
)
@click.option(
    "--scale-up-cooldown",
    type=float,
    default=60.0,
    help="Seconds between scale-up operations",
    show_default=True,
)
@click.option(
    "--scale-down-cooldown",
    type=float,
    default=120.0,
    help="Seconds between scale-down operations",
    show_default=True,
)
@click.option(
    "--interval",
    "-i",
    type=float,
    default=10.0,
    help="Evaluation interval in seconds",
    show_default=True,
)
@click.option(
    "--deployment-name",
    help="Kubernetes deployment name (for k8s backend)",
)
@click.option(
    "--namespace",
    "-n",
    default="default",
    help="Kubernetes namespace (for k8s backend)",
    show_default=True,
)
@click.option(
    "--asg-name",
    help="AWS Auto Scaling Group name (for AWS backend)",
)
@click.option(
    "--region",
    "-r",
    default="us-east-1",
    help="AWS region (for AWS backend)",
    show_default=True,
)
@click.option(
    "--project-id",
    help="GCP project ID (for GCP backend)",
)
@click.option(
    "--zone",
    "-z",
    default="us-central1-a",
    help="GCP zone (for GCP backend)",
    show_default=True,
)
@click.option(
    "--instance-group",
    help="GCP instance group name (for GCP backend)",
)
@click.option(
    "--dry-run",
    is_flag=True,
    help="Show recommendations without applying them",
)
def run_autoscaler(
    backend: str,
    queue_db: str,
    scale_up_threshold: int,
    scale_down_threshold: int,
    max_workers: int,
    min_workers: int,
    scale_up_cooldown: float,
    scale_down_cooldown: float,
    interval: float,
    deployment_name: str | None,
    namespace: str,
    asg_name: str | None,
    region: str,
    project_id: str | None,
    zone: str,
    instance_group: str | None,
    dry_run: bool,
) -> None:
    """Run the auto-scaler daemon.

    Continuously monitors queue depth and adjusts worker count based on thresholds.

    Examples:
        oclawma autoscale run
        oclawma autoscale run --backend kubernetes --deployment-name oclawma-workers
        oclawma autoscale run --backend aws --asg-name oclawma-asg
        oclawma autoscale run --dry-run  # Preview mode
    """
    from oclawma.autoscale import ScalingThresholds, create_autoscaler
    from oclawma.metrics import get_collector
    from oclawma.queue import JobQueue

    click.echo(header("AUTO-SCALER", width=58))
    click.echo()

    # Validate thresholds
    try:
        thresholds = ScalingThresholds(
            scale_up_threshold=scale_up_threshold,
            scale_down_threshold=scale_down_threshold,
            max_workers=max_workers,
            min_workers=min_workers,
            scale_up_cooldown=scale_up_cooldown,
            scale_down_cooldown=scale_down_cooldown,
        )
    except ValueError as e:
        print_error(f"Invalid threshold configuration: {e}")
        raise click.Abort() from None

    # Backend-specific configuration
    backend_kwargs: dict[str, str | int] = {}
    if backend == "kubernetes":
        backend_kwargs["deployment_name"] = deployment_name or "oclawma-workers"
        backend_kwargs["namespace"] = namespace
    elif backend == "aws":
        backend_kwargs["asg_name"] = asg_name or "oclawma-asg"
        backend_kwargs["region"] = region
    elif backend == "gcp":
        backend_kwargs["project_id"] = project_id or ""
        backend_kwargs["zone"] = zone
        backend_kwargs["instance_group_name"] = instance_group or "oclawma-mig"

    # Show configuration
    click.echo(subheader("CONFIGURATION"))
    click.echo(key_value("Backend", backend))
    click.echo(key_value("Queue DB", queue_db))
    click.echo(key_value("Evaluation Interval", f"{interval}s"))
    click.echo()

    click.echo(subheader("THRESHOLDS"))
    click.echo(key_value("Scale Up", f"≥ {scale_up_threshold} jobs"))
    click.echo(key_value("Scale Down", f"≤ {scale_down_threshold} jobs"))
    click.echo(key_value("Min Workers", str(min_workers)))
    click.echo(key_value("Max Workers", str(max_workers)))
    click.echo(key_value("Scale Up Cooldown", f"{scale_up_cooldown}s"))
    click.echo(key_value("Scale Down Cooldown", f"{scale_down_cooldown}s"))
    click.echo()

    if dry_run:
        print_info("Running in DRY-RUN mode (recommendations only)")
        click.echo()

    # Initialize auto-scaler
    try:
        metrics_collector = get_collector()
        scaler = create_autoscaler(
            backend_type=backend,
            thresholds=thresholds,
            metrics_collector=metrics_collector,
            **backend_kwargs,
        )
        print_success(f"Initialized {backend} auto-scaler backend")
    except Exception as e:
        print_error(f"Failed to initialize auto-scaler: {e}")
        raise click.Abort() from None

    # Connect to queue
    db_path = Path(queue_db).expanduser()
    queue: JobQueue | None = None

    if db_path.exists():
        try:
            queue = JobQueue(str(db_path))
            print_success(f"Connected to queue: {db_path}")
        except Exception as e:
            print_warning(f"Could not connect to queue: {e}")
    else:
        print_warning(f"Queue database not found: {db_path}")

    click.echo()
    click.echo(subheader("STATUS"))
    click.echo("Press Ctrl+C to stop")
    click.echo()

    # Setup signal handlers
    running = True

    def signal_handler(sig, frame):
        nonlocal running
        running = False
        click.echo()
        print_info("Shutdown requested...")

    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)

    # Main loop
    try:
        while running:
            try:
                # Get current metrics
                if queue:
                    stats = queue.get_stats()
                    queue_depth = stats.queue_depth
                    metrics_collector.set_queue_depth(queue_depth)
                else:
                    queue_depth = metrics_collector.queue_depth

                # Get recommendation
                recommendation = scaler.recommend_scale(
                    queue_depth=queue_depth,
                    worker_count=scaler._current_worker_count,
                )

                # Display current state
                status = scaler.get_status()
                click.echo(
                    f"[{time.strftime('%H:%M:%S')}] "
                    f"Queue: {accent(str(queue_depth))} | "
                    f"Workers: {highlight(str(status['current_workers']))} | "
                    f"Rate: {status['processing_rate']:.1f}/s | "
                    f"Decision: {recommendation.decision.name}"
                )

                # Apply recommendation (unless dry-run)
                if not dry_run and recommendation.decision.value != "MAINTAIN":
                    success = scaler.apply_recommendation(recommendation)
                    if success:
                        print_success(
                            f"  → Scaled to {recommendation.target_workers} workers: {recommendation.reason}"
                        )
                    else:
                        print_warning("  → Scaling action failed or was blocked")
                elif dry_run and recommendation.decision.value != "MAINTAIN":
                    print_info(f"  → Would scale: {recommendation.reason}")

            except Exception as e:
                logger.error(f"Error in auto-scaler loop: {e}")
                print_error(f"Loop error: {e}")

            # Wait for next iteration
            for _ in range(int(interval)):
                if not running:
                    break
                time.sleep(1)

    finally:
        if queue:
            queue.close()
        print_success("Auto-scaler stopped")


@autoscale_cli.command(name="status")
@click.option(
    "--backend",
    "-b",
    type=click.Choice(["local", "kubernetes", "aws", "gcp"], case_sensitive=False),
    default="local",
    help="Backend type",
    show_default=True,
)
def autoscale_status(backend: str) -> None:
    """Show auto-scaler status."""
    from oclawma.autoscale import create_autoscaler

    click.echo(header("AUTO-SCALER STATUS", width=58))
    click.echo()

    try:
        scaler = create_autoscaler(backend_type=backend)
        status = scaler.get_status()

        click.echo(subheader("CURRENT STATE"))
        click.echo(key_value("Current Workers", str(status["current_workers"])))
        click.echo(key_value("Processing Rate", f"{status['processing_rate']:.2f} jobs/s"))
        click.echo(key_value("Backend Type", status["backend_type"]))
        click.echo()

        click.echo(subheader("THRESHOLDS"))
        thresholds = status["thresholds"]
        click.echo(key_value("Scale Up Threshold", str(thresholds["scale_up_threshold"])))
        click.echo(key_value("Scale Down Threshold", str(thresholds["scale_down_threshold"])))
        click.echo(key_value("Max Workers", str(thresholds["max_workers"])))
        click.echo(key_value("Min Workers", str(thresholds["min_workers"])))
        click.echo()

        click.echo(subheader("COOLDOWNS"))
        cooldowns = status["cooldowns"]
        click.echo(key_value("Scale Up Remaining", f"{cooldowns['scale_up_remaining']:.0f}s"))
        click.echo(key_value("Scale Down Remaining", f"{cooldowns['scale_down_remaining']:.0f}s"))
        click.echo()

    except Exception as e:
        print_error(f"Failed to get status: {e}")
        raise click.Abort() from None


@autoscale_cli.command(name="recommend")
@click.option(
    "--queue-depth",
    "-d",
    type=int,
    required=True,
    help="Current queue depth",
)
@click.option(
    "--worker-count",
    "-w",
    type=int,
    help="Current worker count (detected if not provided)",
)
@click.option(
    "--scale-up-threshold",
    type=int,
    default=50,
    help="Queue depth to trigger scale-up",
    show_default=True,
)
@click.option(
    "--scale-down-threshold",
    type=int,
    default=5,
    help="Queue depth to trigger scale-down",
    show_default=True,
)
@click.option(
    "--max-workers",
    type=int,
    default=10,
    help="Maximum number of workers",
    show_default=True,
)
@click.option(
    "--min-workers",
    type=int,
    default=1,
    help="Minimum number of workers",
    show_default=True,
)
def recommend_scale(
    queue_depth: int,
    worker_count: int | None,
    scale_up_threshold: int,
    scale_down_threshold: int,
    max_workers: int,
    min_workers: int,
) -> None:
    """Get scaling recommendation without applying it.

    Useful for testing threshold configuration before deploying.

    Example:
        oclawma autoscale recommend --queue-depth 100 --worker-count 3
    """
    from oclawma.autoscale import AutoScaler, ScalingThresholds

    click.echo(header("SCALING RECOMMENDATION", width=58))
    click.echo()

    try:
        thresholds = ScalingThresholds(
            scale_up_threshold=scale_up_threshold,
            scale_down_threshold=scale_down_threshold,
            max_workers=max_workers,
            min_workers=min_workers,
        )
    except ValueError as e:
        print_error(f"Invalid threshold configuration: {e}")
        raise click.Abort() from None

    scaler = AutoScaler(thresholds=thresholds)

    if worker_count is None:
        worker_count = scaler._current_worker_count

    recommendation = scaler.recommend_scale(
        queue_depth=queue_depth,
        worker_count=worker_count,
    )

    click.echo(subheader("INPUT"))
    click.echo(key_value("Queue Depth", str(queue_depth)))
    click.echo(key_value("Current Workers", str(worker_count)))
    click.echo()

    click.echo(subheader("RECOMMENDATION"))
    decision_color = {
        "SCALE_UP": "green",
        "SCALE_DOWN": "yellow",
        "MAINTAIN": "cyan",
    }.get(recommendation.decision.name, "white")

    click.echo(key_value("Decision", click.style(recommendation.decision.name, fg=decision_color)))
    click.echo(key_value("Current Workers", str(recommendation.current_workers)))
    click.echo(key_value("Target Workers", str(recommendation.target_workers)))
    click.echo(key_value("Reason", recommendation.reason))
    click.echo()

    click.echo(subheader("METRICS"))
    click.echo(key_value("Processing Rate", f"{recommendation.metrics.processing_rate:.2f} jobs/s"))


@autoscale_cli.command(name="scale-up")
@click.argument("count", type=int, default=1)
@click.option(
    "--backend",
    "-b",
    type=click.Choice(["local", "kubernetes", "aws", "gcp"], case_sensitive=False),
    default="local",
    help="Backend type",
    show_default=True,
)
def scale_up(count: int, backend: str) -> None:
    """Manually scale up workers.

    Example:
        oclawma autoscale scale-up 3
    """
    from oclawma.autoscale import create_autoscaler

    click.echo(header("SCALE UP", width=58))
    click.echo()

    try:
        scaler = create_autoscaler(backend_type=backend)
        current = scaler._current_worker_count

        click.echo(f"Current workers: {current}")
        click.echo(f"Scaling up by: {count}")
        click.echo()

        success = scaler.scale_up(count)

        if success:
            new_count = scaler._current_worker_count
            print_success(f"Scaled up from {current} to {new_count} workers")
        else:
            print_warning("Scale up was blocked (may be at max workers)")

    except Exception as e:
        print_error(f"Failed to scale up: {e}")
        raise click.Abort() from None


@autoscale_cli.command(name="scale-down")
@click.argument("count", type=int, default=1)
@click.option(
    "--backend",
    "-b",
    type=click.Choice(["local", "kubernetes", "aws", "gcp"], case_sensitive=False),
    default="local",
    help="Backend type",
    show_default=True,
)
def scale_down(count: int, backend: str) -> None:
    """Manually scale down workers.

    Example:
        oclawma autoscale scale-down 2
    """
    from oclawma.autoscale import create_autoscaler

    click.echo(header("SCALE DOWN", width=58))
    click.echo()

    try:
        scaler = create_autoscaler(backend_type=backend)
        current = scaler._current_worker_count

        click.echo(f"Current workers: {current}")
        click.echo(f"Scaling down by: {count}")
        click.echo()

        success = scaler.scale_down(count)

        if success:
            new_count = scaler._current_worker_count
            print_success(f"Scaled down from {current} to {new_count} workers")
        else:
            print_warning("Scale down was blocked (may be at min workers)")

    except Exception as e:
        print_error(f"Failed to scale down: {e}")
        raise click.Abort() from None


@autoscale_cli.command(name="set")
@click.argument("count", type=int)
@click.option(
    "--backend",
    "-b",
    type=click.Choice(["local", "kubernetes", "aws", "gcp"], case_sensitive=False),
    default="local",
    help="Backend type",
    show_default=True,
)
def set_workers(count: int, backend: str) -> None:
    """Set exact worker count.

    Example:
        oclawma autoscale set 5
    """
    from oclawma.autoscale import create_autoscaler

    click.echo(header("SET WORKER COUNT", width=58))
    click.echo()

    try:
        scaler = create_autoscaler(backend_type=backend)
        current = scaler._current_worker_count

        click.echo(f"Current workers: {current}")
        click.echo(f"Setting to: {count}")
        click.echo()

        success = scaler.backend.set_worker_count(count)
        scaler._sync_worker_count()

        if success:
            print_success(f"Worker count set to {scaler._current_worker_count}")
        else:
            print_error("Failed to set worker count")
            raise click.Abort()

    except Exception as e:
        print_error(f"Failed to set workers: {e}")
        raise click.Abort() from None
