/**
 * ChatGPTAdapter - ChatGPT 平台适配器（基于真实 DOM 结构 2025）
 *
 * 核心策略：边滚动边提取（解决虚拟滚动竞态问题）
 *
 *   ChatGPT 使用 IntersectionObserver 驱动的虚拟滚动：
 *   - 消息进入视口 → 渲染内容
 *   - 消息离开视口 → 卸载内容（data-is-intersecting=false）
 *
 *   因此必须在消息渲染时立刻提取，不能等到全部滚完再统一提取。
 *
 * 流程：
 *   1. 从顶到底逐步滚动
 *   2. 每次滚动后，立刻提取所有当前 intersecting=true 的 section 内容
 *   3. 用 Map<turnId, message> 缓存，已提取的不重复提取
 *   4. 全部滚完后，返回按 section 顺序排列的完整消息列表
 *
 * 对话结构：
 *   div[data-turn-id-container][data-is-intersecting="true|false"]
 *     └── section[data-turn="user"|"assistant"][data-turn-id="xxx"]
 *           ├── (user)      div.whitespace-pre-wrap  ← 纯文本
 *           └── (assistant) div.markdown.prose       ← 富文本 HTML
 *
 * 操作栏：
 *   div[role="group"][aria-label="回复操作"|"你的消息操作"|"Message actions"]
 */

const SELECTORS = {
  turnSection: 'section[data-turn]',
  toolbar: [
    '[role="group"][aria-label="回复操作"]',
    '[role="group"][aria-label="你的消息操作"]',
    '[role="group"][aria-label="Message actions"]',
    '[role="group"][aria-label="Your message actions"]',
    '[role="group"][aria-label="Response actions"]',
  ].join(', '),
  assistantContent: '.markdown, [class*="prose"]',
  userContent: '.whitespace-pre-wrap, [class*="whitespace-pre-wrap"]',
};

class ChatGPTAdapter extends BaseAdapter {
  constructor() {
    super();
    // 消息缓存：turnId → message（边滚边填充）
    this._messageCache = new Map();
  }

  isMatch() {
    return (
      location.hostname === 'chatgpt.com' ||
      location.hostname === 'chat.openai.com'
    );
  }

  getToolbarAnchors() {
    return Array.from(document.querySelectorAll(SELECTORS.toolbar));
  }

  extractMessageFromAnchor(anchorEl) {
    let el = anchorEl;
    let depth = 0;
    while (el && depth < 20) {
      if (el.tagName === 'SECTION' && el.hasAttribute('data-turn')) {
        return this._extractFromSection(el);
      }
      el = el.parentElement;
      depth++;
    }
    return null;
  }

  /**
   * 返回按 section 顺序排列的完整消息列表
   * 必须在 ensureAllRendered() 之后调用
   */
  extractAllMessages() {
    const messages = [];
    const sections = document.querySelectorAll(SELECTORS.turnSection);

    sections.forEach((section, index) => {
      const role = section.getAttribute('data-turn');
      if (!role || (role !== 'user' && role !== 'assistant')) return;

      const turnId = section.getAttribute('data-turn-id') || `turn-${index}`;

      // 优先从缓存取（边滚边提取时已存入）
      if (this._messageCache.has(turnId)) {
        messages.push(this._messageCache.get(turnId));
        return;
      }

      // 缓存里没有，说明该 section 在滚动过程中始终未渲染
      // 尝试实时提取（可能仍在视口内）
      const result = this._extractFromSection(section);
      if (result) {
        result.id = turnId;
        messages.push(result);
      }
    });

    return messages;
  }

