// ABOUTME: OAuth provider extension — uses CLAUDE_CODE_OAUTH_TOKEN env var for Anthropic auth.
// ABOUTME: Supersedes the built-in OAuth flow so no browser login is needed. Set the env var and go.
/**
 * OAuth Provider — Environment-Variable-Based Anthropic Authentication
 *
 * Instead of using pi's built-in OAuth login flow (which requires opening a browser,
 * completing PKCE auth, and storing refresh/access tokens in auth.json), this extension
 * reads the `CLAUDE_CODE_OAUTH_TOKEN` (or `PI_CLAUDE_OAUTH_TOKEN`) environment variable
 * and uses it directly as the API credential.
 *
 * How it works:
 *   1. On load, checks for CLAUDE_CODE_OAUTH_TOKEN or PI_CLAUDE_OAUTH_TOKEN env var
 *   2. If found, registers an Anthropic provider override via pi.registerProvider()
 *   3. The override's getApiKey() returns the env var token directly
 *   4. No browser login, no token refresh, no auth.json management needed
 *
 * Commands:
 *   /auth-status   — Show which auth method is active and token presence
 *   /auth-logout   — Clear built-in OAuth credentials from auth.json (keeps env var auth)
 *   /auth-clear    — Alias for /auth-logout
 *
 * Environment Variables:
 *   CLAUDE_CODE_OAUTH_TOKEN  — Primary: Claude Code OAuth token (Claude Max Plan)
 *   PI_CLAUDE_OAUTH_TOKEN    — Alias: Pi-specific OAuth token variable
 *
 * Setup:
 *   1. Get your OAuth token from Claude Code or Anthropic Console
 *   2. Add to ~/.zshrc or ~/.bashrc:  export CLAUDE_CODE_OAUTH_TOKEN="your-token-here"
 *   3. Restart terminal and pi — done. No /login needed.
 *
 * Usage: Loaded via packages in agent/settings.json
 */

import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
import { readFileSync, writeFileSync, existsSync } from "node:fs";
import { join } from "node:path";

// ── Constants ────────────────────────────────────────────────────────

const ENV_PRIMARY = "CLAUDE_CODE_OAUTH_TOKEN";
const ENV_ALIAS = "PI_CLAUDE_OAUTH_TOKEN";
const PROVIDER_NAME = "anthropic";
const FAR_FUTURE_EXPIRY = Date.now() + 365 * 24 * 60 * 60 * 1000; // 1 year from now

// ── Helpers ──────────────────────────────────────────────────────────

/** Bridge our env vars to ANTHROPIC_OAUTH_TOKEN so the pi-ai library picks them up. */
export function bridgeOAuthEnvVar(): void {
	if (!process.env.ANTHROPIC_OAUTH_TOKEN) {
		const token = process.env[ENV_PRIMARY] || process.env[ENV_ALIAS];
		if (token) {
			process.env.ANTHROPIC_OAUTH_TOKEN = token;
		}
	}
}

function getOAuthToken(): string | undefined {
	return process.env[ENV_PRIMARY] || process.env[ENV_ALIAS];
}

function getTokenSource(): string | undefined {
	if (process.env[ENV_PRIMARY]) return ENV_PRIMARY;
	if (process.env[ENV_ALIAS]) return ENV_ALIAS;
	return undefined;
}

function getAuthJsonPath(): string {
	// auth.json lives in the agent directory (same level as extensions/)
	const agentDir = join(import.meta.dirname, "..");
	return join(agentDir, "auth.json");
}

function readAuthJson(): Record<string, unknown> | null {
	const path = getAuthJsonPath();
	if (!existsSync(path)) return null;
	try {
		return JSON.parse(readFileSync(path, "utf-8"));
	} catch {
		return null;
	}
}

