import type { FormatHandler, ParsedRequest } from "../handler";
import type { CapturedLog, TokenUsage } from "../../schemas";
import { OpenAIRequestSchema, OpenAIResponseSchema, parseOpenAIResponse } from "./schemas";
import { extractOpenAIStream } from "./stream";

export const OpenAIFormatHandler: FormatHandler = {
  format: "openai",

  parseRequest(rawBody: string, headers?: Headers): ParsedRequest | null {
    try {
      const json: unknown = JSON.parse(rawBody);
      const result = OpenAIRequestSchema.safeParse(json);
      if (result.success) {
        return {
          model: result.data.model,
          sessionId: headers?.get("x-session-affinity") ?? null,
        };
      }
      return null;
    } catch {
      return null;
    }
  },

  extractTokens(responseBody: string): TokenUsage {
    const parsed = parseOpenAIResponse(responseBody);
    if (parsed) {
      // OpenAI puts cached_tokens in usage.prompt_tokens_details (passthrough field)
      let cacheReadInputTokens: number | null = null;
      try {
        const raw: unknown = JSON.parse(responseBody);
        if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
          const usageDesc = Object.getOwnPropertyDescriptor(raw, "usage");
          if (
            usageDesc !== undefined &&
            typeof usageDesc.value === "object" &&
            usageDesc.value !== null
          ) {
            const detailsDesc = Object.getOwnPropertyDescriptor(
              usageDesc.value,
              "prompt_tokens_details",
            );
            if (
              detailsDesc !== undefined &&
              typeof detailsDesc.value === "object" &&
              detailsDesc.value !== null
            ) {
              const cacheDesc = Object.getOwnPropertyDescriptor(detailsDesc.value, "cached_tokens");
              if (cacheDesc !== undefined && typeof cacheDesc.value === "number") {
                cacheReadInputTokens = cacheDesc.value;
              }
            }
          }
        }
      } catch {
        // ignore parse errors
      }
      return {
        inputTokens: parsed.usage.prompt_tokens ?? null,
        outputTokens: parsed.usage.completion_tokens ?? null,
        cacheCreationInputTokens: null,
        cacheReadInputTokens,
      };
    }
    return {
      inputTokens: null,
      outputTokens: null,
      cacheCreationInputTokens: null,
      cacheReadInputTokens: null,
    };
  },

  extractStream(
    raw: string,
    log: CapturedLog,
    fallbackModel?: string,
    collectChunks?: boolean,
  ): string {
    return extractOpenAIStream(raw, log, fallbackModel, collectChunks);
  },

  detectFormat(rawBody: string | null): boolean {
    if (rawBody === null) return false;
    try {
      const json: unknown = JSON.parse(rawBody);
      if (typeof json === "object" && json !== null && !Array.isArray(json)) {
        const keys = Object.keys(json);
        // OpenAI has `model` and `messages` at the top level, but NOT `system`
        return keys.includes("model") && keys.includes("messages") && !keys.includes("system");
      }
      return false;
    } catch {
      return false;
    }
  },
};
