import argparse
import json
import time

from rich.console import Console
from rich.table import Table

from taskr import storage, telemetry

console = Console()


def main():
    parser = argparse.ArgumentParser(
        prog="taskr",
        description="A simple task manager CLI",
    )
    subparsers = parser.add_subparsers(dest="command")

    # taskr add <task>
    add_parser = subparsers.add_parser("add", help="Add a new task")
    add_parser.add_argument("task", help="Task description")

    # taskr list [--pending | --done]
    list_parser = subparsers.add_parser("list", help="List tasks")
    filter_group = list_parser.add_mutually_exclusive_group()
    filter_group.add_argument("--pending", action="store_true", help="Show only pending tasks")
    filter_group.add_argument("--done", action="store_true", help="Show only completed tasks")

    # taskr done <id>
    done_parser = subparsers.add_parser("done", help="Mark a task as done")
    done_parser.add_argument("number", type=int, help="Task id to mark done")

    # taskr delete <id>
    delete_parser = subparsers.add_parser("delete", help="Delete a task")
    delete_parser.add_argument("number", type=int, help="Task id to delete")

    # taskr stats
    subparsers.add_parser("stats", help="Show usage metrics")

    # taskr log [--last N] [--command CMD] [--outcome OUTCOME]
    log_parser = subparsers.add_parser("log", help="Show recent telemetry log entries")
    log_parser.add_argument("--last", type=int, default=20, metavar="N",
                            help="Number of entries to show (default: 20)")
    log_parser.add_argument("--command", dest="cmd_filter", metavar="CMD",
                            help="Filter by command name (add, list, done, delete)")
    log_parser.add_argument("--outcome", metavar="OUTCOME",
                            help="Filter by outcome (ok, not_found)")

    args = parser.parse_args()

    if args.command == "add":
        start = time.perf_counter()
        task = storage.add_task(args.task)
        duration_ms = (time.perf_counter() - start) * 1000
        console.print(f"[green]Added:[/green] {task['text']} [dim](#{task['id']})[/dim]")
        telemetry.record("add", "ok", duration_ms, task_id=task["id"])

    elif args.command == "list":
        start = time.perf_counter()
        tasks = storage.get_tasks()
        if args.pending:
            tasks = [t for t in tasks if not t["done"]]
        elif args.done:
            tasks = [t for t in tasks if t["done"]]
        duration_ms = (time.perf_counter() - start) * 1000

        if not tasks:
            console.print("[dim]No tasks.[/dim]")
        else:
            for task in tasks:
                if task["done"]:
                    console.print(
                        f"[green]  \\[x] {task['id']}.[/green]"
                        f" [dim strike]{task['text']}[/dim strike]"
                    )
                else:
                    console.print(f"  [ ] {task['id']}. {task['text']}")

        telemetry.record("list", "ok", duration_ms, count=len(tasks))

    elif args.command == "done":
        start = time.perf_counter()
        task = storage.mark_done(args.number)
        duration_ms = (time.perf_counter() - start) * 1000
        if task:
            console.print(f"[green]Done:[/green] {task['text']}")
            telemetry.record("done", "ok", duration_ms, task_id=task["id"])
        else:
            console.print(f"[red]No task with id {args.number}.[/red]")
            telemetry.record("done", "not_found", duration_ms, task_id=args.number)

    elif args.command == "delete":
        start = time.perf_counter()
        task = storage.delete_task(args.number)
        duration_ms = (time.perf_counter() - start) * 1000
        if task:
            console.print(f"[red]Deleted:[/red] {task['text']}")
            telemetry.record("delete", "ok", duration_ms, task_id=task["id"])
        else:
            console.print(f"[red]No task with id {args.number}.[/red]")
            telemetry.record("delete", "not_found", duration_ms, task_id=args.number)

    elif args.command == "stats":
        metrics = telemetry.load_metrics()

        cmd_table = Table(title="Command Usage", show_header=True, header_style="bold")
        cmd_table.add_column("Command")
        cmd_table.add_column("Calls", justify="right")
        for cmd, data in sorted(metrics["commands"].items()):
            count = data["count"] if isinstance(data, dict) else data
            cmd_table.add_row(cmd, str(count))
        console.print(cmd_table)

        task_table = Table(title="Task Totals", show_header=True, header_style="bold")
        task_table.add_column("Metric")
        task_table.add_column("Count", justify="right")
        for key, count in metrics["tasks"].items():
            task_table.add_row(key, str(count))
        console.print(task_table)

        stats = telemetry.command_stats()
        if stats:
            dur_table = Table(title="Duration (ms)", header_style="bold cyan")
            dur_table.add_column("Command", style="bold")
            dur_table.add_column("Min", justify="right")
            dur_table.add_column("Mean", justify="right")
            dur_table.add_column("p95", justify="right")
            dur_table.add_column("p99", justify="right")
            dur_table.add_column("Max", justify="right")

            for cmd_name, s in sorted(stats.items()):
                mean = s["mean_ms"]
                p95_str = str(s["p95_ms"])
                p99_str = str(s["p99_ms"])
                max_str = str(s["max_ms"])
                # Flag outliers: p95 or p99 significantly above mean
                if mean > 0 and s["p95_ms"] > 3 * mean:
                    p95_str = f"[yellow]{s['p95_ms']}[/yellow]"
                if mean > 0 and s["p99_ms"] > 5 * mean:
                    p99_str = f"[red bold]{s['p99_ms']}[/red bold]"
                if mean > 0 and s["max_ms"] > 10 * mean:
                    max_str = f"[red bold]{s['max_ms']}[/red bold]"

                dur_table.add_row(
                    cmd_name,
                    str(s["min_ms"]),
                    str(mean),
                    p95_str,
                    p99_str,
                    max_str,
                )
            console.print(dur_table)

            for cmd_name in sorted(stats.keys()):
                buckets = telemetry.duration_buckets(cmd_name)
                if not buckets:
                    continue
                max_count = max(b["count"] for b in buckets)
                if max_count == 0:
                    continue
                bar_width = 28
                total = stats[cmd_name]["count"]
                console.print(
                    f"\n  [bold]{cmd_name}[/bold]"
                    f" [dim]distribution ({total} samples)[/dim]"
                )
                for b in buckets:
                    filled = round(bar_width * b["count"] / max_count)
                    padding = " " * (bar_width - filled)
                    label = f"{b['lo']:5.1f}-{b['hi']:5.1f}ms"
                    count_str = "[dim]0[/dim]" if b["count"] == 0 else str(b["count"])
                    console.print(
                        f"  {label} | [cyan]{'#' * filled}[/cyan]{padding} {count_str}"
                    )

    elif args.command == "log":
        log_file = telemetry.LOG_FILE
        if not log_file.exists():
            console.print("[dim]No log entries yet. Run some taskr commands first.[/dim]")
            return

        entries = [json.loads(line) for line in log_file.read_text().splitlines() if line.strip()]

        if args.cmd_filter:
            entries = [e for e in entries if e.get("command") == args.cmd_filter]
        if args.outcome:
            entries = [e for e in entries if e.get("outcome") == args.outcome]

        entries = entries[-args.last:]

        if not entries:
            console.print("[dim]No matching log entries.[/dim]")
            return

        table = Table(title=f"taskr log (last {len(entries)})", header_style="bold")
        table.add_column("Timestamp", style="dim", no_wrap=True)
        table.add_column("Command", style="bold cyan")
        table.add_column("Outcome")
        table.add_column("Duration (ms)", justify="right")
        table.add_column("Extra", style="dim")

        for e in entries:
            ts = e.get("ts", "")[:19].replace("T", " ")
            cmd = e.get("command", "")
            outcome = e.get("outcome", "")
            outcome_str = f"[green]{outcome}[/green]" if outcome == "ok" else f"[red]{outcome}[/red]"
            dur = str(e.get("duration_ms", ""))
            extra_keys = {k: v for k, v in e.items()
                          if k not in ("ts", "command", "outcome", "duration_ms")}
            extra_str = "  ".join(f"{k}={v}" for k, v in extra_keys.items())
            table.add_row(ts, cmd, outcome_str, dur, extra_str)

        console.print(table)

    else:
        parser.print_help()


if __name__ == "__main__":
    main()
