import { clsx, type ClassValue } from 'clsx';
import { twMerge } from 'tailwind-merge';
import { UIDataTypes, UIMessagePart, UITools } from 'ai';
import { Attachment, IFilledField, IQueryResult } from '@baishuyun/types';
import { FORM_ICON_OPTIONS } from '@/const/ui';

export function cn(...inputs: ClassValue[]) {
  return twMerge(clsx(inputs));
}

export const extractTextPartMeta = (part: any) => {
  if (part?.type !== 'text') {
    return null;
  }

  if (!part.providerMetadata) {
    return null;
  }

  if (!part.providerMetadata.raw || !part.providerMetadata.raw.data) {
    return null;
  }

  if (typeof part.providerMetadata.raw.data !== 'string') {
    return part.providerMetadata.raw.data;
  }

  let payload;
  try {
    payload = JSON.parse(part.providerMetadata.raw.data);
  } catch (e) {
    console.error('invalid raw data');
  }

  return payload || part.providerMetadata.raw.data;
};

export const IsFieldsJsonPart = (part: any): boolean => {
  const payload = extractTextPartMeta(part);
  return payload?.type === 'mcp-fields-json';
};

export const IsQueryResultPart = (part: any): boolean => {
  const payload = extractTextPartMeta(part);
  return payload?.type === 'query-stream-parsed-info';
};

export const GetQueryResult = (part: any): IQueryResult | null => {
  const payload = extractTextPartMeta(part);
  if (payload?.type !== 'query-stream-parsed-info') {
    return null;
  }

  const result = payload.result;
  console.log('query result raw data:', result);
  try {
    return JSON.parse(result);
  } catch (e) {
    console.error('invalid query result data');
    return null;
  }
};

export const IsErrorPart = (part: any): boolean => {
  if (part?.type === 'error') {
    return true;
  }

  const payload = extractTextPartMeta(part);
  return payload?.type === 'agent-error';
};

export const IsFakeMsgPart = (part: any): boolean => {
  const payload = extractTextPartMeta(part);
  return payload?.type === 'agent-fake-msg';
};

export const IsSpecialPart = (part: any): boolean => {
  const payload = extractTextPartMeta(part);
  return payload?.specialPart === true;
};

export const IsAgentCallingPart = (part: any): boolean => {
  const payload = extractTextPartMeta(part);
  return payload?.type === 'agent-calling';
};

export const GetSpecialPartType = (part: any): string | null => {
  const payload = extractTextPartMeta(part);
  if (payload?.specialPart === true) {
    return payload.type || null;
  }

  return null;
};

export const IsFilePart = (part: any): boolean => {
  return part?.type === 'file';
};

export const IsFileGroupPart = (part: any) => {
  if (!Array.isArray(part)) {
    return false;
  }

  // any one is file part
  for (const p of part) {
    if (p?.type === 'file') {
      return true;
    }
  }

  return false;
};

export const IsLoadingPart = (part: any): boolean => {
  const payload = extractTextPartMeta(part);
  return payload?.type === 'agent-loading';
};

export const GetLoadingMsg = (part: any): string => {
  const payload = extractTextPartMeta(part);
  if (payload?.type !== 'agent-loading') {
    return '';
  }

  return payload?.label || '加载中';
};

export const GetPartErrMsg = (part: any): string => {
  if (!IsErrorPart(part)) {
    return '';
  }

  if (part?.errorText) {
    return part.errorText;
  }

  const payload = extractTextPartMeta(part);
  return payload?.error || '未知错误';
};

export const getMetaErrorMsg = (part: any): string => {
  if (part?.errorText) {
    return part.errorText;
  }

  const payload = extractTextPartMeta(part);
  return payload?.error || '未知错误';
};

export const GetFieldJsonInfo = (part: any): IFilledField | null => {
  const payload = extractTextPartMeta(part);
  if (payload?.type !== 'mcp-fields-json') {
    return null;
  }

  return payload?.field || null;
};

export const IsSuggesstionPart = (part: any): boolean => {
  const payload = extractTextPartMeta(part);
  return payload?.type === 'suggestion';
};