  /**
   * 边滚动边提取：从顶到底滚动，每步提取已渲染的 section 内容
   */
  async ensureAllRendered() {
    // 重置缓存
    this._messageCache = new Map();

    const scrollEl = this._findScrollContainer();
    if (!scrollEl) return;

    const viewH = scrollEl.clientHeight || window.innerHeight;
    const step = Math.floor(viewH / 2);

    // 滚到顶部，等待顶部渲染
    scrollEl.scrollTop = 0;
    await _sleep(400);

    // 提取当前视口内已渲染的内容
    this._extractRenderedSections();

    // 逐步向下滚动，边滚边提取
    let safety = 0;
    while (safety < 300) {
      const before = scrollEl.scrollTop;
      scrollEl.scrollTop += step;
      await _sleep(300); // 等待 IntersectionObserver 触发渲染
      this._extractRenderedSections(); // 立刻提取当前已渲染的内容
      if (Math.abs(scrollEl.scrollTop - before) < 2) break;
      safety++;
    }

    // 到底后额外等待并再次提取
    await _sleep(300);
    this._extractRenderedSections();

    // 滚回顶部（让顶部消息保持渲染状态，以防 extractAllMessages 需要兜底）
    scrollEl.scrollTop = 0;
    await _sleep(100);
  }

  /**
   * 提取当前所有 intersecting=true 的 section，存入缓存
   */
  _extractRenderedSections() {
    const sections = document.querySelectorAll(SELECTORS.turnSection);
    sections.forEach((section, index) => {
      const role = section.getAttribute('data-turn');
      if (!role || (role !== 'user' && role !== 'assistant')) return;

      const turnId = section.getAttribute('data-turn-id') || `turn-${index}`;

      // 已缓存的跳过
      if (this._messageCache.has(turnId)) return;

      // 检查是否已渲染（intersecting=true 且有内容）
      const container = section.closest('[data-is-intersecting]');
      const isRendered = !container || container.getAttribute('data-is-intersecting') !== 'false';
      if (!isRendered) return;

      const result = this._extractFromSection(section);
      if (result && result.content) {
        result.id = turnId;
        this._messageCache.set(turnId, result);
      }
    });
  }

  /**
   * 精确查找 ChatGPT 的滚动容器
   * 真实结构：滚动容器在 main 的父级，而非 main 本身
   */
  _findScrollContainer() {
    const firstSection = document.querySelector('section[data-turn]');
    if (firstSection) {
      let el = firstSection.parentElement;
      while (el && el !== document.documentElement) {
        const style = window.getComputedStyle(el);
        const oy = style.overflowY;
        if ((oy === 'auto' || oy === 'scroll') && el.scrollHeight > el.clientHeight + 50) {
          return el;
        }
        el = el.parentElement;
      }
    }

    const main = document.querySelector('main');
    if (main && main.parentElement) {
      let el = main.parentElement;
      while (el && el !== document.documentElement) {
        const style = window.getComputedStyle(el);
        const oy = style.overflowY;
        if ((oy === 'auto' || oy === 'scroll') && el.scrollHeight > el.clientHeight + 50) {
          return el;
        }
        el = el.parentElement;
      }
    }

    return document.documentElement;
  }

  _extractFromSection(section) {
    const role = section.getAttribute('data-turn');
    if (!role) return null;

    let contentNode = null;

    if (role === 'assistant') {
      contentNode = section.querySelector(SELECTORS.assistantContent);
    } else {
      contentNode = section.querySelector(SELECTORS.userContent);
      if (!contentNode) {
        contentNode = section.querySelector('[data-message-author-role]');
      }
    }

    const content = contentNode
      ? (contentNode.innerText || contentNode.textContent || '').trim()
      : '';

    return {
      role,
      content,
      contentNode,
      hasCode: !!(section.querySelector('pre code') || section.querySelector('pre')),
    };
  }

  getConversationTitle() {
    const title = document.title;
    if (title && title !== 'ChatGPT') {
      return title.replace(/\s*[-–]\s*ChatGPT\s*$/i, '').trim() || 'AI Chat';
    }
    const activeNav = document.querySelector('nav [aria-current="page"]');
    if (activeNav) {
      const text = activeNav.textContent.trim();
      if (text) return text;
    }
    return 'AI Chat';
  }
}

function _sleep(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}
