"""CLI commands for the result cache."""

from __future__ import annotations

from pathlib import Path

import click

from oclawma.cache import CacheConfig, CacheKey, ResultCache
from oclawma.cli_ui import (
    accent,
    header,
    key_value,
    print_error,
    print_info,
    print_success,
    subheader,
)

DEFAULT_CACHE_DB = Path.home() / ".oclawma" / "cache.db"


@click.group(name="cache")
@click.option(
    "--db-path",
    type=click.Path(),
    default=str(DEFAULT_CACHE_DB),
    help="Path to the cache database",
    show_default=True,
)
@click.pass_context
def cache_cli(ctx: click.Context, db_path: str) -> None:
    """Manage the job result cache.

    The result cache stores results of identical jobs to avoid reprocessing.
    Jobs are identified by their type and payload hash.

    Examples:
        oclawma cache stats              # Show cache statistics
        oclawma cache list               # List cached entries
        oclawma cache clear              # Clear all cached entries
        oclawma cache invalidate KEY     # Invalidate specific entry
    """
    ctx.ensure_object(dict)
    ctx.obj["db_path"] = db_path


@cache_cli.command(name="stats")
@click.pass_context
def cache_stats(ctx: click.Context) -> None:
    """Show cache statistics."""
    db_path = ctx.obj["db_path"]

    try:
        with ResultCache(CacheConfig(db_path=db_path)) as cache:
            stats = cache.get_stats()

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

        click.echo(subheader("ENTRIES"))
        click.echo(key_value("Total", str(stats.total_entries)))
        click.echo(key_value("Expired", str(stats.expired_entries)))
        click.echo()

        click.echo(subheader("PERFORMANCE"))
        click.echo(key_value("Hits", str(stats.hits)))
        click.echo(key_value("Misses", str(stats.misses)))
        hit_rate_pct = stats.hit_rate * 100
        color = "green" if hit_rate_pct > 50 else "yellow" if hit_rate_pct > 20 else "red"
        click.echo(key_value("Hit Rate", click.style(f"{hit_rate_pct:.1f}%", fg=color)))
        click.echo()

        click.echo(subheader("STORAGE"))
        size_kb = stats.size_bytes / 1024
        click.echo(key_value("Size", f"{size_kb:.2f} KB"))
        click.echo(key_value("Database", db_path))
        click.echo()

        if stats.total_entries == 0:
            print_info("Cache is empty. Jobs will be cached as they are processed.")

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


@cache_cli.command(name="list")
@click.option(
    "--limit",
    "-n",
    type=int,
    default=20,
    help="Maximum number of entries to show",
    show_default=True,
)
@click.option(
    "--job-type",
    "-t",
    help="Filter by job type",
)
@click.option(
    "--include-expired",
    is_flag=True,
    help="Include expired entries",
)
@click.pass_context
def cache_list(
    ctx: click.Context,
    limit: int,
    job_type: str | None,
    include_expired: bool,
) -> None:
    """List cached entries."""
    db_path = ctx.obj["db_path"]

    try:
        with ResultCache(CacheConfig(db_path=db_path)) as cache:
            entries = cache.list_entries(
                job_type=job_type,
                limit=limit,
                include_expired=include_expired,
            )

        if not entries:
            if job_type:
                print_info(f'No cached entries found for job type "{job_type}"')
            else:
                print_info("Cache is empty")
            return

        click.echo(header("CACHE ENTRIES", width=58))
        click.echo()

        for entry in entries:
            status = "EXPIRED" if entry.is_expired() else "ACTIVE"
            status_color = "red" if entry.is_expired() else "green"

            click.echo(f"{accent(str(entry.key), bold=True)}")
            click.echo(key_value("  Status", click.style(status, fg=status_color)))
            click.echo(key_value("  Created", entry.created_at.strftime("%Y-%m-%d %H:%M:%S")))

            if entry.expires_at:
                click.echo(key_value("  Expires", entry.expires_at.strftime("%Y-%m-%d %H:%M:%S")))

            click.echo(key_value("  Accessed", str(entry.access_count)))

            if entry.last_accessed:
                click.echo(
                    key_value("  Last Access", entry.last_accessed.strftime("%Y-%m-%d %H:%M:%S"))
                )

            # Show result preview (truncated)
            result_str = str(entry.result)
            if len(result_str) > 60:
                result_str = result_str[:57] + "..."
            click.echo(key_value("  Result", result_str))
            click.echo()

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


