import type { Message, MessageContent } from '../types';
import { mergeMessageContent } from '../../message-stream.mjs';
import { stripUserAddressPrefix } from './userAddress';

const IMAGE_URL_LINE_RE = /^\[图片\d+]: (.+)$/;

function formatImageUrlsForAgentText(urls: string[]): string {
  return urls
    .filter((url) => typeof url === 'string' && url.trim())
    .map((url, index) => `[图片${index + 1}]: ${url.trim()}`)
    .join('\n');
}

function parseImageUrlsFromText(text: string): string[] {
  const urls: string[] = [];
  for (const line of text.split('\n')) {
    const match = line.trim().match(IMAGE_URL_LINE_RE);
    if (match?.[1]) urls.push(match[1]);
  }
  return urls;
}

function readLegacyImageUrls(content: MessageContent[]): string[] {
  const urls: string[] = [];
  for (const item of content) {
    if (item.type === 'text') {
      urls.push(...parseImageUrlsFromText(item.text));
      continue;
    }
    const legacy = item as MessageContent & {
      type?: string;
      image_url?: { url?: string };
      data?: string;
      mimeType?: string;
    };
    if (legacy.type === 'image_url' && legacy.image_url?.url) {
      urls.push(legacy.image_url.url);
    }
    if (legacy.type === 'image' && legacy.data && legacy.mimeType) {
      urls.push(`data:${legacy.mimeType};base64,${legacy.data}`);
    }
  }
  return urls;
}

/** Keep only text blocks so upstream relay providers accept the payload. */
export function normalizeUserMessageForApi(message: Message): Message {
  if (message.role !== 'user') return message;

  const imageUrls = getImageUrls(message);
  const displayText =
    message.metadata.displayText ??
    message.content
      .filter((item): item is Extract<MessageContent, { type: 'text' }> => item.type === 'text')
      .map((item) => item.text)
      .join('\n')
      .replace(/\n*\[图片\d+]: [^\n]+/g, '')
      .trim();

  const textOnly = message.content
    .filter((item): item is Extract<MessageContent, { type: 'text' }> => item.type === 'text')
    .map((item) => item.text)
    .join('\n')
    .trim();

  if (imageUrls.length === 0 && message.content.every((item) => item.type === 'text')) {
    return message;
  }

  const imageText = formatImageUrlsForAgentText(imageUrls);
  const agentText = [textOnly.replace(/\n*\[图片\d+]: [^\n]+/g, '').trim(), imageText]
    .filter(Boolean)
    .join('\n\n');

  return {
    ...message,
    content: agentText ? [{ type: 'text', text: agentText }] : [],
    metadata: {
      ...message.metadata,
      displayText,
      ...(imageUrls.length ? { imageUrls } : {}),
    },
  };
}

export function buildUserMessage(
  text: string,
  options?: { agentText?: string; displayText?: string; imageUrls?: string[] },
): Message {
  const displayText = options?.displayText ?? text;
  const imageUrls = (options?.imageUrls ?? []).filter((value) => typeof value === 'string' && value.trim());
  const baseAgentText = options?.agentText ?? text;
  const imageText = formatImageUrlsForAgentText(imageUrls);
  const agentText = [baseAgentText.trim(), imageText].filter(Boolean).join('\n\n');

  return {
    id: crypto.randomUUID(),
    role: 'user',
    created: Math.floor(Date.now() / 1000),
    content: agentText ? [{ type: 'text', text: agentText }] : [],
    metadata: {
      userVisible: true,
      agentVisible: true,
      displayText,
      ...(imageUrls.length ? { imageUrls } : {}),
    },
  };
}

export function normalizeConversationMessages(messages: Message[]): Message[] {
  return messages.map((message) =>
    message.role === 'user' ? normalizeUserMessageForApi(message) : message,
  );
}

export function getDisplayText(message: Message): string {
  if ('displayText' in message.metadata) {
    return message.metadata.displayText ?? '';
  }
  const systemText = getSystemNotificationText(message);
  if (systemText) return systemText;
  const visible = getVisibleText(message);
  return message.role === 'user' ? stripUserAddressPrefix(visible) : visible;
}

export function pushMessage(messages: Message[], incoming: Message): Message[] {
  const last = messages[messages.length - 1];
  if (last?.id && incoming.id && last.id === incoming.id) {
    return [
      ...messages.slice(0, -1),
      {
        ...last,
        content: mergeMessageContent(last.content, incoming.content) as MessageContent[],
      },
    ];
  }
  return [...messages, incoming];
}

export function getVisibleText(message: Message): string {
  const text = message.content
    .filter((c): c is Extract<MessageContent, { type: 'text' }> => c.type === 'text')
    .map((c) => c.text)
    .join('');
  return text.replace(/\n*\[图片\d+]: [^\n]+/g, '').trim();
}

export function getImageUrls(message: Message): string[] {
  const fromMetadata = message.metadata.imageUrls?.filter(
    (url) => typeof url === 'string' && url.trim(),
  );
  if (fromMetadata?.length) return fromMetadata;

  const fromContent = readLegacyImageUrls(message.content);
  if (fromContent.length) return fromContent;

  const text = message.content
    .filter((item): item is Extract<MessageContent, { type: 'text' }> => item.type === 'text')
    .map((item) => item.text)
    .join('\n');
  return parseImageUrlsFromText(text);
}

export function getSystemNotificationText(message: Message): string | null {
  const item = message.content.find(
    (c): c is Extract<MessageContent, { type: 'systemNotification' }> =>
      c.type === 'systemNotification',
  );
  return item?.msg ?? null;
}

export function isCreditsExhaustedNotification(message: Message): boolean {
  return message.content.some(
    (c) => c.type === 'systemNotification' && c.notificationType === 'creditsExhausted',
  );
}

/** Goose surfaces upstream relay 500 as a visible assistant error message. */
export function isRelayServerErrorMessage(message: Message): boolean {
  if (message.role !== 'assistant') return false;
  const text = getVisibleText(message);
  if (!text) return false;
  return (
    /Server error \(500 Internal Server Error\)/i.test(text) &&
    /relay|andu\.tkmind\.cn/i.test(text)
  );
}

export function getThinking(message: Message): string | null {
  const parts = message.content
    .filter((c): c is Extract<MessageContent, { type: 'thinking' }> => c.type === 'thinking')
    .map((c) => c.thinking);
  return parts.length > 0 ? parts.join('') : null;
}

export function getToolConfirmation(message: Message) {
  const item = message.content.find(
    (c): c is Extract<MessageContent, { type: 'actionRequired' }> =>
      c.type === 'actionRequired' && c.data.actionType === 'toolConfirmation',
  );
  if (!item) return null;
  return {
    id: item.data.id,
    toolName: item.data.toolName,
    arguments: item.data.arguments,
    prompt: item.data.prompt,
  };
}
