"""CLI commands for the Dead Letter Queue (DLQ)."""

from __future__ import annotations

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.queue import JobQueue
from oclawma.queue.dlq import DeadLetterQueue, DLQNotFoundError

DEFAULT_QUEUE_DB = Path.home() / ".oclawma" / "queue.db"


@click.group(name="dlq")
@click.option(
    "--db-path",
    type=click.Path(),
    default=str(DEFAULT_QUEUE_DB),
    help="Path to the queue database",
    show_default=True,
)
@click.pass_context
def dlq_cli(ctx: click.Context, db_path: str) -> None:
    """Manage the Dead Letter Queue (DLQ).

    The DLQ stores jobs that have failed after exceeding their maximum
    retry attempts. You can list, inspect, replay, or delete these jobs.

    Examples:
        oclawma dlq list              # List all DLQ entries
        oclawma dlq replay 123        # Replay a specific job
        oclawma dlq replay-all        # Replay all jobs
        oclawma dlq delete 123        # Delete a DLQ entry
    """
    ctx.ensure_object(dict)
    ctx.obj["db_path"] = db_path


@dlq_cli.command(name="list")
@click.option(
    "--limit",
    "-n",
    type=int,
    default=50,
    help="Maximum number of entries to show",
    show_default=True,
)
@click.option(
    "--verbose",
    "-v",
    is_flag=True,
    help="Show detailed information including stack traces",
)
@click.pass_context
def dlq_list(ctx: click.Context, limit: int, verbose: bool) -> None:
    """List jobs in the Dead Letter Queue."""
    db_path = ctx.obj["db_path"]

    try:
        with DeadLetterQueue(db_path) as dlq:
            entries = dlq.list(limit=limit)
            stats = dlq.get_stats()

        if not entries:
            print_info("Dead Letter Queue is empty")
            return

        click.echo(header("DEAD LETTER QUEUE", width=58))
        click.echo()

        # Show stats
        click.echo(subheader("STATISTICS"))
        click.echo(key_value("Total Entries", str(stats.total)))
        if stats.by_error_type:
            click.echo(key_value("Error Types", ""))
            for error_type, count in sorted(stats.by_error_type.items(), key=lambda x: -x[1]):
                click.echo(f"  {muted('•')} {error_type}: {count}")
        click.echo()

        # Show entries
        click.echo(subheader(f"ENTRIES (showing {len(entries)} of {stats.total})"))
        click.echo()

        for entry in entries:
            job_id = entry.job.id or "N/A"
            failed_at = entry.failed_at.strftime("%Y-%m-%d %H:%M:%S")

            click.echo(f"{accent(f'Entry #{entry.id}', bold=True)}")
            click.echo(key_value("  Job ID", str(job_id)))
            click.echo(key_value("  Failed At", failed_at))
            click.echo(key_value("  Retries", str(entry.retry_count)))
            click.echo(key_value("  Error Type", entry.failure_reason))

            if entry.error_message:
                error_msg = entry.error_message
                if len(error_msg) > 80 and not verbose:
                    error_msg = error_msg[:77] + "..."
                click.echo(key_value("  Message", error_msg))

            if verbose and entry.stack_trace:
                click.echo(key_value("  Stack Trace", ""))
                for line in entry.stack_trace.split("\n")[:10]:
                    click.echo(f"    {muted(line)}")
                if len(entry.stack_trace.split("\n")) > 10:
                    click.echo(f"    {muted('... (truncated)')}")

            click.echo()

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


