// src/core/oracle/auth.ts
// Load OAuth credentials from .auth/auth-profiles.json
//
// The auth-profiles.json format:
// {
//   "profiles": {
//     "<provider>:<profile>": {
//       "type": "oauth",
//       "provider": "<provider>",
//       "access": "<JWT bearer token>",
//       "refresh": "<refresh token>",
//       "expires": <unix ms timestamp>,
//       "accountId": "<uuid>"
//     }
//   },
//   "lastGood": { "<provider>": "<provider>:<profile>" }
// }
//
// Used to load OAuth bearer tokens from CLI-managed credential stores.

import * as fs from "fs";
import * as path from "path";

export interface AuthProfile {
  type: string;
  provider: string;
  access: string;
  refresh: string;
  expires: number;
  accountId: string;
}

export interface AuthProfiles {
  version: number;
  profiles: Record<string, AuthProfile>;
  lastGood: Record<string, string>;
}

/**
 * Search paths for auth-profiles.json (relative to cwd and common locations).
 */
function findAuthFile(): string | null {
  const candidates = [
    path.join(process.cwd(), ".auth", "auth-profiles.json"),
    path.join(process.cwd(), "..", ".auth", "auth-profiles.json"),
    // CLI tool default location
    path.join(process.env.HOME || process.env.USERPROFILE || "", ".openclaw", "agents", "main", "agent", "auth-profiles.json"),
  ];

  for (const candidate of candidates) {
    if (fs.existsSync(candidate)) {
      return candidate;
    }
  }
  return null;
}

/**
 * Load auth profiles from disk.
 */
export function loadAuthProfiles(): AuthProfiles | null {
  const filePath = findAuthFile();
  if (!filePath) return null;

  try {
    const content = fs.readFileSync(filePath, "utf8");
    return JSON.parse(content) as AuthProfiles;
  } catch {
    return null;
  }
}

/**
 * Get an OAuth bearer token from the first valid profile that hasn't expired.
 *
 * Optionally specify a provider name to filter by.
 * Returns null if no valid token is found.
 */
export function getAuthToken(provider?: string): string | null {
  const profiles = loadAuthProfiles();
  if (!profiles) return null;

  // If provider specified, use lastGood lookup
  if (provider) {
    const profileKey = profiles.lastGood[provider];
    const profile = profileKey ? profiles.profiles[profileKey] : null;

    if (profile && profile.access) {
      const now = Date.now();
      const bufferMs = 5 * 60 * 1000;
      if (profile.expires > now + bufferMs) {
        return profile.access;
      }
    }
    return null;
  }

  // No provider specified — find any valid token
  for (const providerName of Object.keys(profiles.lastGood)) {
    const profileKey = profiles.lastGood[providerName];
    const profile = profileKey ? profiles.profiles[profileKey] : null;

    if (profile && profile.access) {
      const now = Date.now();
      const bufferMs = 5 * 60 * 1000;
      if (profile.expires > now + bufferMs) {
        return profile.access;
      }
    }
  }

  return null;
}

/**
 * Get an API key from the environment.
 * Checks OMEGA_API_KEY first, then any *_API_KEY env var.
 */
export function getEnvApiKey(): string | null {
  if (process.env.OMEGA_API_KEY) return process.env.OMEGA_API_KEY;

  // Check all env vars for *_API_KEY pattern
  for (const [key, value] of Object.entries(process.env)) {
    if (key.endsWith("_API_KEY") && typeof value === "string" && value) {
      return value;
    }
  }

  return null;
}

/**
 * Verify auth-profiles.json is loadable and a valid token exists.
 *
 * Does NOT inject tokens into process.env (security: avoids leaking
 * JWT via crash dumps, child processes, or error serialization).
 * Use getAuthToken() to retrieve the token when needed.
 *
 * Returns true if a valid token was found.
 */
export function loadAuthIntoEnv(): boolean {
  return hasAuthToken();
}

/**
 * Check if we have a valid OAuth token available.
 */
export function hasAuthToken(): boolean {
  return getAuthToken() !== null;
}

// Legacy aliases for backward compatibility with bin/ code.
// The default provider key comes from OMEGA_AUTH_PROVIDER env var or "openai-codex".
// Note: auth-profiles.json uses "openai-codex" as the provider key, not "codex".
const _defaultAuthProvider = process.env.OMEGA_AUTH_PROVIDER || "openai-codex";
export const getCodexToken = (provider?: string) => getAuthToken(provider ?? _defaultAuthProvider);
export const hasCodexToken = () => getAuthToken(_defaultAuthProvider) !== null;

/**
 * Get token expiry info for a specific provider.
 */
export function getTokenExpiry(provider?: string): { hasToken: boolean; expiresAt: Date | null; isExpired: boolean } {
  const profiles = loadAuthProfiles();
  if (!profiles) {
    return { hasToken: false, expiresAt: null, isExpired: false };
  }

  // Find a matching profile
  const targetProvider = provider;
  let profile: AuthProfile | null = null;

  if (targetProvider) {
    const profileKey = profiles.lastGood[targetProvider];
    profile = profileKey ? profiles.profiles[profileKey] : null;
  } else {
    // Find first available profile
    for (const providerName of Object.keys(profiles.lastGood)) {
      const profileKey = profiles.lastGood[providerName];
      const p = profileKey ? profiles.profiles[profileKey] : null;
      if (p) { profile = p; break; }
    }
  }

  if (!profile) {
    return { hasToken: false, expiresAt: null, isExpired: false };
  }

  const expiresAt = new Date(profile.expires);
  const isExpired = Date.now() > profile.expires;

  return { hasToken: true, expiresAt, isExpired };
}
