"""CLI commands for job progress tracking.

Provides commands for monitoring and managing job progress.
"""

from __future__ import annotations

import time
from pathlib import Path

import click

from oclawma.cli_ui import (
    accent,
    header,
    key_value,
    muted,
    print_error,
    print_info,
    print_success,
    print_warning,
    subheader,
)
from oclawma.progress import ProgressManager, ProgressStore

DEFAULT_PROGRESS_DB = Path.home() / ".oclawma" / "progress.db"


@click.group(name="progress")
@click.option(
    "--db-path",
    type=click.Path(),
    default=str(DEFAULT_PROGRESS_DB),
    help="Path to the progress database",
    show_default=True,
)
@click.pass_context
def progress_cli(ctx: click.Context, db_path: str) -> None:
    """Monitor and manage job progress.

    View real-time progress of running jobs, inspect logs, and manage
    job progress data.

    \b
    Examples:
        oclawma progress list                    # List all tracked jobs
        oclawma progress show <job_id>           # Show progress for a job
        oclawma progress logs <job_id>           # Show job logs
        oclawma progress watch <job_id>          # Watch job in real-time
        oclawma progress delete <job_id>         # Delete job progress data
    """
    ctx.ensure_object(dict)
    ctx.obj["db_path"] = db_path


@progress_cli.command(name="list")
@click.option(
    "--status",
    type=click.Choice(["pending", "running", "completed", "failed", "cancelled"]),
    help="Filter by job status",
)
@click.option(
    "--limit",
    "-n",
    type=int,
    default=50,
    help="Maximum number of jobs to show",
    show_default=True,
)
@click.option(
    "--verbose",
    "-v",
    is_flag=True,
    help="Show detailed information",
)
@click.pass_context
def list_jobs(ctx: click.Context, status: str | None, limit: int, verbose: bool) -> None:
    """List all tracked jobs."""
    db_path = ctx.obj["db_path"]

    try:
        store = ProgressStore(db_path)
        manager = ProgressManager(store)
        jobs = manager.list_jobs(status=status, limit=limit)

        if not jobs:
            if status:
                print_info(f"No {status} jobs found")
            else:
                print_info("No tracked jobs found")
            return

        click.echo(header("TRACKED JOBS", width=58))
        click.echo()

        for job in jobs:
            # Status color
            status_color = {
                "pending": "yellow",
                "running": "blue",
                "completed": "green",
                "failed": "red",
                "cancelled": "magenta",
            }.get(job.status, "white")

            click.echo(f"{accent(f'Job {job.job_id}', bold=True)}")
            click.echo(key_value("  Status", click.style(job.status, fg=status_color)))
            click.echo(key_value("  Progress", f"{job.progress:.1f}%"))
            click.echo(key_value("  Last Updated", job.last_updated))

            if verbose:
                click.echo(key_value("  Logs", str(len(job.logs))))
                if job.metadata:
                    click.echo(key_value("  Metadata", ""))
                    for key, value in job.metadata.items():
                        click.echo(f"    {muted(f'{key}: {value}')}")

            click.echo()

        click.echo(f"Showing {len(jobs)} job(s)")

    except Exception as e:
        print_error(f"Failed to list jobs: {e}")
        raise click.Abort() from e


@progress_cli.command(name="show")
@click.argument("job_id")
@click.pass_context
def show_progress(ctx: click.Context, job_id: str) -> None:
    """Show detailed progress for a job."""
    db_path = ctx.obj["db_path"]

    try:
        store = ProgressStore(db_path)
        manager = ProgressManager(store)
        progress = manager.get_job_progress(job_id)

        if not progress:
            print_error(f"Job '{job_id}' not found")
            raise click.Abort()

        # Status color
        status_color = {
            "pending": "yellow",
            "running": "blue",
            "completed": "green",
            "failed": "red",
            "cancelled": "magenta",
        }.get(progress.status, "white")

        click.echo(header(f"JOB: {job_id}", width=58))
        click.echo()

        click.echo(subheader("STATUS"))
        click.echo(key_value("Status", click.style(progress.status, fg=status_color)))
        click.echo(key_value("Progress", f"{progress.progress:.1f}%"))

        # Progress bar
        bar_width = 40
        filled = int(bar_width * progress.progress / 100)
        bar = "█" * filled + "░" * (bar_width - filled)
        click.echo(f"  [{bar}] {progress.progress:.1f}%")
        click.echo()

        click.echo(subheader("TIMESTAMPS"))
        click.echo(key_value("Last Updated", progress.last_updated))
        click.echo()

        if progress.metadata:
            click.echo(subheader("METADATA"))
            for key, value in progress.metadata.items():
                click.echo(key_value(f"  {key}", str(value)))
            click.echo()

        click.echo(subheader("LOGS"))
        if progress.logs:
            for log in progress.logs[-20:]:  # Show last 20 logs
                level_color = {
                    "debug": "dim",
                    "info": "white",
                    "warning": "yellow",
                    "error": "red",
                }.get(log.get("level", "info"), "white")

                timestamp = log.get("timestamp", "")
                message = log.get("message", "")
                level = log.get("level", "info")

                click.echo(f"  {muted(timestamp)} [{click.style(level, fg=level_color)}] {message}")
        else:
            click.echo("  No logs")

        click.echo()

    except click.Abort:
        raise
    except Exception as e:
        print_error(f"Failed to show progress: {e}")
        raise click.Abort() from e


