"""Pre-configure Claude Code (the CLI) so a fresh ``claude`` is authenticated with the
Anthropic API key — **no /login, no approval prompt** — by two independent mechanisms:

1. **apiKeyHelper (the robust one).** We register an ``apiKeyHelper`` in Claude Code's user
   ``~/.claude/settings.json`` pointing at a tiny script that echoes ``ANTHROPIC_API_KEY``
   from the off-repo env file. Claude Code runs that helper **itself** (a subprocess), so
   authentication does **not** depend on the interactive shell having exported the key — this
   is what makes a GUI/AppleScript-launched ``claude`` come up logged-in even when the Terminal
   never sourced ~/.zshrc / ~/.bash_profile. It sits above OAuth in Claude Code's auth
   precedence and needs no approval entry.

2. **Env-key approval (belt-and-suspenders).** For the case where ``ANTHROPIC_API_KEY`` *is*
   in the environment, we also record its approval in ``~/.claude.json`` under
   ``customApiKeyResponses.approved`` (keyed by the **last 20 characters**) so that path
   doesn't show the "Detected a custom API key" prompt either.

Both writes are **additive** (never remove an existing login/approvals, never write anyone's
identity), atomic (tempfile + ``os.replace``), and left ``chmod 600``/``700``.

Invoked by the installer as::

    <venv>/bin/python -m scripts.claude_auth <env_file> <claude_json> [<settings_json>]

The 3rd arg (``~/.claude/settings.json``) enables the apiKeyHelper; omit it for the approval
only. Exits 0 if either mechanism was configured, non-zero otherwise (so the installer warns).
"""
from __future__ import annotations

import json
import os
import sys
import tempfile


def read_key(env_file: str):
    """Return the ANTHROPIC_API_KEY value from a KEY=VALUE env file, or None."""
    try:
        with open(env_file, encoding="utf-8") as f:
            for line in f:
                if line.startswith("ANTHROPIC_API_KEY="):
                    return line.split("=", 1)[1].strip() or None
    except OSError:
        pass
    return None


def preapprove(env_file: str, claude_json: str) -> bool:
    """Additively approve the key in ``claude_json``. Returns True iff written."""
    key = read_key(env_file)
    if not key:
        return False

    data = {}
    if os.path.exists(claude_json):
        try:
            with open(claude_json, encoding="utf-8") as f:
                data = json.load(f)
        except (OSError, ValueError):
            data = {}
    if not isinstance(data, dict):
        data = {}

    resp = data.get("customApiKeyResponses")
    if not isinstance(resp, dict):
        resp = {}
    approved = resp.get("approved")
    if not isinstance(approved, list):
        approved = []
    suffix = key[-20:]
    if suffix not in approved:
        approved.append(suffix)
    resp["approved"] = approved
    if not isinstance(resp.get("rejected"), list):
        resp["rejected"] = []
    data["customApiKeyResponses"] = resp
    data["hasCompletedOnboarding"] = True
    data.setdefault("theme", "dark")

    d = os.path.dirname(claude_json) or "."
    os.makedirs(d, exist_ok=True)
    fd, tmp = tempfile.mkstemp(dir=d, prefix=".claude.json.")
    try:
        with os.fdopen(fd, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2)
        os.replace(tmp, claude_json)
        os.chmod(claude_json, 0o600)
    except OSError:
        try:
            os.unlink(tmp)
        except OSError:
            pass
        return False
    return True


def _helper_body(env_file: str) -> str:
    """A tiny ``sh`` script whose stdout is ``ANTHROPIC_API_KEY`` from the off-repo env file.
    Referenced by settings.json ``apiKeyHelper`` and run by Claude Code as a subprocess — so
    auth is shell-independent. Prints nothing if the key is unset (Claude Code then falls
    through to its next auth method). Uses a ``$HOME``-relative path so it survives a home move."""
    home = os.path.expanduser("~")
    if env_file == home:
        ref = '"$HOME"'
    elif env_file.startswith(home + os.sep):
        ref = '"$HOME/' + env_file[len(home) + 1:] + '"'
    else:
        ref = '"' + env_file + '"'
    return (
        "#!/bin/sh\n"
        "# Alexandria — feeds Claude Code the Anthropic API key (settings.json apiKeyHelper).\n"
        "# Run by Claude Code as a subprocess, so authentication is independent of the shell env.\n"
        "env_file=" + ref + "\n"
        "[ -r \"$env_file\" ] && grep -m1 '^ANTHROPIC_API_KEY=' \"$env_file\" | cut -d= -f2-\n"
    )


def _atomic_write(path: str, text: str, mode: int) -> bool:
    """Atomically write ``text`` → ``path`` (tempfile + ``os.replace`` in the same dir), chmod ``mode``."""
    d = os.path.dirname(path) or "."
    try:
        os.makedirs(d, exist_ok=True)
        fd, tmp = tempfile.mkstemp(dir=d, prefix=".alx-tmp.")
    except OSError:
        return False
    try:
        with os.fdopen(fd, "w", encoding="utf-8") as f:
            f.write(text)
        os.replace(tmp, path)
        os.chmod(path, mode)
    except OSError:
        try:
            os.unlink(tmp)
        except OSError:
            pass
        return False
    return True


def configure_api_key_helper(env_file: str, settings_json: str, helper_path: str = "") -> bool:
    """Register a shell-independent ``apiKeyHelper`` in Claude Code's user ``settings.json`` so a
    freshly-launched ``claude`` authenticates via the API key with no prompt and no reliance on
    the interactive shell. Writes an executable helper (default: a **space-free** path next to
    settings.json, e.g. ``~/.claude/alexandria-apikey-helper.sh`` — avoids quoting issues) that
    echoes the key from ``env_file``, then merges ``apiKeyHelper`` into settings.json
    **additively** (preserving any other settings). Returns True iff both were written."""
    if not read_key(env_file):          # no key yet → nothing to authenticate with (noop)
        return False
    if not helper_path:
        helper_path = os.path.join(os.path.dirname(settings_json) or ".", "alexandria-apikey-helper.sh")
    if not _atomic_write(helper_path, _helper_body(env_file), 0o700):
        return False

    data = {}
    if os.path.exists(settings_json):
        try:
            with open(settings_json, encoding="utf-8") as f:
                data = json.load(f)
        except (OSError, ValueError):
            data = {}
    if not isinstance(data, dict):
        data = {}
    data["apiKeyHelper"] = helper_path
    return _atomic_write(settings_json, json.dumps(data, indent=2), 0o600)


def main(argv=None) -> int:
    argv = list(sys.argv[1:] if argv is None else argv)
    if len(argv) not in (2, 3):
        print("usage: python -m scripts.claude_auth <env_file> <claude_json> [<settings_json>]",
              file=sys.stderr)
        return 2
    approved = preapprove(argv[0], argv[1])                      # env-key approval (~/.claude.json)
    helper_ok = configure_api_key_helper(argv[0], argv[2]) if len(argv) == 3 else False
    return 0 if (approved or helper_ok) else 1                   # success if either path configured


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