/**
 * Gemini multi-turn contents 校验与修复（functionCall / functionResponse 轮次约束）。
 */

import { extractFunctionCallsFromParts } from "./geminiContentStream.ts";
import type { GeminiChatContent } from "./geminiChatMemory.ts";

type GeminiPart = {
  text?: string;
  thought?: boolean;
  functionCall?: { name?: string; id?: string; args?: unknown };
  functionResponse?: { name?: string; id?: string; response?: unknown };
  inlineData?: unknown;
  fileData?: unknown;
};

function asPart(part: unknown): GeminiPart {
  return part as GeminiPart;
}

export function contentHasFunctionCall(content: GeminiChatContent): boolean {
  return content.parts.some((p) => Boolean(asPart(p).functionCall));
}

export function contentHasFunctionResponse(content: GeminiChatContent): boolean {
  return content.parts.some((p) => Boolean(asPart(p).functionResponse));
}

/** user 轮次是否仅含 functionResponse（无普通文本/媒体）。 */
export function contentIsFunctionResponseOnly(content: GeminiChatContent): boolean {
  if (content.role !== "user") return false;
  const parts = content.parts ?? [];
  if (parts.length === 0) return false;
  return parts.every((p) => Boolean(asPart(p).functionResponse));
}

/** 工具轮结束后缺失 model 回复时的占位（满足 Gemini：FR 后须接 model）。 */
const GEMINI_STUB_MODEL_AFTER_FR: GeminiChatContent = {
  role: "model",
  parts: [{ text: "…" }],
};

/** 保留含 functionCall / functionResponse / 非空 text / 媒体的 part。 */
export function isMeaningfulGeminiPart(part: unknown): boolean {
  const p = asPart(part);
  if (p.functionCall || p.functionResponse) return true;
  if (p.inlineData || p.fileData) return true;
  if (typeof p.text === "string" && p.text.trim().length > 0) return true;
  return false;
}

export function normalizeGeminiContent(content: GeminiChatContent): GeminiChatContent | null {
  const role = content.role === "model" ? "model" : "user";
  const parts = (content.parts ?? []).filter(isMeaningfulGeminiPart);
  if (parts.length === 0) return null;
  return { role, parts };
}

/**
 * 修复 Gemini API 要求的 contents 序列：
 * - 首条须为 user
 * - model(functionCall) 后必须紧跟 user(functionResponse)
 * - 去掉尾部未闭合的 model(functionCall)
 * - 去掉会破坏轮次的截断前缀
 */
export function sanitizeGeminiContents(
  contents: readonly GeminiChatContent[],
): GeminiChatContent[] {
  const normalized = contents
    .map((c) => normalizeGeminiContent(c))
    .filter((c): c is GeminiChatContent => c !== null);

  let start = 0;
  while (start < normalized.length && normalized[start]!.role !== "user") {
    start++;
  }
  // memory 截断可能留下无前置 model(functionCall) 的 orphan user(FR)
  while (
    start < normalized.length &&
    contentIsFunctionResponseOnly(normalized[start]!)
  ) {
    start++;
  }
  const slice = normalized.slice(start);

  const validated: GeminiChatContent[] = [];
  for (const msg of slice) {
    if (validated.length === 0) {
      validated.push(msg);
      continue;
    }

    const prev = validated[validated.length - 1]!;

    if (prev.role === "model" && contentHasFunctionCall(prev)) {
      if (msg.role !== "user" || !contentHasFunctionResponse(msg)) {
        break;
      }
      validated.push(msg);
      continue;
    }

    if (prev.role === "model" && !contentHasFunctionCall(prev)) {
      if (msg.role !== "user") break;
      validated.push(msg);
      continue;
    }

    if (prev.role === "user") {
      if (msg.role === "model") {
        validated.push(msg);
        continue;
      }
      if (msg.role === "user") {
        const prevFrOnly = contentIsFunctionResponseOnly(prev);
        const msgFr = contentHasFunctionResponse(msg);
        // functionResponse 后必须接 model，再接新的 user 文本（memory 缺最终 model 时补占位）
        if (prevFrOnly && !msgFr) {
          validated.push(GEMINI_STUB_MODEL_AFTER_FR);
          validated.push(msg);
          continue;
        }
        // 连续 plain user：保留最新一条（memory 截断或多次 send 合并）
        if (!prevFrOnly && !msgFr) {
          validated[validated.length - 1] = msg;
          continue;
        }
      }
      break;
    }

    if (prev.role === "model" && msg.role === "model") {
      break;
    }

    validated.push(msg);
  }

  while (validated.length > 0) {
    const last = validated[validated.length - 1]!;
    if (last.role === "model" && contentHasFunctionCall(last)) {
      validated.pop();
      continue;
    }
    // 持久化/截断后尾部 orphan user(FR)（缺 model 收尾）会导致下次 send 400
    if (contentIsFunctionResponseOnly(last)) {
      validated.pop();
      continue;
    }
    break;
  }

  return validated;
}

export type GeminiToolResult = {
  name: string;
  id?: string;
  response: Record<string, unknown>;
};

