/**
 * Codex usage-limit tracker.
 *
 * ChatGPT subscription Codex usage is exposed via two mechanisms:
 *
 *   1. Per-response headers (sent on every 2xx and 4xx):
 *        x-codex-primary-used-percent       0..100
 *        x-codex-primary-window-minutes     window length (e.g. 300 = 5h)
 *        x-codex-primary-reset-at           absolute epoch-seconds
 *        x-codex-primary-reset-after-seconds seconds from now
 *        x-codex-secondary-*                same keys for the weekly cap
 *
 *   2. 429 body (on usage_limit_reached):
 *        { error: { type: "usage_limit_reached", plan_type: "plus"|"pro"|…,
 *                   resets_at: <epoch_seconds> } }
 *
 * We keep the latest snapshot in memory so `/codex-usage` can report it
 * without making a probe request. No persistence — the numbers are only
 * useful during the current session.
 */

export interface CodexBucketUsage {
  usedPercent: number
  windowMinutes: number | null
  resetAt: number | null // epoch ms
}

export interface CodexUsageSnapshot {
  primary: CodexBucketUsage | null
  secondary: CodexBucketUsage | null
  /** Last time we received a usage header from the backend. */
  updatedAt: number
  /** Latest `usage_limit_reached` plan_type, if we've hit a cap. */
  capHitPlanType?: string
  /** Timestamp (ms) the current cap resets, if hit. */
  capResetsAt?: number
}

let snapshot: CodexUsageSnapshot | null = null

function parseBucket(
  headers: Headers | Record<string, string | string[] | undefined>,
  prefix: 'primary' | 'secondary',
): CodexBucketUsage | null {
  const get = (k: string): string | null => {
    if (headers instanceof Headers) return headers.get(k)
    const v = (headers as Record<string, string | string[] | undefined>)[k.toLowerCase()]
    if (Array.isArray(v)) return v[0] ?? null
    return typeof v === 'string' ? v : null
  }
  const usedPercentRaw = get(`x-codex-${prefix}-used-percent`)
  if (usedPercentRaw == null) return null

  const usedPercent = Number(usedPercentRaw)
  if (!Number.isFinite(usedPercent)) return null

  const windowMinutesRaw = get(`x-codex-${prefix}-window-minutes`)
  const windowMinutes = windowMinutesRaw ? Number(windowMinutesRaw) : null

  // Prefer reset-at (absolute) over reset-after-seconds (relative).
  const resetAtRaw = get(`x-codex-${prefix}-reset-at`)
  const resetAfterRaw = get(`x-codex-${prefix}-reset-after-seconds`)
  let resetAt: number | null = null
  if (resetAtRaw) {
    const n = Number(resetAtRaw)
    if (Number.isFinite(n)) resetAt = n * 1000
  } else if (resetAfterRaw) {
    const n = Number(resetAfterRaw)
    if (Number.isFinite(n)) resetAt = Date.now() + n * 1000
  }

  return {
    usedPercent,
    windowMinutes: Number.isFinite(windowMinutes as number) ? (windowMinutes as number) : null,
    resetAt,
  }
}

export function recordCodexUsageHeaders(
  headers: Headers | Record<string, string | string[] | undefined>,
): void {
  const primary = parseBucket(headers, 'primary')
  const secondary = parseBucket(headers, 'secondary')
  if (!primary && !secondary) return

  snapshot = {
    primary: primary ?? snapshot?.primary ?? null,
    secondary: secondary ?? snapshot?.secondary ?? null,
    updatedAt: Date.now(),
    capHitPlanType: snapshot?.capHitPlanType,
    capResetsAt: snapshot?.capResetsAt,
  }
}

/** Call when a 429 response body has `error.type === 'usage_limit_reached'`. */
export function recordCodexCapHit(opts: {
  planType?: string
  resetsAt?: number /* epoch seconds */
}): void {
  snapshot = {
    primary: snapshot?.primary ?? null,
    secondary: snapshot?.secondary ?? null,
    updatedAt: Date.now(),
    capHitPlanType: opts.planType ?? snapshot?.capHitPlanType,
    capResetsAt: opts.resetsAt ? opts.resetsAt * 1000 : snapshot?.capResetsAt,
  }
}

export function getCodexUsage(): CodexUsageSnapshot | null {
  return snapshot
}

export function formatCodexUsage(s: CodexUsageSnapshot | null): string {
  if (!s) return 'No Codex usage data yet — make a request to populate.'
  const lines: string[] = []
  const fmtBucket = (label: string, b: CodexBucketUsage | null): string | null => {
    if (!b) return null
    const used = `${b.usedPercent.toFixed(1)}%`
    const window = b.windowMinutes ? ` / ${b.windowMinutes}min window` : ''
    const reset = b.resetAt
      ? ` · resets ${new Date(b.resetAt).toLocaleString()}`
      : ''
    return `${label}: ${used}${window}${reset}`
  }
  const p = fmtBucket('Primary', s.primary)
  const q = fmtBucket('Secondary', s.secondary)
  if (p) lines.push(p)
  if (q) lines.push(q)
  if (s.capHitPlanType) {
    const when = s.capResetsAt ? ` (resets ${new Date(s.capResetsAt).toLocaleString()})` : ''
    lines.push(`⚠ Cap hit on ${s.capHitPlanType}${when}`)
  }
  lines.push(`(updated ${new Date(s.updatedAt).toLocaleTimeString()})`)
  return lines.join('\n')
}

/** For tests. */
export function resetCodexUsageSnapshot(): void {
  snapshot = null
}