@cache_cli.command(name="get")
@click.argument("key_hash")
@click.option(
    "--job-type",
    "-t",
    default="default",
    help="Job type for the key",
    show_default=True,
)
@click.pass_context
def cache_get(ctx: click.Context, key_hash: str, job_type: str) -> None:
    """Get details of a specific cache entry."""
    db_path = ctx.obj["db_path"]

    try:
        with ResultCache(CacheConfig(db_path=db_path)) as cache:
            key = CacheKey(key_hash=key_hash, job_type=job_type)
            entry = cache.get_entry_info(key)

        if entry is None:
            print_error(f"Cache entry not found: {job_type}:{key_hash}")
            raise click.Abort()

        click.echo(header("CACHE ENTRY", width=58))
        click.echo()

        status = "EXPIRED" if entry.is_expired() else "ACTIVE"
        status_color = "red" if entry.is_expired() else "green"

        click.echo(key_value("Key", str(entry.key)))
        click.echo(key_value("Status", click.style(status, fg=status_color)))
        click.echo(key_value("Created", entry.created_at.strftime("%Y-%m-%d %H:%M:%S")))

        if entry.expires_at:
            click.echo(key_value("Expires", entry.expires_at.strftime("%Y-%m-%d %H:%M:%S")))

        click.echo(key_value("Access Count", str(entry.access_count)))

        if entry.last_accessed:
            click.echo(
                key_value("Last Accessed", entry.last_accessed.strftime("%Y-%m-%d %H:%M:%S"))
            )

        click.echo()
        click.echo(subheader("RESULT"))
        click.echo(entry.result)

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


@cache_cli.command(name="clear")
@click.confirmation_option(
    prompt="Are you sure you want to clear all cached entries?",
    help="Confirm clearing cache",
)
@click.pass_context
def cache_clear(ctx: click.Context) -> None:
    """Clear all cached entries."""
    db_path = ctx.obj["db_path"]

    try:
        with ResultCache(CacheConfig(db_path=db_path)) as cache:
            count = cache.clear()

        print_success(f"Cleared {count} entries from cache")

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


@cache_cli.command(name="invalidate")
@click.argument("pattern")
@click.option(
    "--job-type",
    "-t",
    help="Invalidate all entries for a specific job type",
)
@click.pass_context
def cache_invalidate(
    ctx: click.Context,
    pattern: str,
    job_type: str | None,
) -> None:
    """Invalidate cache entries by pattern or job type.

    If --job-type is specified, all entries for that job type are invalidated.
    Otherwise, the PATTERN argument is treated as a SQL LIKE pattern to match
    against cache keys.

    Examples:
        oclawma cache invalidate "%"               # Invalidate all (same as clear)
        oclawma cache invalidate "abc%"            # Invalidate keys starting with "abc"
        oclawma cache invalidate --job-type=email  # Invalidate all email jobs
    """
    db_path = ctx.obj["db_path"]

    try:
        with ResultCache(CacheConfig(db_path=db_path)) as cache:
            if job_type:
                count = cache.invalidate_by_job_type(job_type)
                print_success(f"Invalidated {count} entries for job type '{job_type}'")
            else:
                count = cache.invalidate_pattern(pattern)
                print_success(f"Invalidated {count} entries matching pattern '{pattern}'")

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


@cache_cli.command(name="cleanup")
@click.pass_context
def cache_cleanup(ctx: click.Context) -> None:
    """Remove expired entries from the cache."""
    db_path = ctx.obj["db_path"]

    try:
        with ResultCache(CacheConfig(db_path=db_path)) as cache:
            count = cache.cleanup_expired()

        if count == 0:
            print_info("No expired entries to clean up")
        else:
            print_success(f"Removed {count} expired entries")

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