export const IsMockSuggesstionPart = (part: any): boolean => {
  const payload = extractTextPartMeta(part);
  return payload?.type === 'mock-mcp-fields-sugession';
};

export const NeedToAppendFieldsSaveConfirm = (part: any): boolean => {
  const payload = extractTextPartMeta(part);
  return payload?.appendConfirm === 'save-fields';
};

export const extractFormName = (origin: string): string => {
  const regex = /现在要创建一张表：“([^”]+)”/g;
  const result = regex.exec(origin)?.[1] || '';
  return result.replace(/（.*\）/, '');
};

export function sanitizeText(text: string) {
  return text.replace('<has_function_call>', '');
}

/**
 * 合并parts数组中连续相同的元素，以连续组的最后一个元素为准
 * @param {Array} parts - 待合并的原数组
 * @param {Function} isTheSamePart - 判断两个part是否相同的方法（接收a、b两个参数，返回布尔值）
 * @returns {Array} 合并后的数组
 */
export function mergeParts(
  parts: UIMessagePart<UIDataTypes, UITools>[],
  isTheSamePart: (
    p0: UIMessagePart<UIDataTypes, UITools>,
    p1: UIMessagePart<UIDataTypes, UITools>
  ) => boolean
) {
  // 边界处理：如果原数组为空，直接返回空数组
  if (!Array.isArray(parts) || parts.length === 0) {
    return [];
  }

  const mergedParts = [parts[0]];

  for (let i = 1; i < parts.length; i++) {
    const currentPart = parts[i];
    const lastMergedPart = mergedParts[mergedParts.length - 1]; // 结果数组的最后一个元素

    if (isTheSamePart(lastMergedPart, currentPart)) {
      mergedParts[mergedParts.length - 1] = currentPart;
    } else {
      mergedParts.push(currentPart);
    }
  }

  return mergedParts;
}

// 复制文本的核心函数（TypeScript 版本）
export async function copyTextToClipboard(text: string): Promise<boolean> {
  // 校验输入：确保复制的文本是字符串且非空
  if (typeof text !== 'string' || text.trim() === '') {
    console.error('要复制的文本必须是非空字符串');
    return false;
  }

  try {
    // 方案1：优先使用现代浏览器的 Clipboard API（更安全、无DOM操作）
    if (navigator.clipboard && window.isSecureContext) {
      await navigator.clipboard.writeText(text);
      console.log('文本复制成功（Clipboard API）');
      return true;
    }

    // 方案2：降级使用 execCommand（兼容旧浏览器，需创建临时DOM元素）
    // 1. 创建临时textarea元素（隐藏，避免影响页面布局）
    const textarea = document.createElement('textarea');
    textarea.value = text;
    textarea.style.position = 'fixed'; // 避免滚动条偏移
    textarea.style.left = '-9999px';
    textarea.style.top = '-9999px';
    document.body.appendChild(textarea);

    // 2. 选中文本并执行复制
    textarea.focus();
    textarea.select();
    const success = document.execCommand('copy');

    // 3. 移除临时元素
    document.body.removeChild(textarea);

    if (success) {
      console.log('文本复制成功（execCommand 降级方案）');
      return true;
    } else {
      throw new Error('execCommand 复制失败');
    }
  } catch (error) {
    // 捕获复制失败的异常（如用户拒绝权限、浏览器不支持等）
    console.error('文本复制失败：', error);
    return false;
  }
}

/**
 * 合并数组中连续的目标元素（至少2个）为子数组（单函数实现）
 * @template T - 数组元素的类型
 * @param {T[]} arr - 原始数组
 * @param {(item: T) => boolean} isTarget - 判断元素是否为目标的函数（返回布尔值）
 * @returns {(T | T[])[]} 处理后的数组（元素可能是原类型或原类型的子数组）
 */
