/**
 * Codex Session Helpers
 *
 * Pure functions for session state construction, policy mapping,
 * rate limit normalization, and token usage processing.
 */

import { randomUUID } from "node:crypto";
import type { SessionState, CLIResultMessage } from "./session-types.js";
import { asRecord } from "./codex-mcp-helpers.js";

// ─── Types ────────────────────────────────────────────────────────────────────

export interface NormalizedRateLimit {
  usedPercent: number;
  windowDurationMins: number;
  resetsAt: number;
}

export interface RateLimits {
  primary: NormalizedRateLimit | null;
  secondary: NormalizedRateLimit | null;
}

// ─── Session State Builder ────────────────────────────────────────────────────

/** Build the initial SessionState emitted in session_init after Codex initialization. */
export function buildInitialSessionState(
  sessionId: string,
  model: string,
  cwd: string,
  permissionMode: string,
): SessionState {
  return {
    session_id: sessionId,
    backend_type: "codex",
    model,
    cwd,
    tools: [],
    permissionMode,
    claude_code_version: "",
    mcp_servers: [],
    agents: [],
    slash_commands: [],
    skills: [],
    total_cost_usd: 0,
    num_turns: 0,
    context_used_percent: 0,
    input_tokens: 0,
    output_tokens: 0,
    cache_read_tokens: 0,
    cache_creation_tokens: 0,
    context_window: 0,
    is_compacting: false,
    git_branch: "",
    is_worktree: false,
    is_containerized: false,
    repo_root: "",
    git_ahead: 0,
    git_behind: 0,
    total_lines_added: 0,
    total_lines_removed: 0,
  };
}

// ─── Turn Completion ──────────────────────────────────────────────────────────

/** Build a CLIResultMessage from a completed Codex turn. */
export function buildTurnCompletedResult(
  turn: { id: string; status: string; error?: { message: string } } | undefined,
  sessionId: string,
): CLIResultMessage {
  return {
    type: "result",
    subtype: turn?.status === "completed" ? "success" : "error_during_execution",
    is_error: turn?.status !== "completed",
    result: turn?.error?.message,
    duration_ms: 0,
    duration_api_ms: 0,
    num_turns: 1,
    total_cost_usd: 0,
    stop_reason: turn?.status || "end_turn",
    usage: {
      input_tokens: 0,
      output_tokens: 0,
      cache_creation_input_tokens: 0,
      cache_read_input_tokens: 0,
    },
    uuid: randomUUID(),
    session_id: sessionId,
  };
}

// ─── Rate Limit Normalization ─────────────────────────────────────────────────

function toEpochMs(value: unknown): number {
  if (typeof value !== "number" || !Number.isFinite(value) || value <= 0) {
    return 0;
  }
  return value < 1_000_000_000_000 ? value * 1000 : value;
}

function normalizeLimit(raw: unknown): NormalizedRateLimit | null {
  if (!raw || typeof raw !== "object") return null;
  const limit = raw as Record<string, unknown>;
  return {
    usedPercent: typeof limit.usedPercent === "number" ? limit.usedPercent : 0,
    windowDurationMins: typeof limit.windowDurationMins === "number" ? limit.windowDurationMins : 0,
    resetsAt: toEpochMs(limit.resetsAt),
  };
}

/** Normalize raw Codex rate limit data into a structured format. Returns null if no data. */
export function normalizeRateLimits(data: Record<string, unknown>): RateLimits | null {
  const rl = data?.rateLimits as Record<string, unknown> | undefined;
  if (!rl) return null;
  return {
    primary: normalizeLimit(rl.primary),
    secondary: normalizeLimit(rl.secondary),
  };
}

// ─── Token Usage ──────────────────────────────────────────────────────────────

/**
 * Build session update fields from Codex token usage notification.
 * Returns null if no meaningful updates exist.
 *
 * Uses the `last` turn's input tokens for context usage (what's actually in context),
 * and the cumulative `total` for display in the UI.
 */
export function buildTokenUsageSessionUpdate(
  params: Record<string, unknown>,
): Partial<SessionState> | null {
  const tokenUsage = params.tokenUsage as Record<string, unknown> | undefined;
  if (!tokenUsage) return null;

  const total = tokenUsage.total as Record<string, number> | undefined;
  const last = tokenUsage.last as Record<string, number> | undefined;
  const contextWindow = tokenUsage.modelContextWindow as number | undefined;

  const updates: Partial<SessionState> = {};

  if (last && contextWindow && contextWindow > 0) {
    const usedInContext = (last.inputTokens || 0) + (last.outputTokens || 0);
    const pct = Math.round((usedInContext / contextWindow) * 100);
    updates.context_used_percent = Math.max(0, Math.min(pct, 100));
  }

  if (total) {
    updates.codex_token_details = {
      inputTokens: total.inputTokens || 0,
      outputTokens: total.outputTokens || 0,
      cachedInputTokens: total.cachedInputTokens || 0,
      reasoningOutputTokens: total.reasoningOutputTokens || 0,
      modelContextWindow: contextWindow || 0,
    };
  }

  return Object.keys(updates).length > 0 ? updates : null;
}

// ─── Policy Mappers ───────────────────────────────────────────────────────────

/**
 * Map permission mode to Codex approval policy.
 * Always "never" — permission prompts are handled by the Zencode UI, not Codex.
 */
export function mapApprovalPolicy(_mode?: string): string {
  return "never";
}

/**
 * Map permission mode to Codex sandbox policy string (for thread/start, thread/resume).
 * Always full access — matches approvalPolicy: "never" for full autonomy.
 */
export function mapSandboxPolicy(_mode?: string): string {
  return "danger-full-access";
}

/**
 * Map permission mode to Codex SandboxPolicy object (for turn/start's sandboxPolicy field).
 * Always full access — matches approvalPolicy: "never" for full autonomy.
 */
export function mapSandboxPolicyObject(_mode?: string): { type: string } {
  return { type: "dangerFullAccess" };
}

/** Map collaboration mode kind + model to Codex collaborationMode payload. */
export function mapCollaborationMode(
  kind: "default" | "plan",
  model: string,
): { mode: "default" | "plan"; settings: { model: string } } {
  return { mode: kind, settings: { model } };
}

// ─── Collaboration Mode Resolution ───────────────────────────────────────────

/**
 * Resolve the collaboration mode kind from a turn/started notification payload.
 * Checks both the nested collaborationMode object and the flat collaborationModeKind field.
 * Returns null if no valid mode is found.
 */
export function resolveCollaborationModeFromTurn(
  params: Record<string, unknown>,
): "default" | "plan" | null {
  const turn = asRecord(params.turn);
  const collab = asRecord(turn?.collaborationMode);
  const fromObject = collab?.mode;
  const fromFlat = turn?.collaborationModeKind;

  if (fromObject === "plan" || fromObject === "default") return fromObject;
  if (fromFlat === "plan" || fromFlat === "default") return fromFlat as "default" | "plan";
  return null;
}
