import fs from "fs";
import os from "os";
import path from "path";
import type { ChatAuthMethod } from "@/lib/types";

export type CliProvider = "codex-cli" | "gemini-cli";

export interface ProviderAuthStatus {
  provider: CliProvider;
  method: ChatAuthMethod;
  connected: boolean;
  message: string;
  detail?: string;
}

export interface ProviderAuthConnectResult extends ProviderAuthStatus {
  started?: boolean;
  command?: string;
}

export interface ResolvedCliOAuthCredential {
  provider: CliProvider;
  accessToken: string;
  refreshToken?: string;
  expiresAt?: number;
  accountId?: string;
}

interface CodexAuthFile {
  auth_mode?: unknown;
  tokens?: {
    access_token?: unknown;
    refresh_token?: unknown;
    account_id?: unknown;
  };
  last_refresh?: unknown;
}

interface GeminiOauthCreds {
  access_token?: unknown;
  refresh_token?: unknown;
  expiry_date?: unknown;
}

function asNonEmptyString(value: unknown): string | null {
  if (typeof value !== "string") return null;
  const trimmed = value.trim();
  return trimmed ? trimmed : null;
}

function asEpochMs(value: unknown): number | null {
  if (typeof value === "number" && Number.isFinite(value)) {
    return value;
  }
  if (typeof value === "string" && value.trim()) {
    const parsed = Date.parse(value);
    if (Number.isFinite(parsed)) {
      return parsed;
    }
  }
  return null;
}

function readJsonObject(filePath: string): Record<string, unknown> | null {
  try {
    const raw = fs.readFileSync(filePath, "utf-8");
    const parsed = JSON.parse(raw) as unknown;
    if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
      return null;
    }
    return parsed as Record<string, unknown>;
  } catch {
    return null;
  }
}

function resolveAuthPath(envName: string, defaultPath: string): string {
  const envValue = process.env[envName];
  if (typeof envValue !== "string") {
    return defaultPath;
  }
  const trimmed = envValue.trim();
  return trimmed ? trimmed : defaultPath;
}

function readCodexAuth(): { path: string; parsed: CodexAuthFile | null } {
  const authPath = resolveAuthPath(
    "CODEX_AUTH_FILE",
    path.join(os.homedir(), ".codex", "auth.json")
  );
  const parsed = readJsonObject(authPath) as CodexAuthFile | null;
  return { path: authPath, parsed };
}

function resolveCodexCredential(): ResolvedCliOAuthCredential {
  const { parsed } = readCodexAuth();
  if (!parsed) {
    throw new Error("Codex OAuth file is missing. Run `codex login`.");
  }

  const authMode = asNonEmptyString(parsed.auth_mode)?.toLowerCase() || "";
  const accessToken = asNonEmptyString(parsed.tokens?.access_token);
  const refreshToken = asNonEmptyString(parsed.tokens?.refresh_token);
  const accountId = asNonEmptyString(parsed.tokens?.account_id) || undefined;

  if (authMode !== "chatgpt") {
    throw new Error("Codex CLI is not in OAuth mode (`auth_mode=chatgpt` required).");
  }
  if (!accessToken || !refreshToken) {
    throw new Error("Codex OAuth tokens are missing. Run `codex login`.");
  }

  return {
    provider: "codex-cli",
    accessToken,
    refreshToken,
    accountId,
  };
}