export function mergeConsecutiveTargets<T>(
  arr: T[],
  isTarget: (item: T) => boolean,
  minWidth = 1
): (T | T[])[] {
  // 存储最终结果
  const result: (T | T[])[] = [];
  // 临时收集连续的目标元素
  const temp: T[] = [];

  // 遍历原始数组
  for (const item of arr) {
    if (isTarget(item)) {
      // 是目标元素，加入临时数组
      temp.push(item);
    } else {
      // 不是目标元素：先处理临时数组（内联原辅助函数逻辑）
      if (temp.length > 0) {
        // 连续≥ minWidth 个目标元素 → 合并为子数组
        result.push(temp.length >= minWidth ? [...temp] : temp[0]);
        temp.length = 0; // 清空临时数组
      }
      // 将当前非目标元素加入结果
      result.push(item);
    }
  }

  // 处理遍历结束后剩余的临时数组（末尾可能全是目标元素）
  if (temp.length > 0) {
    result.push(temp.length >= 2 ? [...temp] : temp[0]);
  }

  return result;
}

export const mergeFileParts = (parts: Array<UIMessagePart<UIDataTypes, UITools>>) => {
  return mergeConsecutiveTargets<UIMessagePart<UIDataTypes, UITools>>(
    parts,
    (part) => {
      return part.type === 'file';
    },
    2
  );
};

export const getIconValue = (iconCode?: string): number => {
  if (!iconCode) {
    return 33;
  }

  const options = FORM_ICON_OPTIONS;
  const option = options.find((opt) => opt.code === iconCode);

  return option ? option.value : 33;
};

export const getIconOption = (iconValue?: number) => {
  const options = FORM_ICON_OPTIONS;
  const option = options.find((opt) => opt.value === iconValue);

  return option || options[8];
};

export const ensureEndSlash = (url: string): string => {
  if (!url) {
    return '';
  }

  return url.endsWith('/') ? url : `${url}/`;
};

export const generatePhoneFromTimestamp = (): string => {
  const timestamp = Date.now().toString();
  // 第二位取 3-9 的随机数，避免过于规律
  const secondDigit = 3 + Math.floor(Math.random() * 7);
  // 取时间戳后9位，如果不足则前面补零
  const suffix = timestamp.slice(-9).padStart(9, '0');
  return `1${secondDigit}${suffix}`;
};

export const shortTimeHash = (length = 4) => {
  const time = Date.now().toString(36); // 时间转36进制
  const random = Math.random().toString(36).slice(2, 4); // 防碰撞
  const combined = time + random;

  // 进一步混淆
  let hash = '';
  for (let i = 0; i < combined.length && hash.length < length; i++) {
    hash += combined.charCodeAt(i).toString(36);
  }

  return hash.slice(0, length);
};

export const generateUniqueValue = (numOnly?: boolean) => {
  if (numOnly) {
    return generatePhoneFromTimestamp();
  }

  return shortTimeHash(4);
};

/**
 * Convert an image URL to a base64 data URL via canvas.
 * Returns the original URL on failure (e.g. CORS).
 */
export const imageUrlToBase64 = (url: string, mediaType = 'image/png'): Promise<string> =>
  new Promise((resolve) => {
    const img = new Image();
    img.crossOrigin = 'anonymous';
    img.onload = () => {
      const canvas = document.createElement('canvas');
      canvas.width = img.naturalWidth;
      canvas.height = img.naturalHeight;
      canvas.getContext('2d')!.drawImage(img, 0, 0);
      resolve(canvas.toDataURL(mediaType));
    };
    img.onerror = () => resolve(url);
    img.src = url;
  });

export const isValidJSON = (str: string): boolean => {
  if (!str || typeof str !== 'string') return false;
  try {
    JSON.parse(str);
    return true;
  } catch {
    return false;
  }
};

export const formatJSONIfValid = (str: string): string | null => {
  try {
    const parsed = JSON.parse(str);
    console.log('try parse JSON: ', str);
    return JSON.stringify(parsed, null, 2);
  } catch {
    return null;
  }
};

