import dayjs from "dayjs";

/**
 * 判断聊天列表中某条消息上方是否需要展示时间分隔。
 *
 * 典型用法：在 `v-for` 渲染消息时，根据相邻两条消息的时间差决定是否插入时间条。
 *
 * 规则：
 * - 第一条消息始终展示时间
 * - 其他消息：与上一条消息时间差大于 `gapMs` 时展示
 *
 * 边界与兼容：
 * - `messageList` 不是数组 / 为空：返回 `false`
 * - `sendDate` 为空或无法解析为有效时间戳：返回 `false`
 *
 * @param {Array<{ sendDate?: string }>} messageList - 消息数组（按时间正序）
 * @param {number} index - 当前消息在数组中的下标
 * @param {number} gapMs - 时间间隔阈值（毫秒），默认 30 分钟
 * @returns {boolean} - 是否需要展示时间
 */
export function shouldShowTime(messageList, index, gapMs = 30 * 60 * 1000) {
  if (!Array.isArray(messageList) || !messageList.length) return false;
  if (index === 0) return true;

  const current = messageList[index];
  const prev = messageList[index - 1];
  // `sendDate` 期望为 ISO 字符串或能被 Date 解析的字符串
  const currentTime = new Date(current && current.sendDate).getTime();
  const prevTime = new Date(prev && prev.sendDate).getTime();

  if (Number.isNaN(currentTime) || Number.isNaN(prevTime)) return false;
  return currentTime - prevTime > gapMs;
}

/**
 * 将消息发送时间格式化为聊天页可展示的文本。
 *
 * 规则（与页面原逻辑一致）：
 * - 60 秒内：`刚刚`
 * - 1 小时内：`x分钟前`
 * - 今天：`今天 HH:mm`
 * - 昨天：`昨天 HH:mm`
 * - 更早但在今年：`MM月DD日 HH:mm`
 * - 往年：`YYYY年MM月DD日 HH:mm`
 *
 * 说明：
 * - 使用 `dayjs` 进行日期差计算与格式化，避免手写边界处理。
 * - 若传入的 `sendDate` 不可解析，dayjs 会返回 Invalid Date，
 *   此时 diff 结果可能不符合预期；上层建议确保 sendDate 为后端返回的时间字符串。
 *
 * @param {string} sendDate - 消息发送时间（后端返回的时间字符串）
 * @returns {string} - 格式化后的展示文本
 */
export function formatChatShowTime(sendDate) {
  const now = dayjs();
  const msgTime = dayjs(sendDate);

  if (now.diff(msgTime, "second") < 60) {
    return "刚刚";
  }
  if (now.diff(msgTime, "hour") < 1) {
    return `${now.diff(msgTime, "minute")}分钟前`;
  }

  // 使用 startOf('day') 抹平时分秒，方便按“天”比较
  const today = dayjs().startOf("day");
  const msgDay = dayjs(sendDate).startOf("day");
  const dayDiff = today.diff(msgDay, "day");

  if (dayDiff === 0) {
    return `今天 ${msgTime.format("HH:mm")}`;
  }
  if (dayDiff === 1) {
    return `昨天 ${msgTime.format("HH:mm")}`;
  }

  // 使用 startOf('year') 抹平年月日，方便按“年”比较
  const thisYear = dayjs().startOf("year");
  const msgYear = dayjs(sendDate).startOf("year");
  const yearDiff = thisYear.diff(msgYear, "year");

  if (yearDiff === 0) {
    return `${msgTime.format("MM月DD日 HH:mm")}`;
  }

  return `${msgTime.format("YYYY年MM月DD日 HH:mm")}`;
}

/**
 * 通过计算内容容器高度的方式，滚动到底部（与首页实现保持一致）。
 *
 * 为什么不用 `scroll-into-view`：
 * - 私聊场景经常需要“追加/插入消息”并保持当前视口稳定
 * - `scroll-into-view` 在某些端会触发额外的自动滚动/动画，造成跳动体验
 *
 * 工作方式：
 * - 使用 `uni.createSelectorQuery().in(vm)` 取到组件内 `selector` 对应节点高度
 * - 以节点高度作为 `scrollTop` 的目标值，并加上 `extraOffset` 作为底部缓冲
 *
 * 使用要求：
 * - `vm` 必须是 Vue 组件实例（需要 `vm.$nextTick` 与 `vm.scrollTop`）
 * - `selector` 对应的节点需要存在且能计算出 `boundingClientRect`
 *
 * @param {any} vm - Vue 组件实例（一般直接传 `this`）
 * @param {{ selector?: string; extraOffset?: number }} options - 选择器与额外偏移
 */
