import { normalizeProviderId } from "./model-selection.js";
import { isGoogleModelApi } from "./google-api.js";
import {
  isAnthropicProviderFamily,
  isOpenAiProviderFamily,
  preservesAnthropicThinkingSignatures,
  resolveTranscriptToolCallIdMode,
  shouldDropThinkingBlocksForModel,
  shouldSanitizeGeminiThoughtSignaturesForModel,
  supportsOpenAiCompatTurnValidation,
} from "./provider-capabilities.js";
import type { ToolCallIdMode } from "./tool-call-id.js";

export type TranscriptSanitizeMode = "full" | "images-only";

export type TranscriptPolicy = {
  sanitizeMode: TranscriptSanitizeMode;
  sanitizeToolCallIds: boolean;
  toolCallIdMode?: ToolCallIdMode;
  repairToolUseResultPairing: boolean;
  preserveSignatures: boolean;
  sanitizeThoughtSignatures?: {
    allowBase64Only?: boolean;
    includeCamelCase?: boolean;
  };
  sanitizeThinkingSignatures: boolean;
  dropThinkingBlocks: boolean;
  applyGoogleTurnOrdering: boolean;
  validateGeminiTurns: boolean;
  validateAnthropicTurns: boolean;
  allowSyntheticToolResults: boolean;
};

const OPENAI_MODEL_APIS = new Set([
  "openai",
  "openai-completions",
  "openai-responses",
  "openai-codex-responses",
]);

function isOpenAiApi(modelApi?: string | null): boolean {
  if (!modelApi) {
    return false;
  }
  return OPENAI_MODEL_APIS.has(modelApi);
}

function isOpenAiProvider(provider?: string | null): boolean {
  return isOpenAiProviderFamily(provider);
}

function isAnthropicApi(modelApi?: string | null, provider?: string | null): boolean {
  if (modelApi === "anthropic-messages" || modelApi === "bedrock-converse-stream") {
    return true;
  }
  // MiniMax now uses openai-completions API, not anthropic-messages
  return isAnthropicProviderFamily(provider);
}

function resolveToolCallIdMode(
  providerToolCallIdMode: ToolCallIdMode | undefined,
  sanitizeToolCallIds: boolean,
  isMistral: boolean,
): ToolCallIdMode | undefined {
  if (providerToolCallIdMode) return providerToolCallIdMode;
  if (isMistral) return "strict9";
  if (sanitizeToolCallIds) return "strict";
  return undefined;
}

export function resolveTranscriptPolicy(params: {
  modelApi?: string | null;
  provider?: string | null;
  modelId?: string | null;
}): TranscriptPolicy {
  const provider = normalizeProviderId(params.provider ?? "");
  const modelId = params.modelId ?? "";
  const isGoogle = isGoogleModelApi(params.modelApi);
  const isAnthropic = isAnthropicApi(params.modelApi, provider);
  const isOpenAi = isOpenAiProvider(provider) || (!provider && isOpenAiApi(params.modelApi));
  const isStrictOpenAiCompatible =
    params.modelApi === "openai-completions" &&
    !isOpenAi &&
    supportsOpenAiCompatTurnValidation(provider);
  const providerToolCallIdMode = resolveTranscriptToolCallIdMode(provider, modelId);
  const isMistral = providerToolCallIdMode === "strict9";
  const shouldSanitizeGeminiThoughtSignaturesForProvider =
    shouldSanitizeGeminiThoughtSignaturesForModel({
      provider,
      modelId,
    });
  const requiresOpenAiCompatibleToolIdSanitization = params.modelApi === "openai-completions";

  // GitHub Copilot's Claude endpoints can reject persisted `thinking` blocks with
  // non-binary/non-base64 signatures (e.g. thinkingSignature: "reasoning_text").
  // Drop these blocks at send-time to keep sessions usable.
  const dropThinkingBlocks = shouldDropThinkingBlocksForModel({ provider, modelId });

  const needsNonImageSanitize =
    isGoogle || isAnthropic || isMistral || shouldSanitizeGeminiThoughtSignaturesForProvider;

  const sanitizeToolCallIds =
    isGoogle || isMistral || isAnthropic || requiresOpenAiCompatibleToolIdSanitization;
  const toolCallIdMode = resolveToolCallIdMode(
    providerToolCallIdMode,
    sanitizeToolCallIds,
    isMistral,
  );
  // All providers need orphaned tool_result repair after history truncation.
  // OpenAI rejects function_call_output items whose call_id has no matching
  // function_call in the conversation, so the repair must run universally.
  const repairToolUseResultPairing = true;
  const sanitizeThoughtSignatures =
    shouldSanitizeGeminiThoughtSignaturesForProvider || isGoogle
      ? { allowBase64Only: true, includeCamelCase: true }
      : undefined;

  return {
    sanitizeMode: isOpenAi ? "images-only" : needsNonImageSanitize ? "full" : "images-only",
    sanitizeToolCallIds:
      (!isOpenAi && sanitizeToolCallIds) || requiresOpenAiCompatibleToolIdSanitization,
    toolCallIdMode,
    repairToolUseResultPairing,
    preserveSignatures: isAnthropic && preservesAnthropicThinkingSignatures(provider),
    sanitizeThoughtSignatures: isOpenAi ? undefined : sanitizeThoughtSignatures,
    sanitizeThinkingSignatures: false,
    dropThinkingBlocks,
    applyGoogleTurnOrdering: !isOpenAi && (isGoogle || isStrictOpenAiCompatible),
    validateGeminiTurns: !isOpenAi && (isGoogle || isStrictOpenAiCompatible),
    validateAnthropicTurns: !isOpenAi && (isAnthropic || isStrictOpenAiCompatible),
    allowSyntheticToolResults: !isOpenAi && (isGoogle || isAnthropic),
  };
}