const MIME_MAP: Record<string, string> = {
  // 文档
  xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  xls: 'application/vnd.ms-excel',
  docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  pdf: 'application/pdf',
  txt: 'text/plain',
  // 图片
  png: 'image/png',
  jpg: 'image/jpeg',
  jpeg: 'image/jpeg',
  gif: 'image/gif',
  webp: 'image/webp',
  svg: 'image/svg+xml',
  // 音视频
  mp4: 'video/mp4',
  mp3: 'audio/mpeg',
  // 压缩包
  zip: 'application/zip',
  rar: 'application/x-rar-compressed',
  '7z': 'application/x-7z-compressed',
  // 代码/数据
  json: 'application/json',
  js: 'application/javascript',
  ts: 'application/typescript',
  csv: 'text/csv',
  xml: 'application/xml',
  // 默认回退
  default: 'application/octet-stream',
};

const getMimeType = (filename: string, serverType: string | null): string => {
  // 如果服务器给了具体类型（不是 octet-stream），就信任它
  if (serverType && serverType !== 'application/octet-stream') {
    return serverType;
  }

  // 否则从扩展名推断
  const ext = filename.split('.').pop()?.toLowerCase() || '';
  return MIME_MAP[ext] || MIME_MAP.default;
};

/**
 * 从 URL 下载文件，返回 File 对象
 */
export const downloadFile = async (url: string): Promise<File> => {
  const response = await fetch(url, {
    // credentials: 'include', // 必须携带，因为 Allow-Credentials: true
    mode: 'cors',
  });

  if (!response.ok) {
    throw new Error(`Failed to download: ${response.status}`);
  }

  const blob = await response.blob();

  // 1. 从 Content-Disposition 解析文件名（优先）
  const disposition = response.headers.get('content-disposition');
  let filename = 'attachment';

  if (disposition) {
    // 尝试 filename*=UTF-8'' 格式（RFC 5987）
    const utf8Match = disposition.match(/filename\*=UTF-8''([^;]+)/i);
    // 尝试 filename="..." 或 filename=... 格式（需要解码 MinIO 的 URL 编码）
    const asciiMatch = disposition.match(/filename=["']?([^"';]+)["']?/i);

    if (utf8Match) {
      filename = decodeURIComponent(utf8Match[1]);
    } else if (asciiMatch) {
      // MinIO 特殊处理：filename 可能是 URL 编码的
      filename = decodeURIComponent(asciiMatch[1]);
    }
  }

  // 2. 修正 MIME 类型（如果服务器返回的是 octet-stream）
  let contentType = response.headers.get('content-type') || 'application/octet-stream';
  if (contentType === 'application/octet-stream') {
    contentType = getMimeType(filename, contentType);
  }

  return new File([blob], filename, { type: contentType });
};

/**
 * 上传文件到指定 endpoint，返回 Attachment 信息
 */
export const uploadFile = async (
  file: File,
  uploadEndpoint: string,
  signal?: AbortSignal
): Promise<Attachment | undefined> => {
  const formData = new FormData();
  formData.append('file', file);

  try {
    const response = await fetch(uploadEndpoint, {
      method: 'POST',
      body: formData,
      signal,
    });

    if (response.ok) {
      return await response.json();
    }
    const { error } = await response.json();
    console.error('upload error', error);
    return undefined;
  } catch (error) {
    console.error('Error uploading file:', error);
    return undefined;
  }
};

export const urls2fileParts = async (attachmentsUrl: string[], uploadEndpoint: string) => {
  if (!attachmentsUrl?.length) {
    return [];
  }

  let results: (Attachment | undefined)[];
  try {
    results = await Promise.all(
      attachmentsUrl.map(async (url) => {
        const file = await downloadFile(url);
        return uploadFile(file, uploadEndpoint);
      })
    );
  } catch (error) {
    console.error('Error processing attachments:', error);
    return [];
  }

  return results
    .filter((attachment): attachment is Attachment => attachment !== undefined)
    .map((attachment) => ({
      type: 'file' as const,
      url: attachment.url,
      name: attachment.name,
      mediaType: attachment.contentType,
      providerMetadata: {
        uploadMetadata: attachment,
      },
    }));
};