@progress_cli.command(name="logs")
@click.argument("job_id")
@click.option(
    "--follow",
    "-f",
    is_flag=True,
    help="Follow log output in real-time",
)
@click.option(
    "--tail",
    "-n",
    type=int,
    default=50,
    help="Number of log lines to show",
    show_default=True,
)
@click.pass_context
def show_logs(ctx: click.Context, job_id: str, follow: bool, tail: int) -> None:
    """Show logs for a job."""
    db_path = ctx.obj["db_path"]

    try:
        store = ProgressStore(db_path)
        manager = ProgressManager(store)

        def print_logs(progress, show_all: bool = False):
            """Print logs from progress."""
            logs = progress.logs if show_all else progress.logs[-tail:]

            for log in logs:
                level_color = {
                    "debug": "dim",
                    "info": "white",
                    "warning": "yellow",
                    "error": "red",
                }.get(log.get("level", "info"), "white")

                timestamp = log.get("timestamp", "")
                message = log.get("message", "")
                level = log.get("level", "info")

                click.echo(f"{muted(timestamp)} [{click.style(level, fg=level_color)}] {message}")

        # Get initial progress
        progress = manager.get_job_progress(job_id)
        if not progress:
            print_error(f"Job '{job_id}' not found")
            raise click.Abort()

        if not follow:
            # Just show logs and exit
            print_logs(progress)
            return

        # Follow mode - continuously poll for updates
        click.echo(header(f"LOGS FOR {job_id}", width=58))
        click.echo(muted("Press Ctrl+C to stop following"))
        click.echo()

        # Show existing logs
        print_logs(progress, show_all=True)
        last_log_count = len(progress.logs)

        try:
            while True:
                time.sleep(1)
                progress = manager.get_job_progress(job_id)

                if not progress:
                    print_warning(f"\nJob '{job_id}' no longer exists")
                    break

                # Print any new logs
                if len(progress.logs) > last_log_count:
                    new_logs = progress.logs[last_log_count:]
                    for log in new_logs:
                        level_color = {
                            "debug": "dim",
                            "info": "white",
                            "warning": "yellow",
                            "error": "red",
                        }.get(log.get("level", "info"), "white")

                        timestamp = log.get("timestamp", "")
                        message = log.get("message", "")
                        level = log.get("level", "info")

                        click.echo(
                            f"{muted(timestamp)} [{click.style(level, fg=level_color)}] {message}"
                        )

                    last_log_count = len(progress.logs)

                # Stop following if job is done
                if progress.status in ("completed", "failed", "cancelled"):
                    click.echo()
                    print_info(f"Job {progress.status} - stopping log follow")
                    break

        except KeyboardInterrupt:
            click.echo()
            print_info("Stopped following logs")

    except click.Abort:
        raise
    except Exception as e:
        print_error(f"Failed to show logs: {e}")
        raise click.Abort() from e


