#!/usr/bin/env python3
"""Small no-dependency Brain Dump todo CRUD helper."""

from __future__ import annotations

import argparse
import json
import os
import sys
import urllib.error
import urllib.request
from typing import Any

DEFAULT_API_BASE = "https://api.braindump.latentedge.io"
USER_AGENT = "brain-dump-api-skill/1.0"


def require_api_key(value: str | None) -> str:
    if not value:
        raise SystemExit("Missing API key. Set BRAINDUMP_API_KEY=bdk_... before running this helper.")
    if not value.startswith("bdk_"):
        raise SystemExit("BRAINDUMP_API_KEY must be a Brain Dump API key that starts with bdk_.")
    return value


def request_json(api_base: str, api_key: str, method: str, path: str, payload: dict[str, Any] | None = None) -> dict[str, Any]:
    url = f"{api_base.rstrip('/')}{path}"
    data = None
    headers = {
        "Accept": "application/json",
        "Authorization": f"Bearer {api_key}",
        "User-Agent": USER_AGENT,
    }
    if payload is not None:
        data = json.dumps(payload).encode("utf-8")
        headers["Content-Type"] = "application/json"

    request = urllib.request.Request(url, data=data, headers=headers, method=method)
    try:
        with urllib.request.urlopen(request, timeout=30) as response:
            response_body = response.read().decode("utf-8")
    except urllib.error.HTTPError as exc:
        response_body = exc.read().decode("utf-8", errors="replace")
        try:
            parsed = json.loads(response_body)
            formatted = json.dumps(parsed, indent=2, sort_keys=True)
        except json.JSONDecodeError:
            formatted = response_body or exc.reason
        raise SystemExit(f"Brain Dump API returned HTTP {exc.code}:\n{formatted}") from exc
    except urllib.error.URLError as exc:
        raise SystemExit(f"Failed to reach Brain Dump API: {exc.reason}") from exc

    if not response_body:
        return {}
    try:
        return json.loads(response_body)
    except json.JSONDecodeError as exc:
        raise SystemExit(f"Brain Dump API returned non-JSON response:\n{response_body}") from exc


def add_common(parser: argparse.ArgumentParser) -> None:
    parser.add_argument(
        "--api-base",
        default=os.environ.get("BRAINDUMP_API_BASE", DEFAULT_API_BASE),
        help=f"API base URL. Defaults to {DEFAULT_API_BASE} or BRAINDUMP_API_BASE.",
    )
    parser.add_argument(
        "--api-key",
        default=os.environ.get("BRAINDUMP_API_KEY") or os.environ.get("BDK_API_KEY"),
        help="Brain Dump API key. Defaults to BRAINDUMP_API_KEY or BDK_API_KEY.",
    )
    parser.add_argument("--summary", action="store_true", help="Print a readable summary instead of JSON.")


def priority(value: str) -> int:
    try:
        parsed = int(value)
    except ValueError as exc:
        raise argparse.ArgumentTypeError("priority must be an integer from 1 to 4") from exc
    if parsed < 1 or parsed > 4:
        raise argparse.ArgumentTypeError("priority must be an integer from 1 to 4")
    return parsed


def non_negative_int(value: str) -> int:
    try:
        parsed = int(value)
    except ValueError as exc:
        raise argparse.ArgumentTypeError("minutes must be a non-negative integer") from exc
    if parsed < 0:
        raise argparse.ArgumentTypeError("minutes must be a non-negative integer")
    return parsed


def print_todo(todo: dict[str, Any]) -> None:
    minutes = todo.get("estimatedDurationMinutes")
    duration = f", {minutes}m" if minutes is not None else ""
    print(f"[{todo.get('id')}] p{todo.get('priority')} {todo.get('category')}{duration} - {todo.get('text')}")


def print_summary(command: str, payload: dict[str, Any]) -> None:
    data = payload.get("data") if isinstance(payload, dict) else None
    if not isinstance(data, dict):
        print(json.dumps(payload, indent=2, sort_keys=True))
        return

    if command == "list":
        todos = data.get("todos", [])
        if not todos:
            print("No todos found.")
            return
        for todo in todos:
            if isinstance(todo, dict):
                print_todo(todo)
        return

    todo = data.get("todo")
    if isinstance(todo, dict):
        print_todo(todo)
        return

    print(json.dumps(payload, indent=2, sort_keys=True))


def build_parser() -> argparse.ArgumentParser:
    parser = argparse.ArgumentParser(description="Brain Dump todo CRUD helper")
    add_common(parser)
    subcommands = parser.add_subparsers(dest="command", required=True)

    subcommands.add_parser("list", help="List todos")

    create = subcommands.add_parser("create", help="Create a todo")
    create.add_argument("--text", required=True, help="Todo text")
    create.add_argument("--priority", type=priority, default=3, help="Priority 1-4. Defaults to 3.")
    create.add_argument("--category", default="general", help="Todo category. Defaults to general.")
    create.add_argument("--minutes", type=non_negative_int, help="Estimated duration in minutes")

    update = subcommands.add_parser("update", help="Update a todo")
    update.add_argument("todo_id", type=int, help="Todo ID")
    update.add_argument("--text", help="Replacement todo text")
    update.add_argument("--priority", type=priority, help="Priority 1-4")
    update.add_argument("--category", help="Replacement category")
    update.add_argument("--minutes", type=non_negative_int, help="Estimated duration in minutes")
    update.add_argument("--clear-minutes", action="store_true", help="Set estimated duration to null")

    delete = subcommands.add_parser("delete", help="Delete a todo")
    delete.add_argument("todo_id", type=int, help="Todo ID")

    return parser


def main() -> int:
    parser = build_parser()
    args = parser.parse_args()
    api_key = require_api_key(args.api_key)

    if args.command == "list":
        payload = request_json(args.api_base, api_key, "GET", "/api/v1/todos")
    elif args.command == "create":
        body: dict[str, Any] = {
            "text": args.text,
            "priority": args.priority,
            "category": args.category,
        }
        if args.minutes is not None:
            body["estimatedDurationMinutes"] = args.minutes
        payload = request_json(args.api_base, api_key, "POST", "/api/v1/todos", body)
    elif args.command == "update":
        body = {}
        if args.text is not None:
            body["text"] = args.text
        if args.priority is not None:
            body["priority"] = args.priority
        if args.category is not None:
            body["category"] = args.category
        if args.minutes is not None and args.clear_minutes:
            parser.error("Use either --minutes or --clear-minutes, not both.")
        if args.minutes is not None:
            body["estimatedDurationMinutes"] = args.minutes
        if args.clear_minutes:
            body["estimatedDurationMinutes"] = None
        if not body:
            parser.error("update requires at least one of --text, --priority, --category, --minutes, or --clear-minutes")
        payload = request_json(args.api_base, api_key, "PUT", f"/api/v1/todos/{args.todo_id}", body)
    elif args.command == "delete":
        payload = request_json(args.api_base, api_key, "DELETE", f"/api/v1/todos/{args.todo_id}")
    else:
        parser.error("unknown command")

    if args.summary:
        print_summary(args.command, payload)
    else:
        print(json.dumps(payload, indent=2, sort_keys=True))
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