function writeAuthJson(data: Record<string, unknown>): void {
	const path = getAuthJsonPath();
	writeFileSync(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
}

function maskToken(token: string): string {
	if (token.length <= 12) return "***";
	return token.slice(0, 8) + "..." + token.slice(-4);
}

// ── Extension Factory ────────────────────────────────────────────────

export default function oauthProvider(pi: ExtensionAPI): void {
	// Bridge env vars so the underlying pi-ai library sees ANTHROPIC_OAUTH_TOKEN
	bridgeOAuthEnvVar();

	const token = getOAuthToken();
	const source = getTokenSource();

	// ── Register Provider Override ─────────────────────────────────

	if (token) {
		pi.registerProvider(PROVIDER_NAME, {
			oauth: {
				name: "Anthropic (OAuth Env Var)",

				async login(_callbacks) {
					// No browser login needed — return synthetic credentials from env var
					const currentToken = getOAuthToken();
					if (!currentToken) {
						throw new Error(
							`OAuth token not found. Set ${ENV_PRIMARY} or ${ENV_ALIAS} in your environment.\n` +
							`Add to ~/.zshrc:  export ${ENV_PRIMARY}="your-token-here"`
						);
					}
					return {
						refresh: "env-var-managed",
						access: currentToken,
						expires: FAR_FUTURE_EXPIRY,
					};
				},

				async refreshToken(_credentials) {
					// Re-read from env var on "refresh" — picks up any changes
					const currentToken = getOAuthToken();
					if (!currentToken) {
						throw new Error(
							`OAuth token no longer available in environment. ` +
							`Set ${ENV_PRIMARY} or ${ENV_ALIAS} to continue.`
						);
					}
					return {
						refresh: "env-var-managed",
						access: currentToken,
						expires: FAR_FUTURE_EXPIRY,
					};
				},

				getApiKey(_credentials) {
					// Always return the live env var value (not the stored credential)
					const currentToken = getOAuthToken();
					if (!currentToken) {
						throw new Error(`OAuth token not found in environment. Set ${ENV_PRIMARY}.`);
					}
					return currentToken;
				},
			},
		});
	}

	// ── /auth-status Command ───────────────────────────────────────

	pi.registerCommand("auth-status", {
		description: "Show current authentication method and status",
		async handler(_args, ctx) {
			const currentToken = getOAuthToken();
			const currentSource = getTokenSource();
			const authData = readAuthJson();
			const hasAuthJsonEntry = authData && typeof authData[PROVIDER_NAME] === "object";

			const lines: string[] = [];
			lines.push("═══ Authentication Status ═══");
			lines.push("");

			if (currentToken) {
				lines.push(`✅ Env var auth ACTIVE`);
				lines.push(`   Source:  ${currentSource}`);
				lines.push(`   Token:   ${maskToken(currentToken)}`);
				lines.push(`   Method:  Environment variable (no login required)`);
			} else {
				lines.push(`⚠️  Env var auth NOT configured`);
				lines.push(`   Neither ${ENV_PRIMARY} nor ${ENV_ALIAS} is set.`);
				lines.push(`   Set one in your shell profile to enable env-var auth.`);
			}

			lines.push("");

			if (hasAuthJsonEntry) {
				const entry = authData[PROVIDER_NAME] as Record<string, unknown>;
				if (entry.type === "oauth") {
					const expires = typeof entry.expires === "number" ? entry.expires : 0;
					const isExpired = expires < Date.now();
					lines.push(`📄 auth.json entry: ${isExpired ? "EXPIRED" : "valid"}`);
					if (typeof entry.access === "string") {
						lines.push(`   Access:  ${maskToken(entry.access)}`);
					}
					lines.push(`   Expires: ${new Date(expires).toLocaleString()}`);
					if (currentToken) {
						lines.push(`   ℹ️  Env var takes priority over auth.json.`);
						lines.push(`   Run /auth-logout to clear auth.json entry.`);
					}
				} else if (entry.type === "api_key") {
					lines.push(`📄 auth.json entry: API key`);
				}
			} else {
				lines.push(`📄 auth.json: No ${PROVIDER_NAME} entry`);
			}

			lines.push("");
			lines.push("─────────────────────────────");

			ctx.ui.notify(lines.join("\n"), "info");
		},
	});

	// ── /auth-logout Command ───────────────────────────────────────

	pi.registerCommand("auth-logout", {
		description: "Clear built-in Anthropic OAuth credentials from auth.json",
		async handler(_args, ctx) {
			const authData = readAuthJson();

			if (!authData || !(PROVIDER_NAME in authData)) {
				ctx.ui.notify(
					`No ${PROVIDER_NAME} credentials found in auth.json. Nothing to clear.`,
					"info"
				);
				return;
			}

			const confirmed = await ctx.ui.confirm(
				"Clear Anthropic Credentials",
				`Remove the "${PROVIDER_NAME}" entry from auth.json?\n` +
				`This clears the built-in OAuth credentials.\n` +
				`${getOAuthToken() ? "Env var auth will continue to work." : "⚠️  No env var token set — you'll need to set one or /login again."}`
			);

			if (!confirmed) {
				ctx.ui.notify("Cancelled.", "info");
				return;
			}

			// Remove the anthropic entry
			const { [PROVIDER_NAME]: _removed, ...rest } = authData;
			writeAuthJson(rest);

			ctx.ui.notify(
				`✅ Cleared "${PROVIDER_NAME}" from auth.json.\n` +
				`${getOAuthToken() ? "Env var auth remains active." : "Set " + ENV_PRIMARY + " to continue using Claude."}`,
				"info"
			);
		},
	});

	// ── /auth-clear Alias ──────────────────────────────────────────

	pi.registerCommand("auth-clear", {
		description: "Alias for /auth-logout — clear built-in OAuth credentials",
		async handler(args, ctx) {
			// Delegate to auth-logout
			const commands = pi.getCommands();
			const logoutCmd = commands.find(c => c.name === "auth-logout");
			if (logoutCmd) {
				// Can't invoke commands directly, so duplicate the logic
				const authData = readAuthJson();

				if (!authData || !(PROVIDER_NAME in authData)) {
					ctx.ui.notify(
						`No ${PROVIDER_NAME} credentials found in auth.json. Nothing to clear.`,
						"info"
					);
					return;
				}

				const confirmed = await ctx.ui.confirm(
					"Clear Anthropic Credentials",
					`Remove the "${PROVIDER_NAME}" entry from auth.json?\n` +
					`This clears the built-in OAuth credentials.\n` +
					`${getOAuthToken() ? "Env var auth will continue to work." : "⚠️  No env var token set — you'll need to set one or /login again."}`
				);

				if (!confirmed) {
					ctx.ui.notify("Cancelled.", "info");
					return;
				}

				const { [PROVIDER_NAME]: _removed, ...rest } = authData;
				writeAuthJson(rest);

				ctx.ui.notify(
					`✅ Cleared "${PROVIDER_NAME}" from auth.json.\n` +
					`${getOAuthToken() ? "Env var auth remains active." : "Set " + ENV_PRIMARY + " to continue using Claude."}`,
					"info"
				);
			}
		},
	});
}