function checkCodexOauthStatus(): ProviderAuthStatus {
  const { path: authPath, parsed } = readCodexAuth();

  if (!parsed) {
    return {
      provider: "codex-cli",
      method: "oauth",
      connected: false,
      message: "Codex CLI OAuth token file was not found.",
      detail: `Expected: ${authPath}`,
    };
  }

  const authMode = asNonEmptyString(parsed.auth_mode)?.toLowerCase() || "";
  const accessToken = asNonEmptyString(parsed.tokens?.access_token);
  const refreshToken = asNonEmptyString(parsed.tokens?.refresh_token);
  const accountId = asNonEmptyString(parsed.tokens?.account_id);
  const lastRefresh = asEpochMs(parsed.last_refresh);

  if (authMode !== "chatgpt") {
    return {
      provider: "codex-cli",
      method: "oauth",
      connected: false,
      message: "Codex CLI is not in OAuth mode.",
      detail: authMode
        ? `auth_mode=${authMode}. Run \`codex login\` with ChatGPT.`
        : `auth_mode is missing in ${authPath}`,
    };
  }

  if (!accessToken || !refreshToken) {
    return {
      provider: "codex-cli",
      method: "oauth",
      connected: false,
      message: "Codex CLI OAuth tokens are missing.",
      detail: "Run `codex login` and complete browser authorization.",
    };
  }

  const detailParts: string[] = [];
  if (accountId) detailParts.push(`account_id=${accountId}`);
  if (lastRefresh) detailParts.push(`last_refresh=${new Date(lastRefresh).toISOString()}`);

  return {
    provider: "codex-cli",
    method: "oauth",
    connected: true,
    message: "Codex CLI OAuth is configured.",
    detail: detailParts.length > 0 ? detailParts.join("; ") : undefined,
  };
}

function readGeminiSettings(): { path: string; parsed: Record<string, unknown> | null } {
  const settingsPath = resolveAuthPath(
    "GEMINI_SETTINGS_FILE",
    path.join(os.homedir(), ".gemini", "settings.json")
  );
  return { path: settingsPath, parsed: readJsonObject(settingsPath) };
}

function readGeminiOauthCreds(): { path: string; parsed: GeminiOauthCreds | null } {
  const credsPath = resolveAuthPath(
    "GEMINI_OAUTH_CREDS_FILE",
    path.join(os.homedir(), ".gemini", "oauth_creds.json")
  );
  const parsed = readJsonObject(credsPath) as GeminiOauthCreds | null;
  return { path: credsPath, parsed };
}

function resolveGeminiCredential(): ResolvedCliOAuthCredential {
  const { parsed: creds } = readGeminiOauthCreds();
  const { path: settingsPath, parsed: settings } = readGeminiSettings();
  if (!creds) {
    throw new Error("Gemini OAuth file is missing. Run `gemini` and login with Google.");
  }

  const selectedType = (
    (settings?.security as Record<string, unknown> | undefined)?.auth as
      | Record<string, unknown>
      | undefined
  )?.selectedType;

  const selectedTypeValue =
    typeof selectedType === "string" ? selectedType.trim().toLowerCase() : "";
  const selectedOauth =
    selectedTypeValue === "oauth-personal" ||
    selectedTypeValue === "login_with_google" ||
    selectedTypeValue.startsWith("oauth");

  if (!selectedOauth) {
    throw new Error(
      `Gemini CLI is not in OAuth mode. Switch auth to OAuth in Gemini CLI (settings: ${settingsPath}).`
    );
  }

  const accessToken = asNonEmptyString(creds.access_token);
  const refreshToken = asNonEmptyString(creds.refresh_token) || undefined;
  const expiresAt = asEpochMs(creds.expiry_date) ?? undefined;
  const isExpired = typeof expiresAt === "number" && Date.now() >= expiresAt;

  if (!accessToken) {
    throw new Error("Gemini OAuth access token is missing. Re-login with `gemini`.");
  }

  if (isExpired) {
    throw new Error("Gemini OAuth access token is expired. Re-login with `gemini`.");
  }

  return {
    provider: "gemini-cli",
    accessToken,
    refreshToken,
    expiresAt,
  };
}