@dlq_cli.command(name="replay")
@click.argument("entry_id", type=int)
@click.option(
    "--enqueue",
    is_flag=True,
    help="Automatically enqueue the replayed job",
)
@click.pass_context
def dlq_replay(ctx: click.Context, entry_id: int, enqueue: bool) -> None:
    """Replay a job from the Dead Letter Queue.

    Creates a new job based on the original failed job with:
    - Status reset to PENDING
    - retry_count reset to 0
    - error cleared

    The DLQ entry is deleted after replay.
    """
    db_path = ctx.obj["db_path"]

    try:
        with DeadLetterQueue(db_path) as dlq:
            # Verify entry exists before replay
            try:
                dlq.get(entry_id)
            except DLQNotFoundError:
                print_error(f"DLQ entry {entry_id} not found")
                raise click.Abort() from None

            # Replay the job
            job = dlq.replay(entry_id)

        # Optionally enqueue the job
        if enqueue:
            with JobQueue(db_path) as queue:
                new_job = queue.enqueue(
                    payload=job.payload,
                    priority=job.priority,
                    max_retries=job.max_retries,
                    depends_on=job.depends_on,
                )
                print_success(f"Replayed and enqueued job {new_job.id}")
        else:
            print_success(f"Replayed job from DLQ entry {entry_id}")
            print_info("Use --enqueue to add it back to the queue")

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


@dlq_cli.command(name="replay-all")
@click.option(
    "--enqueue",
    is_flag=True,
    help="Automatically enqueue all replayed jobs",
)
@click.pass_context
def dlq_replay_all(ctx: click.Context, enqueue: bool) -> None:
    """Replay all jobs from the Dead Letter Queue."""
    db_path = ctx.obj["db_path"]

    try:
        with DeadLetterQueue(db_path) as dlq:
            stats_before = dlq.get_stats()

            if stats_before.total == 0:
                print_info("Dead Letter Queue is empty")
                return

            jobs = dlq.replay_all()

        if enqueue:
            with JobQueue(db_path) as queue:
                count = 0
                for job in jobs:
                    queue.enqueue(
                        payload=job.payload,
                        priority=job.priority,
                        max_retries=job.max_retries,
                        depends_on=job.depends_on,
                    )
                    count += 1
                print_success(f"Replayed and enqueued {count} jobs")
        else:
            print_success(f"Replayed {len(jobs)} jobs from DLQ")
            print_info("Use --enqueue to add them back to the queue")

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


@dlq_cli.command(name="delete")
@click.argument("entry_id", type=int)
@click.confirmation_option(
    prompt="Are you sure you want to delete this DLQ entry?",
    help="Confirm deletion",
)
@click.pass_context
def dlq_delete(ctx: click.Context, entry_id: int) -> None:
    """Delete a job from the Dead Letter Queue."""
    db_path = ctx.obj["db_path"]

    try:
        with DeadLetterQueue(db_path) as dlq:
            result = dlq.delete(entry_id)

        if result:
            print_success(f"Deleted DLQ entry {entry_id}")
        else:
            print_warning(f"DLQ entry {entry_id} not found")

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


@dlq_cli.command(name="stats")
@click.pass_context
def dlq_stats(ctx: click.Context) -> None:
    """Show Dead Letter Queue statistics."""
    db_path = ctx.obj["db_path"]

    try:
        with DeadLetterQueue(db_path) as dlq:
            stats = dlq.get_stats()

        click.echo(header("DLQ STATISTICS", width=58))
        click.echo()

        click.echo(key_value("Total Entries", str(stats.total)))

        if stats.oldest:
            click.echo(key_value("Oldest Entry", stats.oldest.strftime("%Y-%m-%d %H:%M:%S")))
        if stats.newest:
            click.echo(key_value("Newest Entry", stats.newest.strftime("%Y-%m-%d %H:%M:%S")))

        click.echo()

        if stats.by_error_type:
            click.echo(subheader("ERRORS BY TYPE"))
            for error_type, count in sorted(stats.by_error_type.items(), key=lambda x: -x[1]):
                percentage = (count / stats.total) * 100
                bar = "█" * int(percentage / 5)
                click.echo(f"  {error_type:20s} {count:4d} ({percentage:5.1f}%) {bar}")
        else:
            print_info("No error data available")

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