import json
import time
from datetime import datetime, timezone

import click

from claude_meter.api import FetchError, fetch_usage, get_access_token, get_extra_usage_enabled

METER_WIDTH = 30


def format_reset_time(resets_at: str | None) -> str:
    """Format the reset timestamp as a human-readable relative time."""
    if not resets_at:
        return "no reset scheduled"

    try:
        reset_dt = datetime.fromisoformat(resets_at)
        now = datetime.now(timezone.utc)
        diff = reset_dt - now
        total_seconds = diff.total_seconds()

        if total_seconds <= 0:
            return "resetting now"

        total_minutes = int(total_seconds / 60)
        days, remainder = divmod(total_minutes, 1440)
        hours, minutes = divmod(remainder, 60)

        if days > 0:
            return f"resets in {days}d {hours}h"
        if hours > 0:
            return f"resets in {hours}h {minutes}m"
        return f"resets in {minutes}m"
    except (ValueError, TypeError):
        return "unknown reset time"


def bar_color(pct: float) -> str:
    """Return a click color name based on utilization percentage."""
    if pct >= 90:
        return "red"
    if pct >= 70:
        return "yellow"
    return "green"


def render_meter(utilization: float, width: int = METER_WIDTH, color: bool = True) -> str:
    """Render an ASCII progress bar like [========>         ] 27.0%"""
    pct = max(0.0, utilization)
    overflow = pct > 100.0
    display_pct = pct
    fill_pct = min(100.0, pct)
    filled = int(width * fill_pct / 100)

    if filled == 0:
        inner = " " * width
    elif overflow:
        inner = "=" * (width - 1) + "!"
    elif filled >= width:
        inner = "=" * width
    else:
        inner = "=" * (filled - 1) + ">" + " " * (width - filled)

    suffix = ""
    if color:
        if overflow:
            inner = click.style(inner, fg="red", bold=True)
            pct_text = click.style(f"{display_pct:5.1f}%", fg="red", bold=True)
            suffix = " " + click.style("EXTRA USAGE", fg="red", bold=True)
        else:
            c = bar_color(pct)
            inner = click.style(inner, fg=c)
            pct_text = click.style(f"{display_pct:5.1f}%", fg=c, bold=pct >= 90)
    else:
        pct_text = f"{display_pct:5.1f}%"
        if overflow:
            suffix = " EXTRA USAGE"

    return f"[{inner}] {pct_text}{suffix}"


def render_display(limits: list[dict], color: bool = True,
                    extra_usage_enabled: bool | None = None) -> str:
    """Render the full usage display."""
    if not limits:
        return "No usage data available."

    lines = []
    title = click.style("⚡ Claude Meter", bold=True) if color else "Claude Meter"
    sep = click.style("-" * 50, dim=True) if color else "-" * 50
    lines.append(title)

    if extra_usage_enabled is not None:
        if extra_usage_enabled:
            status = click.style("Extra usage: enabled", fg="green") if color else "Extra usage: enabled"
        else:
            status = click.style("Extra usage: disabled", dim=True) if color else "Extra usage: disabled"
        lines.append(status)

    lines.append(sep)

    for limit in limits:
        name = limit["name"]
        utilization = limit["utilization"]
        reset_str = format_reset_time(limit["resets_at"])
        meter = render_meter(utilization, color=color)

        if color:
            lines.append(f"  📊 {click.style(name, bold=True)}")
            lines.append(f"    {meter}  (🔄 {reset_str})")
        else:
            lines.append(f"  {name}")
            lines.append(f"    {meter}  ({reset_str})")
        lines.append("")

    return "\n".join(lines)


def filter_limits(limits: list[dict], text: str) -> list[dict]:
    """Return limits whose name contains text (case-insensitive)."""
    text_lower = text.lower()
    return [l for l in limits if text_lower in l["name"].lower()]


def sort_limits(limits: list[dict]) -> list[dict]:
    """Return limits sorted by utilization descending."""
    return sorted(limits, key=lambda l: l["utilization"], reverse=True)


@click.command()
@click.option(
    "--update-period",
    default=30,
    type=int,
    help="Refresh interval in seconds. Use 0 to display once and exit.",
)
@click.option("--no-color", is_flag=True, help="Disable colors and emojis.")
@click.option("--filter", "filter_text", type=str, default=None,
              help="Show only limits whose name contains this text (case-insensitive).")
@click.option("--sort", "sort_by_usage", is_flag=True,
              help="Sort limits by utilization descending.")
@click.option("--json", "output_json", is_flag=True,
              help="Output limits as JSON.")
@click.version_option(package_name="claude-meter")
def main(update_period: int, no_color: bool, filter_text: str | None,
         sort_by_usage: bool, output_json: bool):
    """Display Claude.ai plan usage as ASCII meters.

    Credentials are read from ~/.claude/.credentials.json (created by Claude
    Code) or from the CLAUDE_CODE_OAUTH_TOKEN environment variable.
    """
    token = get_access_token()
    extra_usage = get_extra_usage_enabled()
    color = not no_color and not output_json

    if update_period == 0:
        try:
            limits = fetch_usage(token)
        except FetchError as e:
            raise SystemExit(f"Error: {e}")
        if filter_text:
            limits = filter_limits(limits, filter_text)
        if sort_by_usage:
            limits = sort_limits(limits)
        if output_json:
            click.echo(json.dumps(limits, indent=2))
        else:
            click.echo(render_display(limits, color=color, extra_usage_enabled=extra_usage))
        return

    prev_lines = 0
    try:
        while True:
            try:
                limits = fetch_usage(token)
            except FetchError as e:
                warning = click.style(f"⚠ Network error: {e} — retrying…",
                                      fg="yellow") if color else f"Network error: {e} — retrying…"
                click.echo(warning)
                time.sleep(update_period)
                continue
            if filter_text:
                limits = filter_limits(limits, filter_text)
            if sort_by_usage:
                limits = sort_limits(limits)
            if output_json:
                click.echo(json.dumps(limits, indent=2))
            else:
                output = render_display(limits, color=color, extra_usage_enabled=extra_usage)
                if prev_lines > 0:
                    click.echo(f"\033[{prev_lines}A\033[J", nl=False)
                click.echo(output)
                prev_lines = output.count("\n") + 1
            time.sleep(update_period)
    except KeyboardInterrupt:
        click.echo("\nDone.")
