import type { CodexAuth, CodexUsageResponse } from "@/types/codex";

const USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
const RESET_CREDITS_URL =
  "https://chatgpt.com/backend-api/wham/rate-limit-reset-credits";

interface RawUsageWindow {
  used_percent?: number;
  limit_window_seconds?: number;
  reset_at?: number;
}

interface RawUsageResponse {
  plan_type?: string | null;
  rate_limit?: {
    primary_window?: RawUsageWindow | null;
    secondary_window?: RawUsageWindow | null;
  } | null;
  credits?: {
    has_credits?: boolean;
    unlimited?: boolean;
    balance?: string | number;
  } | null;
  rate_limit_reached_type?: string | null;
  rate_limit_reset_credits?: {
    available_count?: number | null;
  } | null;
}

interface RawResetCredit {
  status?: string;
  granted_at?: string | null;
  expires_at?: string | null;
}

interface RawResetCreditsResponse {
  available_count?: number | null;
  total_earned_count?: number | null;
  credits?: RawResetCredit[] | null;
}

function isNumber(value: unknown): value is number {
  return typeof value === "number" && Number.isFinite(value);
}

function formatDurationUntil(isoString: string | null | undefined): string | null {
  if (!isoString) return null;

  const targetMs = new Date(isoString).getTime();
  if (Number.isNaN(targetMs)) return null;

  let remainingSeconds = Math.max(Math.round((targetMs - Date.now()) / 1000), 0);
  const days = Math.floor(remainingSeconds / 86400);
  remainingSeconds -= days * 86400;
  const hours = Math.floor(remainingSeconds / 3600);
  remainingSeconds -= hours * 3600;
  const minutes = Math.floor(remainingSeconds / 60);
  remainingSeconds -= minutes * 60;

  const parts: string[] = [];
  if (days > 0) parts.push(`${days}d`);
  if (hours > 0) parts.push(`${hours}h`);
  if (minutes > 0) parts.push(`${minutes}m`);
  if (remainingSeconds > 0 || parts.length === 0) parts.push(`${remainingSeconds}s`);
  return parts.join(" ");
}

function normalizeWindow(window: RawUsageWindow | null | undefined) {
  if (!window) return null;

  const windowSeconds =
    typeof window.limit_window_seconds === "number" ? window.limit_window_seconds : 0;
  const resetsAt = typeof window.reset_at === "number" ? window.reset_at : 0;

  return {
    usedPercent: typeof window.used_percent === "number" ? window.used_percent : 0,
    resetsAt,
    windowMinutes: Math.round(windowSeconds / 60),
    windowSeconds,
  };
}

function buildBaseHeaders(auth: CodexAuth): Record<string, string> {
  const headers: Record<string, string> = {
    Authorization: `Bearer ${auth.accessToken}`,
    "Content-Type": "application/json",
  };

  if (auth.accountId) {
    headers["ChatGPT-Account-Id"] = auth.accountId;
  }

  return headers;
}

async function fetchResetCoupons(auth: CodexAuth, usage: RawUsageResponse) {
  const headers = {
    ...buildBaseHeaders(auth),
    "OpenAI-Beta": "codex-1",
    originator: "Codex Desktop",
  };

  try {
    const response = await fetch(RESET_CREDITS_URL, { headers });
    if (!response.ok) {
      throw new Error(`HTTP_ERROR_${response.status}`);
    }

    const payload = (await response.json()) as RawResetCreditsResponse;
    const credits = Array.isArray(payload.credits) ? payload.credits : [];
    const normalizedCredits = credits
      .map((credit, index) => ({
        index: index + 1,
        status: credit.status,
        grantedAt: credit.granted_at ?? null,
        grantedAtLocal: credit.granted_at
          ? new Date(credit.granted_at).toLocaleString()
          : null,
        expiresAt: credit.expires_at ?? null,
        expiresAtLocal: credit.expires_at
          ? new Date(credit.expires_at).toLocaleString()
          : null,
        timeUntilExpiry: formatDurationUntil(credit.expires_at),
      }))
      .sort((a, b) => {
        const left = a.expiresAt ? new Date(a.expiresAt).getTime() : Number.MAX_SAFE_INTEGER;
        const right = b.expiresAt ? new Date(b.expiresAt).getTime() : Number.MAX_SAFE_INTEGER;
        return left - right;
      });

    const availableCredits = normalizedCredits.filter(
      (credit) => credit.status === "available"
    );

    return {
      source: "live_api" as const,
      sourceDescription: "Live Codex reset-credit endpoint",
      availableCount:
        payload.available_count ?? usage.rate_limit_reset_credits?.available_count ?? null,
      totalEarnedCount: payload.total_earned_count ?? null,
      credits: normalizedCredits,
      nextExpiringCredit: availableCredits[0] ?? normalizedCredits[0] ?? null,
    };
  } catch (error) {
    const message = error instanceof Error ? error.message : "UNKNOWN_ERROR";
    return {
      source: "unavailable" as const,
      sourceDescription: "Reset-credit endpoint unavailable",
      availableCount: usage.rate_limit_reset_credits?.available_count ?? null,
      fallbackReason: message,
      error: message,
    };
  }
}

export function parseCodexUsageResponse(data: unknown): CodexUsageResponse {
  if (!data || typeof data !== "object") {
    throw new Error("INVALID_CODEX_RESPONSE");
  }

  const value = data as Record<string, unknown>;
  const primary = value.primary as Record<string, unknown> | null | undefined;
  const secondary = value.secondary as Record<string, unknown> | null | undefined;
  const credits = value.credits as Record<string, unknown> | undefined;

  const validWindow = (window: Record<string, unknown> | null | undefined) =>
    window === null ||
    window === undefined ||
    (isNumber(window.usedPercent) &&
      isNumber(window.resetsAt) &&
      isNumber(window.windowMinutes) &&
      isNumber(window.windowSeconds));

  if (
    !validWindow(primary) ||
    !validWindow(secondary) ||
    !credits ||
    typeof credits.hasCredits !== "boolean" ||
    typeof credits.unlimited !== "boolean" ||
    !isNumber(credits.balance)
  ) {
    throw new Error("INVALID_CODEX_RESPONSE");
  }

  return value as unknown as CodexUsageResponse;
}

export async function fetchCodexUsage(auth: CodexAuth): Promise<CodexUsageResponse> {
  const headers = buildBaseHeaders(auth);
  const response = await fetch(USAGE_URL, { headers });

  if (response.status === 401 || response.status === 403) {
    throw new Error("TOKEN_EXPIRED");
  }
  if (!response.ok) {
    await response.text().catch(() => null);
    throw new Error(`HTTP_ERROR_${response.status}`);
  }

  const payload = (await response.json()) as RawUsageResponse;
  const resetCoupons = await fetchResetCoupons(auth, payload);

  return {
    planType: payload.plan_type ?? null,
    rateLimitReachedType: payload.rate_limit_reached_type ?? null,
    primary: normalizeWindow(payload.rate_limit?.primary_window),
    secondary: normalizeWindow(payload.rate_limit?.secondary_window),
    credits: {
      hasCredits: Boolean(payload.credits?.has_credits),
      unlimited: Boolean(payload.credits?.unlimited),
      balance: Number(payload.credits?.balance ?? 0),
    },
    resetCoupons,
  };
}