export function scrollToBottomByContentHeight(
  vm,
  { selector = ".chat-content", extraOffset = 200 } = {},
) {
  if (!vm) return;

  vm.$nextTick(() => {
    const query = uni.createSelectorQuery().in(vm);
    query
      .select(selector)
      .boundingClientRect((data) => {
        if (data) {
          // 这里用高度作为 scrollTop 目标值即可“贴底”，额外加一点缓冲避免极端情况没到最底
          vm.scrollTop = Number(data.height || 0) + Number(extraOffset || 0);
        }
      })
      .exec();
  });
}

/**
 * 处理聊天消息内容：若内容为 JSON 字符串且符合预期结构，则提取并拼接为可读文本。
 *
 * 说明：
 * - 首页对话存在部分消息内容为 JSON 字符串的情况（如包含 `answerTypeLabel/imageUrl/nearbyPaths`）
 * - 这里做“尽量解析、解析失败回退原文”的处理，避免误伤普通文本消息
 *
 * @param {string} message - 原始消息文本
 * @returns {string} - 处理后的文本
 */
export function processChatMessageContent(message) {
  try {
    const parsed = JSON.parse(message);
    if (typeof parsed === "object" && parsed !== null) {
      let content = "";
      const { answerTypeLabel, imageUrl, nearbyPaths } = parsed;

      if (answerTypeLabel) content += `${answerTypeLabel}\n`;
      if (imageUrl) content += `${imageUrl}\n`;
      if (nearbyPaths) content += `${nearbyPaths}\n`;

      return content ? content.trim() : message;
    }
  } catch (e) {
    return message;
  }

  return message;
}

// 解析 sendDate 为微秒级时间戳（防止毫秒级精度丢失）
function getSendDateMicroValue(sendDate) {
  const raw = String(sendDate || "").trim();
  if (!raw) return Number.NaN;
  const match = raw.match(/^(.+?)(?:\.(\d+))?(Z|[+-]\d{2}:?\d{2})?$/);
  if (!match) {
    const ms = new Date(raw).getTime();
    return Number.isNaN(ms) ? Number.NaN : ms * 1000;
  }

  const base = match[1] || "";
  const fraction = match[2] || "";
  const zone = match[3] || "";
  const baseMs = new Date(`${base}${zone}`).getTime();
  if (Number.isNaN(baseMs)) return Number.NaN;

  const microStr = `${fraction}000000`.slice(0, 6);
  const microValue = Number(microStr);
  return baseMs * 1000 + microValue;
}

/**
 * 聊天消息排序：按 `sendDate` 升序；时间相同时，用户消息（`interactMode=0`）排在前面。
 * 同时会对每条消息的 `message` 字段进行 `processChatMessageContent` 处理。
 *
 * @param {Array<any>} list - 会话详情接口返回的消息列表
 * @returns {Array<any>} - 处理并排序后的新数组
 */
export function sortChatMessages(list = []) {
  const processedList = (list || []).map((item) => ({
    ...item,
    rawMessage: item && item.message,
    message: processChatMessageContent(item && item.message),
  }));

  return processedList.sort((a, b) => {
    const timeA = getSendDateMicroValue(a && a.sendDate);
    const timeB = getSendDateMicroValue(b && b.sendDate);
    if (Number.isNaN(timeA) && Number.isNaN(timeB)) {
      return (a && a.interactMode) - (b && b.interactMode);
    }
    if (Number.isNaN(timeA)) return 1;
    if (Number.isNaN(timeB)) return -1;
    if (timeA === timeB) return (a && a.interactMode) - (b && b.interactMode);
    return timeA - timeB;
  });
}

/**
 * 将 Date 格式化为 `HH:mm`。
 *
 * @param {Date} date - 时间对象
 * @returns {string} - 格式化字符串
 */
export function formatHHmm(date) {
  if (!(date instanceof Date) || Number.isNaN(date.getTime())) return "";
  const hours = date.getHours().toString().padStart(2, "0");
  const minutes = date.getMinutes().toString().padStart(2, "0");
  return `${hours}:${minutes}`;
}
