import type { ChatStreamPayload, OpenAIChatMessage, UserMessageContentPart } from '../../types';
import { isThinkingWithToolClaudeModel } from './claudeModelId';

interface ClaudeThinkingHistoryMessageMetadata {
  model?: null | string;
  provider?: null | string;
  reasoning?: {
    content?: string;
    signature?: string;
  };
}

/**
 * Determine whether a historical assistant message was generated by Claude and
 * can safely be replayed as Claude signed thinking.
 */
const isClaudeThinkingHistoryMessage = (message: OpenAIChatMessage): boolean => {
  const { model, provider } = message as OpenAIChatMessage & ClaudeThinkingHistoryMessageMetadata;

  // Prefer the source model when it is available. Router conversations can switch
  // target providers mid-thread, and only Claude thinking-capable history can be
  // replayed to Claude as a signed `thinking` block.
  if (typeof model === 'string' && model) return isThinkingWithToolClaudeModel(model);

  return provider === 'anthropic' || provider === 'bedrock';
};

const hasSignedThinkingPart = (content: UserMessageContentPart[]): boolean =>
  content.some((part) => part.type === 'thinking' && !!part.signature);

const filterSignedThinkingParts = (content: UserMessageContentPart[]): UserMessageContentPart[] =>
  content.filter((part) => part.type !== 'thinking' || !part.signature);

const toContentParts = (
  content: OpenAIChatMessage['content'],
): UserMessageContentPart[] | undefined => {
  if (Array.isArray(content)) return content;
  if (typeof content === 'string' && content.trim()) return [{ text: content, type: 'text' }];
};

/**
 * Normalize historical assistant thinking only for Claude target runtimes.
 *
 * Anthropic validates `thinking.signature` against Claude-generated thinking.
 * Signatures from other providers (for example DeepSeek UUID-like values) must
 * not be forwarded as Claude thinking blocks, otherwise Claude rejects the
 * request before generation starts.
 *
 * Keep this out of the shared Anthropic-compatible context builder because
 * providers such as DeepSeek and Moonshot have their own thinking contracts.
 */
export const normalizeClaudeThinkingHistoryMessages = (
  messages: ChatStreamPayload['messages'],
): ChatStreamPayload['messages'] => {
  return messages.map((message) => {
    if (message.role !== 'assistant') return message;

    const content = toContentParts(message.content);
    const isClaudeThinkingMessage = isClaudeThinkingHistoryMessage(message);

    if (!isClaudeThinkingMessage && Array.isArray(content) && hasSignedThinkingPart(content)) {
      return {
        ...message,
        content: filterSignedThinkingParts(content),
      };
    }

    const { reasoning } = message as OpenAIChatMessage & ClaudeThinkingHistoryMessageMetadata;

    if (!isClaudeThinkingMessage || !reasoning?.content || !reasoning.signature) return message;
    if (content && hasSignedThinkingPart(content)) return message;

    return {
      ...message,
      content: [
        {
          signature: reasoning.signature,
          thinking: reasoning.content,
          type: 'thinking',
        },
        ...(content ?? []),
      ],
    };
  });
};