function checkGeminiOauthStatus(): ProviderAuthStatus {
  const { path: credsPath, parsed: creds } = readGeminiOauthCreds();
  const { path: settingsPath, parsed: settings } = readGeminiSettings();

  if (!creds) {
    return {
      provider: "gemini-cli",
      method: "oauth",
      connected: false,
      message: "Gemini CLI OAuth token file was not found.",
      detail: `Expected: ${credsPath}`,
    };
  }

  const selectedType = (
    (settings?.security as Record<string, unknown> | undefined)?.auth as
      | Record<string, unknown>
      | undefined
  )?.selectedType;

  const selectedTypeValue =
    typeof selectedType === "string" ? selectedType.trim().toLowerCase() : "";
  const selectedOauth =
    selectedTypeValue === "oauth-personal" ||
    selectedTypeValue === "login_with_google" ||
    selectedTypeValue.startsWith("oauth");

  const accessToken = asNonEmptyString(creds.access_token);
  const refreshToken = asNonEmptyString(creds.refresh_token);
  const expiresAt = asEpochMs(creds.expiry_date);
  const isExpired = typeof expiresAt === "number" && Date.now() >= expiresAt;

  if (!selectedOauth) {
    return {
      provider: "gemini-cli",
      method: "oauth",
      connected: false,
      message: "Gemini CLI is not in OAuth mode.",
      detail: selectedTypeValue
        ? `selectedType=${selectedTypeValue}; switch to OAuth in Gemini CLI`
        : `selectedType is missing in ${settingsPath}`,
    };
  }

  if (!accessToken && !refreshToken) {
    return {
      provider: "gemini-cli",
      method: "oauth",
      connected: false,
      message: "Gemini CLI OAuth tokens are missing.",
      detail: "Run `gemini` and complete Login with Google.",
    };
  }

  if (isExpired && !refreshToken) {
    return {
      provider: "gemini-cli",
      method: "oauth",
      connected: false,
      message: "Gemini OAuth token is expired and cannot be refreshed.",
      detail: "Run `gemini` and complete Login with Google again.",
    };
  }

  const detailParts: string[] = [];
  if (typeof expiresAt === "number") {
    detailParts.push(
      `expires_at=${new Date(expiresAt).toISOString()}${isExpired ? " (expired)" : ""}`
    );
  }
  if (refreshToken) {
    detailParts.push("refresh_token=present");
  }

  return {
    provider: "gemini-cli",
    method: "oauth",
    connected: !isExpired,
    message: isExpired
      ? "Gemini OAuth token is expired."
      : "Gemini CLI OAuth is configured.",
    detail: detailParts.length > 0 ? detailParts.join("; ") : undefined,
  };
}

export function resolveCliOAuthCredentialSync(
  provider: CliProvider
): ResolvedCliOAuthCredential {
  if (provider === "codex-cli") {
    return resolveCodexCredential();
  }
  return resolveGeminiCredential();
}

function unsupportedMethodStatus(provider: CliProvider, method: ChatAuthMethod): ProviderAuthStatus {
  return {
    provider,
    method,
    connected: false,
    message: "Only OAuth is supported for this CLI provider in Eggent.",
    detail:
      provider === "codex-cli"
        ? "Use provider OpenAI for API key mode, or Codex CLI with OAuth."
        : "Use provider Google for API key mode, or Gemini CLI with OAuth.",
  };
}

export async function checkProviderAuthStatus(input: {
  provider: CliProvider;
  method: ChatAuthMethod;
  hasApiKey?: boolean;
}): Promise<ProviderAuthStatus> {
  const { provider, method } = input;

  if (method !== "oauth") {
    return unsupportedMethodStatus(provider, method);
  }

  if (provider === "codex-cli") {
    return checkCodexOauthStatus();
  }
  return checkGeminiOauthStatus();
}

export async function connectProviderAuth(input: {
  provider: CliProvider;
  method: ChatAuthMethod;
  apiKey?: string;
}): Promise<ProviderAuthConnectResult> {
  const { provider, method } = input;

  if (method !== "oauth") {
    return {
      ...unsupportedMethodStatus(provider, method),
      started: false,
    };
  }

  if (provider === "codex-cli") {
    return {
      provider,
      method,
      connected: false,
      started: false,
      message: "OAuth must be completed in your terminal.",
      command: "codex login",
      detail:
        "Run `codex login`, complete browser authorization, then click Check connection.",
    };
  }

  return {
    provider,
    method,
    connected: false,
    started: false,
    message: "OAuth must be completed in your terminal.",
    command: "gemini",
    detail:
      "Run `gemini`, choose Login with Google, complete browser flow, then click Check connection.",
  };
}
