export function createRecallInputState(deps = {}) {
  let currentGenerationTrivialSkip = null;

  const getPendingRecallSendIntent = () =>
    deps.getPendingRecallSendIntent?.() ?? deps.createRecallInputRecord?.();
  const setPendingRecallSendIntent = (record) => {
    deps.setPendingRecallSendIntent?.(record);
    return record;
  };
  const getPendingHostGenerationInputSnapshot = () =>
    deps.getPendingHostGenerationInputSnapshot?.() ?? deps.createRecallInputRecord?.();
  const setPendingHostGenerationInputSnapshot = (record) => {
    deps.setPendingHostGenerationInputSnapshot?.(record);
    return record;
  };
  const getLastRecallSentUserMessage = () =>
    deps.getLastRecallSentUserMessage?.() ?? deps.createRecallInputRecord?.();
  const setLastRecallSentUserMessage = (record) => {
    deps.setLastRecallSentUserMessage?.(record);
    return record;
  };
  const getCurrentChatId = (...args) => deps.getCurrentChatId?.(...args);
  const normalizeChatIdCandidate = (value = "") =>
    deps.normalizeChatIdCandidate?.(value) ?? String(value ?? "").trim();
  const normalizeRecallInputText = (value = "") =>
    deps.normalizeRecallInputText?.(value) ?? String(value || "").trim();
  const createRecallInputRecord = (record = {}) =>
    deps.createRecallInputRecord?.(record) ?? { ...(record || {}) };
  const hashRecallInput = (value = "") => deps.hashRecallInput?.(value) ?? "";
  const isFreshRecallInputRecord = (record) =>
    deps.isFreshRecallInputRecord?.(record) ?? Boolean(record?.text);
  const getTrivialGenerationSkipTtlMs = () =>
    Number.isFinite(Number(deps.TRIVIAL_GENERATION_SKIP_TTL_MS))
      ? Number(deps.TRIVIAL_GENERATION_SKIP_TTL_MS)
      : 60000;

  function freezeHostGenerationInputSnapshot(
    text,
    source = "host-generation-lifecycle",
  ) {
    const normalized = normalizeRecallInputText(text);
    if (!normalized) return null;

    const nextSnapshot = createRecallInputRecord({
      text: normalized,
      hash: hashRecallInput(normalized),
      source,
      at: Date.now(),
    });
    setPendingHostGenerationInputSnapshot(nextSnapshot);
    return nextSnapshot;
  }

  function consumeHostGenerationInputSnapshot(options = {}) {
    const { preserve = false } = options;
    const pendingHostGenerationInputSnapshot = getPendingHostGenerationInputSnapshot();
    if (!isFreshRecallInputRecord(pendingHostGenerationInputSnapshot)) {
      if (!preserve) {
        setPendingHostGenerationInputSnapshot(createRecallInputRecord());
      }
      return createRecallInputRecord();
    }

    const snapshot = createRecallInputRecord({
      ...pendingHostGenerationInputSnapshot,
    });
    if (!preserve) {
      setPendingHostGenerationInputSnapshot(createRecallInputRecord());
    }
    return snapshot;
  }

  function readPendingHostGenerationInputSnapshot() {
    return getPendingHostGenerationInputSnapshot();
  }

  function clearPendingRecallSendIntent() {
    const nextRecord = createRecallInputRecord();
    setPendingRecallSendIntent(nextRecord);
    return nextRecord;
  }

  function clearPendingHostGenerationInputSnapshot() {
    const nextSnapshot = createRecallInputRecord();
    setPendingHostGenerationInputSnapshot(nextSnapshot);
    return nextSnapshot;
  }

  function getCurrentGenerationTrivialSkip(
    chatId = getCurrentChatId(),
    now = Date.now(),
  ) {
    if (!currentGenerationTrivialSkip) return null;

    const setAtMs = Number(currentGenerationTrivialSkip.setAtMs) || 0;
    if (
      !setAtMs ||
      now - setAtMs > getTrivialGenerationSkipTtlMs()
    ) {
      currentGenerationTrivialSkip = null;
      return null;
    }

    const normalizedChatId = normalizeChatIdCandidate(chatId);
    const activeChatId = normalizeChatIdCandidate(
      currentGenerationTrivialSkip.chatId,
    );
    if (normalizedChatId && activeChatId && normalizedChatId !== activeChatId) {
      return null;
    }

    return currentGenerationTrivialSkip;
  }

  function markCurrentGenerationTrivialSkip({
    reason = "",
    chatId = getCurrentChatId(),
    chatLength = 0,
  } = {}) {
    currentGenerationTrivialSkip = {
      chatId: normalizeChatIdCandidate(chatId),
      setAtMs: Date.now(),
      reason: String(reason || ""),
      generationStartMinChatIndex: Math.max(
        0,
        Math.floor(Number(chatLength) || 0),
      ),
    };
    return currentGenerationTrivialSkip;
  }

  function clearCurrentGenerationTrivialSkip(_reason = "") {
    const previous = currentGenerationTrivialSkip;
    currentGenerationTrivialSkip = null;
    return previous;
  }

  function consumeCurrentGenerationTrivialSkip(
    targetMessageIndex,
    chatId = getCurrentChatId(),
    now = Date.now(),
  ) {
    const activeSkip = getCurrentGenerationTrivialSkip(chatId, now);
    if (!activeSkip) return false;

    const normalizedTargetIndex = Number.isFinite(Number(targetMessageIndex))
      ? Math.floor(Number(targetMessageIndex))
      : null;
    if (!Number.isFinite(normalizedTargetIndex)) {
      return false;
    }

    if (
      normalizedTargetIndex <
      Math.max(0, Math.floor(Number(activeSkip.generationStartMinChatIndex) || 0))
    ) {
      return false;
    }

    currentGenerationTrivialSkip = null;
    return true;
  }

  function recordRecallSendIntent(text, source = "dom-intent") {
    const normalized = normalizeRecallInputText(text);
    if (!normalized) return createRecallInputRecord();

    const hash = hashRecallInput(normalized);
    const pendingRecallSendIntent = getPendingRecallSendIntent();
    const previousRecord = isFreshRecallInputRecord(pendingRecallSendIntent)
      ? pendingRecallSendIntent
      : null;
    const previousHash = String(previousRecord?.hash || "");
    const previousText = String(previousRecord?.text || "");

    if (previousHash && previousHash === hash && previousText === normalized) {
      const nextRecord = createRecallInputRecord({
        ...previousRecord,
        at: Date.now(),
        source: String(source || previousRecord.source || "dom-intent"),
      });
      setPendingRecallSendIntent(nextRecord);
      return nextRecord;
    }

    const nextRecord = createRecallInputRecord({
      text: normalized,
      hash,
      source,
      at: Date.now(),
    });
    setPendingRecallSendIntent(nextRecord);
    return nextRecord;
  }

  function recordRecallSentUserMessage(messageId, text, source = "message-sent") {
    const normalized = normalizeRecallInputText(text);
    if (!normalized) return createRecallInputRecord();

    const hash = hashRecallInput(normalized);
    const nextRecord = createRecallInputRecord({
      text: normalized,
      hash,
      messageId: Number.isFinite(messageId) ? messageId : null,
      source,
      at: Date.now(),
    });
    setLastRecallSentUserMessage(nextRecord);
    if (typeof deps.recordMessageTraceSnapshot === "function") {
      deps.recordMessageTraceSnapshot({
        lastSentUserMessage: {
          text: normalized,
          hash,
          messageId: Number.isFinite(messageId) ? messageId : null,
          source,
          updatedAt: new Date().toISOString(),
        },
      });
    }

    // 注意：不再在 MESSAGE_SENT 阶段清空 pendingRecallSendIntent /
    // pendingHostGenerationInputSnapshot / transactions。
    // 这些数据在 GENERATION_AFTER_COMMANDS 中被消费；MESSAGE_SENT 先于
    // GENERATION_AFTER_COMMANDS 触发，提前清空会导致召回拿不到用户输入。
    // 真正的消费发生在 recall 执行后（runRecallController 内部）。

    return nextRecord;
  }

  function clearRecallInputTracking() {
    clearPendingRecallSendIntent();
    setLastRecallSentUserMessage(createRecallInputRecord());
    clearPendingHostGenerationInputSnapshot();
    deps.clearPendingRerollRecallReuse?.("recall-input-tracking-cleared");
    if (typeof deps.recordMessageTraceSnapshot === "function") {
      deps.recordMessageTraceSnapshot({
        lastSentUserMessage: null,
      });
    }
    deps.clearPlannerRecallHandoffsForChat?.("", { clearAll: true });
  }

  return {
    freezeHostGenerationInputSnapshot,
    consumeHostGenerationInputSnapshot,
    getPendingHostGenerationInputSnapshot: readPendingHostGenerationInputSnapshot,
    clearPendingHostGenerationInputSnapshot,
    recordRecallSendIntent,
    clearPendingRecallSendIntent,
    recordRecallSentUserMessage,
    getCurrentGenerationTrivialSkip,
    markCurrentGenerationTrivialSkip,
    clearCurrentGenerationTrivialSkip,
    consumeCurrentGenerationTrivialSkip,
    clearRecallInputTracking,
    getLastRecallSentUserMessage,
    getPendingRecallSendIntent,
  };
}