@progress_cli.command(name="watch")
@click.argument("job_id")
@click.option(
    "--interval",
    "-i",
    type=float,
    default=1.0,
    help="Update interval in seconds",
    show_default=True,
)
@click.pass_context
def watch_progress(ctx: click.Context, job_id: str, interval: float) -> None:
    """Watch job progress in real-time."""
    db_path = ctx.obj["db_path"]

    try:
        store = ProgressStore(db_path)
        manager = ProgressManager(store)

        # Check job exists
        progress = manager.get_job_progress(job_id)
        if not progress:
            print_error(f"Job '{job_id}' not found")
            raise click.Abort()

        click.echo(header(f"WATCHING {job_id}", width=58))
        click.echo(muted("Press Ctrl+C to stop watching"))
        click.echo()

        last_status = None
        last_progress = -1

        try:
            while True:
                progress = manager.get_job_progress(job_id)

                if not progress:
                    print_warning(f"\nJob '{job_id}' no longer exists")
                    break

                # Only update display on changes
                if progress.status != last_status or progress.progress != last_progress:
                    # Clear line and show progress
                    status_color = {
                        "pending": "yellow",
                        "running": "blue",
                        "completed": "green",
                        "failed": "red",
                        "cancelled": "magenta",
                    }.get(progress.status, "white")

                    # Progress bar
                    bar_width = 30
                    filled = int(bar_width * progress.progress / 100)
                    bar = "█" * filled + "░" * (bar_width - filled)

                    status_text = f"\r[{bar}] {progress.progress:6.2f}% | {click.style(progress.status.upper(), fg=status_color)}"
                    click.echo(status_text, nl=False)

                    last_status = progress.status
                    last_progress = progress.progress

                # Stop watching if job is done
                if progress.status in ("completed", "failed", "cancelled"):
                    click.echo()  # New line after progress
                    click.echo()

                    if progress.status == "completed":
                        print_success(f"Job {job_id} completed!")
                    elif progress.status == "failed":
                        print_error(f"Job {job_id} failed!")
                    else:
                        print_warning(f"Job {job_id} was cancelled")

                    # Show recent logs
                    if progress.logs:
                        click.echo()
                        click.echo(subheader("RECENT LOGS"))
                        for log in progress.logs[-5:]:
                            level_color = {
                                "debug": "dim",
                                "info": "white",
                                "warning": "yellow",
                                "error": "red",
                            }.get(log.get("level", "info"), "white")

                            timestamp = log.get("timestamp", "")[11:19]  # Just time
                            message = log.get("message", "")
                            level = log.get("level", "info")

                            click.echo(
                                f"  {timestamp} [{click.style(level, fg=level_color)}] {message}"
                            )

                    break

                time.sleep(interval)

        except KeyboardInterrupt:
            click.echo()
            click.echo()
            print_info("Stopped watching")

    except click.Abort:
        raise
    except Exception as e:
        print_error(f"Failed to watch progress: {e}")
        raise click.Abort() from e


@progress_cli.command(name="delete")
@click.argument("job_id")
@click.confirmation_option(
    prompt="Are you sure you want to delete this job's progress data?",
    help="Confirm deletion",
)
@click.pass_context
def delete_progress(ctx: click.Context, job_id: str) -> None:
    """Delete a job's progress data."""
    db_path = ctx.obj["db_path"]

    try:
        store = ProgressStore(db_path)
        manager = ProgressManager(store)

        # Remove from active trackers
        manager.remove_tracker(job_id)

        # Delete from storage
        deleted = manager.store.delete_job(job_id)

        if deleted:
            print_success(f"Deleted progress data for job '{job_id}'")
        else:
            print_warning(f"Job '{job_id}' not found")

    except Exception as e:
        print_error(f"Failed to delete progress: {e}")
        raise click.Abort() from e


@progress_cli.command(name="cleanup")
@click.option(
    "--days",
    "-d",
    type=int,
    default=30,
    help="Remove jobs older than this many days",
    show_default=True,
)
@click.option(
    "--dry-run",
    is_flag=True,
    help="Show what would be deleted without deleting",
)
@click.pass_context
def cleanup_progress(ctx: click.Context, days: int, dry_run: bool) -> None:
    """Clean up old job progress data."""
    db_path = ctx.obj["db_path"]

    try:
        store = ProgressStore(db_path)

        if dry_run:
            # Count old jobs without deleting
            with store.connection() as conn:
                cursor = conn.cursor()
                cursor.execute(
                    f"""
                    SELECT COUNT(*) FROM job_progress
                    WHERE last_updated < datetime('now', '-{days} days')
                    """
                )
                count = cursor.fetchone()[0]

            print_info(f"Would delete {count} job(s) older than {days} days")
        else:
            deleted_count = store.cleanup_old_jobs(days)
            print_success(f"Deleted {deleted_count} job(s) older than {days} days")

    except Exception as e:
        print_error(f"Failed to cleanup progress: {e}")
        raise click.Abort() from e