/** 为 model turn 中每个 functionCall part 生成匹配的 functionResponse part。 */
export function buildFunctionResponseUserTurn(
  modelParts: readonly unknown[],
  toolResults: readonly GeminiToolResult[],
): GeminiChatContent {
  const callsFromParts = extractFunctionCallsFromParts(modelParts) as Array<{
    name?: string;
    id?: string;
  }>;

  const consumed = new Set<number>();
  const parts: unknown[] = [];

  for (const call of callsFromParts) {
    const callName = String(call.name ?? "unknown_tool");
    let matchedIndex = toolResults.findIndex(
      (tr, idx) =>
        !consumed.has(idx) &&
        tr.name === callName &&
        (!call.id || !tr.id || tr.id === call.id),
    );
    if (matchedIndex < 0) {
      matchedIndex = toolResults.findIndex(
        (tr, idx) => !consumed.has(idx) && tr.name === callName,
      );
    }

    const tr: GeminiToolResult =
      matchedIndex >= 0
        ? toolResults[matchedIndex]!
        : {
            name: callName,
            id: call.id,
            response: {
              error:
                "Tool result missing for function call (CodeFlowMu adapter repair).",
            },
          };

    if (matchedIndex >= 0) consumed.add(matchedIndex);

    const part: Record<string, unknown> = {
      functionResponse: {
        name: tr.name,
        response: tr.response,
      },
    };
    const responseId = tr.id ?? call.id;
    if (responseId) {
      (part.functionResponse as { id?: string }).id = responseId;
    }
    parts.push(part);
  }

  if (parts.length === 0 && toolResults.length > 0) {
    for (const tr of toolResults) {
      const part: Record<string, unknown> = {
        functionResponse: {
          name: tr.name,
          response: tr.response,
        },
      };
      if (tr.id) {
        (part.functionResponse as { id?: string }).id = tr.id;
      }
      parts.push(part);
    }
  }

  return { role: "user", parts };
}

/** 校验 model FC 数量与 user FR 数量一致（错位时 Gemini 下一轮易复读工具）。 */
export function validateFunctionResponseAlignment(
  modelParts: readonly unknown[],
  userTurn: GeminiChatContent,
): { ok: boolean; callCount: number; responseCount: number } {
  const calls = extractFunctionCallsFromParts(modelParts);
  const responses = (userTurn.parts ?? [])
    .map((p) => asPart(p).functionResponse)
    .filter(Boolean) as Array<{ id?: string; name?: string }>;
  const callCount = calls.length;
  const responseCount = responses.length;

  if (callCount !== responseCount) {
    return { ok: false, callCount, responseCount };
  }

  for (let i = 0; i < calls.length; i++) {
    const call = calls[i]!;
    const response = responses[i]!;
    const callId = call.id?.trim();
    const responseId = response.id?.trim();
    if (callId && responseId && callId !== responseId) {
      return { ok: false, callCount, responseCount };
    }
    if (call.name && response.name && call.name !== response.name) {
      return { ok: false, callCount, responseCount };
    }
  }

  return { ok: true, callCount, responseCount };
}

/**
 * 按 model functionCall 顺序强制重建 functionResponse 轮次（P0 错位修复）。
 * 每个 call 必须对应一条 FR，且优先沿用 call.id。
 */
export function repairFunctionResponseUserTurn(
  modelParts: readonly unknown[],
  toolResults: readonly GeminiToolResult[],
): GeminiChatContent {
  const calls = extractFunctionCallsFromParts(modelParts);
  const consumed = new Set<number>();
  const parts: unknown[] = [];

  for (const call of calls) {
    let matchedIndex = -1;
    if (call.id) {
      matchedIndex = toolResults.findIndex(
        (tr, idx) => !consumed.has(idx) && tr.id === call.id,
      );
    }
    if (matchedIndex < 0 && call.name) {
      matchedIndex = toolResults.findIndex(
        (tr, idx) => !consumed.has(idx) && tr.name === call.name,
      );
    }
    if (matchedIndex < 0) {
      matchedIndex = toolResults.findIndex((_, idx) => !consumed.has(idx));
    }

    const tr =
      matchedIndex >= 0
        ? toolResults[matchedIndex]!
        : {
            name: call.name ?? "unknown_tool",
            id: call.id,
            response: {
              error: "Missing tool result for functionCall alignment repair.",
            },
          };

    if (matchedIndex >= 0) consumed.add(matchedIndex);

    const part: Record<string, unknown> = {
      functionResponse: {
        name: tr.name ?? call.name ?? "unknown_tool",
        response: tr.response,
      },
    };
    const responseId = call.id ?? tr.id;
    if (responseId) {
      (part.functionResponse as { id?: string }).id = responseId;
    }
    parts.push(part);
  }

  return { role: "user", parts };
}

/** 无 tools 声明时移除 history 中的 functionCall/functionResponse 轮次，避免 400。 */
export function stripFunctionCallTurns(
  contents: readonly GeminiChatContent[],
): GeminiChatContent[] {
  return contents.filter(
    (c) => !contentHasFunctionCall(c) && !contentHasFunctionResponse(c),
  );
}
