"""MyBurn Auth — GitHub Device Flow login + config management."""
from __future__ import annotations

import json
import os
import sys
import time
import urllib.error
import urllib.parse
import urllib.request
import webbrowser
from pathlib import Path


CONFIG_DIR = Path.home() / ".myburn"
CONFIG_PATH = CONFIG_DIR / "config.json"

GITHUB_CLIENT_ID = "Ov23lieYjvqPnKUIyHrP"
MYBURN_API = os.environ.get("MYBURN_API", "https://myburn.cc")


# ---------------------------------------------------------------------------
# Config helpers
# ---------------------------------------------------------------------------
def load_config() -> dict:
    """Load ~/.myburn/config.json, return empty dict if missing."""
    if not CONFIG_PATH.exists():
        return {}
    try:
        return json.loads(CONFIG_PATH.read_text(encoding="utf-8"))
    except (json.JSONDecodeError, OSError):
        return {}


def save_config(cfg: dict) -> None:
    """Write config to ~/.myburn/config.json."""
    CONFIG_DIR.mkdir(parents=True, exist_ok=True)
    CONFIG_PATH.write_text(json.dumps(cfg, indent=2) + "\n", encoding="utf-8")
    try:
        CONFIG_PATH.chmod(0o600)
    except OSError:
        pass


def get_token() -> str | None:
    """Return the stored MyBurn API token, or None."""
    return load_config().get("token")


def get_username() -> str | None:
    """Return the stored username, or None."""
    return load_config().get("username")


# ---------------------------------------------------------------------------
# HTTP helpers (stdlib only — no httpx/requests)
# ---------------------------------------------------------------------------
_UA = "MyBurn-CLI/0.1"


def _post_json(url: str, data: dict, headers: dict | None = None) -> dict:
    """POST JSON, return parsed response."""
    body = json.dumps(data).encode()
    hdrs = {"Content-Type": "application/json", "Accept": "application/json", "User-Agent": _UA}
    if headers:
        hdrs.update(headers)
    req = urllib.request.Request(url, data=body, headers=hdrs, method="POST")
    with urllib.request.urlopen(req, timeout=30) as resp:
        return json.loads(resp.read())


def _post_form(url: str, data: dict) -> dict:
    """POST form-encoded, return parsed JSON response."""
    body = urllib.parse.urlencode(data).encode()
    hdrs = {"Accept": "application/json"}
    req = urllib.request.Request(url, data=body, headers=hdrs, method="POST")
    with urllib.request.urlopen(req, timeout=30) as resp:
        return json.loads(resp.read())


# ---------------------------------------------------------------------------
# GitHub Device Flow
# ---------------------------------------------------------------------------
def _device_flow() -> str:
    """Run GitHub Device Flow, return GitHub access token."""
    client_id = os.environ.get("GITHUB_CLIENT_ID", GITHUB_CLIENT_ID)
    if "PLACEHOLDER" in client_id:
        print("\n  Error: GitHub OAuth App not configured.")
        print("  Set GITHUB_CLIENT_ID env var or update auth.py.\n")
        sys.exit(1)

    # Step 1: Request device + user codes
    resp = _post_form("https://github.com/login/device/code", {
        "client_id": client_id,
        "scope": "read:user",
    })

    device_code = resp["device_code"]
    user_code = resp["user_code"]
    verification_uri = resp["verification_uri"]
    interval = resp.get("interval", 5)
    expires_in = resp.get("expires_in", 900)

    # Step 2: Show code, open browser
    print(f"\n  Open {verification_uri}")
    print(f"  Enter code: {user_code}\n")

    try:
        webbrowser.open(verification_uri)
    except Exception:
        pass  # headless / SSH — user copies URL manually

    # Step 3: Poll for token
    deadline = time.time() + expires_in
    while time.time() < deadline:
        time.sleep(interval)
        try:
            token_resp = _post_form("https://github.com/login/oauth/access_token", {
                "client_id": client_id,
                "device_code": device_code,
                "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
            })
        except urllib.error.HTTPError:
            continue

        if "access_token" in token_resp:
            return token_resp["access_token"]

        error = token_resp.get("error")
        if error == "authorization_pending":
            continue
        elif error == "slow_down":
            interval = token_resp.get("interval", interval + 5)
            continue
        elif error == "expired_token":
            print("  Code expired. Please try again.")
            sys.exit(1)
        elif error == "access_denied":
            print("  Authorization denied.")
            sys.exit(1)
        else:
            print(f"  Unexpected error: {error}")
            sys.exit(1)

    print("  Timed out waiting for authorization.")
    sys.exit(1)


# ---------------------------------------------------------------------------
# Login / Logout
# ---------------------------------------------------------------------------
def login() -> None:
    """Full login flow: GitHub Device Flow → exchange token → save config."""
    existing = get_token()
    if existing:
        username = get_username() or "unknown"
        print(f"\n  Already logged in as @{username}")
        print("  Run 'myburn logout' first to switch accounts.\n")
        return

    print("\n  Logging in via GitHub...")
    gh_token = _device_flow()

    # Exchange GitHub token for MyBurn API token
    print("  Authenticating with myburn.cc...")
    try:
        result = _post_json(f"{MYBURN_API}/api/auth/github", {
            "github_token": gh_token,
        })
    except urllib.error.HTTPError as e:
        body = e.read().decode() if hasattr(e, "read") else str(e)
        print(f"  Auth failed: {body}")
        sys.exit(1)

    from datetime import datetime, timezone
    save_config({
        "token": result["token"],
        "username": result["username"],
        "avatar_url": result.get("avatar_url", ""),
        "logged_in_at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
    })

    print(f"  Logged in as @{result['username']}\n")


def logout() -> None:
    """Clear stored credentials and revoke token server-side."""
    cfg = load_config()
    username = cfg.get("username", "unknown")
    token = cfg.get("token")

    if not token:
        print("\n  Not logged in.\n")
        return

    # Try to revoke server-side (best-effort — server may be unreachable)
    try:
        _post_json(f"{MYBURN_API}/api/auth/revoke", {},
                    headers={"Authorization": f"Bearer {token}"})
    except Exception:
        pass

    # Keep config file but clear auth fields
    cfg.pop("token", None)
    cfg.pop("username", None)
    cfg.pop("avatar_url", None)
    cfg.pop("logged_in_at", None)
    save_config(cfg)

    print(f"\n  Logged out (@{username}).\n")
