import { CommandExecutionError } from '@jackwener/opencli/errors';

export const DOUBAO_DOMAIN = 'www.doubao.com';
export const DOUBAO_CHAT_URL = 'https://www.doubao.com/chat';
export const DOUBAO_NEW_CHAT_URL = 'https://www.doubao.com/chat/new-thread/create-by-msg';
const DOUBAO_COMPOSER_SELECTORS = [
    'textarea[data-testid="chat_input_input"]',
    '[data-testid="chat_input"] textarea',
    '.chat-input textarea',
    '.chat-input [contenteditable="true"]',
    '.chat-editor textarea',
    '.chat-editor [contenteditable="true"]',
    'textarea[placeholder*="发消息"]',
    'textarea[placeholder*="Message"]',
    '[contenteditable="true"][placeholder*="发消息"]',
    '[contenteditable="true"][placeholder*="Message"]',
    '[contenteditable="true"][aria-label*="发消息"]',
    '[contenteditable="true"][aria-label*="Message"]',
    'textarea',
    '[contenteditable="true"]',
];
function buildDoubaoComposerLocatorScript() {
    return `
    const isVisible = (el) => {
      if (!(el instanceof HTMLElement)) return false;
      const style = window.getComputedStyle(el);
      if (style.display === 'none' || style.visibility === 'hidden') return false;
      const rect = el.getBoundingClientRect();
      return rect.width > 0 && rect.height > 0;
    };

    const composerSelectors = ${JSON.stringify(DOUBAO_COMPOSER_SELECTORS)};
    const findComposer = () => {
      for (const selector of composerSelectors) {
        const node = Array.from(document.querySelectorAll(selector)).find(isVisible);
        if (node) return node;
      }
      return null;
    };
  `;
}
function getTranscriptLinesScript() {
    return `
    (() => {
      const clean = (value) => (value || '')
        .replace(/\\u00a0/g, ' ')
        .replace(/\\n{3,}/g, '\\n\\n')
        .trim();

      const root = document.body.cloneNode(true);
      const removableSelectors = [
        '[data-testid="flow_chat_sidebar"]',
        '[data-testid="chat_input"]',
        '[data-testid="flow_chat_guidance_page"]',
      ];

      for (const selector of removableSelectors) {
        root.querySelectorAll(selector).forEach((node) => node.remove());
      }

      root.querySelectorAll('script, style, noscript').forEach((node) => node.remove());

      const stopLines = new Set([
        '豆包',
        '新对话',
        '内容由豆包 AI 生成，请仔细甄别',
        'AI 创作',
        '云盘',
        '更多',
        '历史对话',
        '手机版对话',
        '快速',
        '超能模式',
        'Beta',
        'PPT 生成',
        '图像生成',
        '帮我写作',
        '请仔细甄别',
        '下载电脑版',
      ]);

      const noisyPatterns = [
        /^window\\._SSR_DATA/,
        /^window\\._ROUTER_DATA/,
        /^\{"namedChunks"/,
        /^在此处拖放文件/,
        /^文件数量：/,
        /^文件类型：/,
      ];

      const transcriptText = clean(root.innerText || root.textContent || '')
        .replace(/新对话/g, '\\n')
        .replace(/内容由豆包 AI 生成，请仔细甄别/g, '\\n')
        .replace(/在此处拖放文件/g, '\\n')
        .replace(/文件数量：[^\\n]*/g, '')
        .replace(/文件类型：[^\\n]*/g, '');

      return clean(transcriptText)
        .split('\\n')
        .map((line) => clean(line))
        .filter((line) => line
          && line.length <= 400
          && !stopLines.has(line)
          && !noisyPatterns.some((pattern) => pattern.test(line)));
    })()
  `;
}
function getStateScript() {
    return `
    (() => {
      const routerData = window._ROUTER_DATA?.loaderData?.chat_layout;
      const placeholderNode = document.querySelector(
        'textarea[data-testid="chat_input_input"], textarea[placeholder], [contenteditable="true"][placeholder], [aria-label*="发消息"], [aria-label*="Message"]'
      );
      return {
        url: window.location.href,
        title: document.title || '',
        isLogin: typeof routerData?.userSetting?.data?.is_login === 'boolean'
          ? routerData.userSetting.data.is_login
          : null,
        accountDescription: routerData?.accountInfo?.data?.description || '',
        placeholder: placeholderNode?.getAttribute('placeholder')
          || placeholderNode?.getAttribute('aria-label')
          || '',
      };
    })()
  `;
}
function getTurnsScript() {
    return `
    (() => {
      const clean = (value) => (value || '')
        .replace(/\\u00a0/g, ' ')
        .replace(/\\n{3,}/g, '\\n\\n')
        .trim();

      const isVisible = (el) => {
        if (!(el instanceof HTMLElement)) return false;
        const style = window.getComputedStyle(el);
        if (style.display === 'none' || style.visibility === 'hidden') return false;
        const rect = el.getBoundingClientRect();
        return rect.width > 0 && rect.height > 0;
      };

      const getRole = (root) => {
        if (
          root.matches('[data-testid="send_message"], [class*="send-message"]')
          || root.querySelector('[data-testid="send_message"], [class*="send-message"]')
          || root.matches('[class*="bg-g-send-msg-bubble"]')
          ||
          root.querySelector('[class*="bg-g-send-msg-bubble"]')
          || root.querySelector('[data-foundation-type="send-message-action-bar"]')
        ) {
          return 'User';
        }
        if (
          root.matches('[data-testid="receive_message"], [data-testid*="receive_message"], [class*="receive-message"]')
          || root.querySelector('[data-testid="receive_message"], [data-testid*="receive_message"], [class*="receive-message"]')
          || root.matches('[class*="bg-g-receive-msg-bubble"]')
          ||
          root.querySelector('[class*="bg-g-receive-msg-bubble"]')
          || root.querySelector('[data-foundation-type="receive-message-action-bar"]')
        ) {
          return 'Assistant';
        }
        // 2026-05 Doubao DOM refactor: no more receive-message / bg-g-receive-msg-bubble
        // markers on assistant turns. Wrappers are now [class*="inner-item-"] /
        // [class*="top-item-"] and the only reliable assistant signal is the
        // .flow-markdown-body / .md-box-root content container WITHOUT any
        // send-bubble marker.
        if (
          (root.matches('[class*="inner-item-"], [class*="top-item-"]')
            || root.closest('[class*="inner-item-"], [class*="top-item-"]'))
          && (root.matches('.flow-markdown-body, .md-box-root, [class*="md-box-root"]')
            || root.querySelector('.flow-markdown-body, .md-box-root, [class*="md-box-root"]'))
          && !root.matches('[class*="bg-g-send-msg-bubble"]')
          && !root.querySelector('[class*="bg-g-send-msg-bubble"]')
        ) {
          return 'Assistant';
        }
        return '';
      };

      const messageTextSelectors = [
        '[data-testid="message_text_content"]',
        '[data-testid="message_content"]',
        '[data-testid*="message_text"]',
        '[data-testid*="message_content"]',
        '[class*="message-text"]',
        '[class*="message-content"]',
        '[class*="bg-g-send-msg-bubble"]',
        '[class*="bg-g-receive-msg-bubble"]',
        '.flow-markdown-body',
        '.md-box-root',
        '[class*="md-box-root"]',
        '[class*="bubble"]',
      ];
      const messageImageSelector = messageTextSelectors.map((s) => s + ' img').join(', ');

      const extractTextChunks = (root) => {
        const chunks = [];
        const seen = new Set();
        for (const selector of messageTextSelectors) {
          const nodes = Array.from(root.querySelectorAll(selector))
            .filter((el) => isVisible(el))
            .map((el) => clean(el.innerText || el.textContent || ''))
            .filter(Boolean);

          for (const nodeText of nodes) {
            if (seen.has(nodeText)) continue;
            seen.add(nodeText);
            chunks.push(nodeText);
          }

          if (chunks.length > 0) break;
        }
        return chunks;
      };

      const extractImageLines = (root) => Array.from(root.querySelectorAll(messageImageSelector))
          .filter((el) => el instanceof HTMLImageElement && isVisible(el))
          .map((el) => {
            const width = el.naturalWidth || el.width || 0;
            const height = el.naturalHeight || el.height || 0;
            if (width > 0 && height > 0 && width <= 48 && height <= 48) return '';
            const url = clean(el.currentSrc || el.src || '');
            return /^https?:\\/\\//i.test(url) ? 'Image: ' + url : '';
          })
          .filter((line, index, items) => Boolean(line) && items.indexOf(line) === index);

      const extractText = (root) => {
        const chunks = extractTextChunks(root);
        const text = chunks.length > 0 ? clean(chunks.join('\\n')) : clean(root.innerText || root.textContent || '');
        const imageLines = extractImageLines(root);
        if (imageLines.length === 0) return text;
        return text ? text + '\\n' + imageLines.join('\\n') : imageLines.join('\\n');
      };

      const itemSelectors = [
        // 2026-05 Doubao DOM refactor wrappers (prepended; outer ones win via
        // ancestor-keep dedup below).
        '[class*="inner-item-"]',
        '[class*="top-item-"]',
        '[class*="item-kDun2N"]',
        '[data-testid="union_message"]',
        '[data-testid="message-block-container"]',
        '[data-message-id]',
        '[class*="bg-g-send-msg-bubble"]',
        '[class*="bg-g-receive-msg-bubble"]',
      ];

      const messageLists = Array.from(document.querySelectorAll('[class*="message-list-"], .container-PvPoAn, .scroll-view-OEiNXD, [data-testid="message-list"]'))
        .filter((el) => isVisible(el));
      if (messageLists.length === 0) return [];

      const allRoots = [];
      const seen = new Set();
      for (const messageList of messageLists) {
        for (const sel of itemSelectors) {
          messageList.querySelectorAll(sel).forEach((el) => {
            if (!seen.has(el)) {
              seen.add(el);
              allRoots.push(el);
            }
          });
        }
      }
      const roots = allRoots
        .filter((el) => isVisible(el) && !el.closest('script, style, noscript'))
        .filter((el, index, items) => !items.some((other, otherIndex) => otherIndex !== index && other.contains(el)));

      const turns = roots
        .map((el) => {
          const role = getRole(el);
          const text = extractText(el);
          return { el, role, text };
        })
        .filter((item) => (item.role === 'User' || item.role === 'Assistant') && item.text);

      turns.sort((a, b) => {
        if (a.el === b.el) return 0;
        const pos = a.el.compareDocumentPosition(b.el);
        return pos & Node.DOCUMENT_POSITION_FOLLOWING ? -1 : 1;
      });

      const deduped = [];
      const dedupedSeen = new Set();
      for (const turn of turns) {
        const key = turn.role + '::' + turn.text;
        if (dedupedSeen.has(key)) continue;
        dedupedSeen.add(key);
        deduped.push({ Role: turn.role, Text: turn.text });
      }

      if (deduped.length > 0) return deduped;
      return [];
    })()
  `;
}
function prepareDoubaoComposerScript() {
    return `
    (() => {
      ${buildDoubaoComposerLocatorScript()}
      const composer = findComposer();

      if (
        !(composer instanceof HTMLTextAreaElement)
        && !(composer instanceof HTMLInputElement)
        && !(composer instanceof HTMLElement)
      ) {
        return { ok: false, reason: 'Could not find Doubao input element' };
      }

      try {
        composer.focus();

        if (composer instanceof HTMLTextAreaElement || composer instanceof HTMLInputElement) {
          const length = composer.value.length;
          composer.setSelectionRange(0, length);
        } else {
          const selection = window.getSelection();
          const range = document.createRange();
          range.selectNodeContents(composer);
          selection?.removeAllRanges();
          selection?.addRange(range);
        }
      } catch (error) {
        return {
          ok: false,
          reason: error instanceof Error ? error.message : String(error),
        };
      }

      return { ok: true };
    })()
  `;
}
function composerStateScript() {
    return `
    (() => {
      ${buildDoubaoComposerLocatorScript()}
      const composer = findComposer();

      if (composer instanceof HTMLTextAreaElement || composer instanceof HTMLInputElement) {
        return { hasText: !!composer.value.trim(), text: composer.value };
      }

      if (composer instanceof HTMLElement) {
        const text = (composer.innerText || '').trim() || (composer.textContent || '').trim();
        return {
          hasText: !!text,
          text,
        };
      }

      return { hasText: false, text: '' };
    })()
  `;
}
function syncComposerAfterNativeTypeScript() {
    return `
    (() => {
      ${buildDoubaoComposerLocatorScript()}
      const composer = findComposer();

      if (composer instanceof HTMLTextAreaElement || composer instanceof HTMLInputElement) {
        const value = composer.value;
        composer.dispatchEvent(new InputEvent('beforeinput', { bubbles: true, data: value, inputType: 'insertText' }));
        composer.dispatchEvent(new InputEvent('input', { bubbles: true, data: value, inputType: 'insertText' }));
        composer.dispatchEvent(new Event('change', { bubbles: true }));
        return { hasText: !!value.trim(), text: value };
      }

      if (composer instanceof HTMLElement) {
        const text = (composer.innerText || '').trim() || (composer.textContent || '').trim();
        composer.dispatchEvent(new InputEvent('beforeinput', { bubbles: true, data: text, inputType: 'insertText' }));
        composer.dispatchEvent(new InputEvent('input', { bubbles: true, data: text, inputType: 'insertText' }));
        composer.dispatchEvent(new Event('change', { bubbles: true }));
        return { hasText: !!text, text };
      }

      return { hasText: false, text: '' };
    })()
  `;
}
function fillComposerScript(text) {
    return `
    ((inputText) => {
      ${buildDoubaoComposerLocatorScript()}
      const composer = findComposer();

      if (!composer) throw new Error('Could not find Doubao input element');

      composer.focus();

      if (composer instanceof HTMLTextAreaElement || composer instanceof HTMLInputElement) {
        const proto = composer instanceof HTMLTextAreaElement
          ? window.HTMLTextAreaElement.prototype
          : window.HTMLInputElement.prototype;
        const setter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
        setter?.call(composer, inputText);
        composer.dispatchEvent(new InputEvent('beforeinput', { bubbles: true, data: inputText, inputType: 'insertText' }));
        composer.dispatchEvent(new InputEvent('input', { bubbles: true, data: inputText, inputType: 'insertText' }));
        composer.dispatchEvent(new Event('change', { bubbles: true }));
        return { hasText: !!composer.value.trim(), mode: 'text-input', text: composer.value };
      }

      if (composer instanceof HTMLElement) {
        composer.textContent = '';
        const selection = window.getSelection();
        const range = document.createRange();
        range.selectNodeContents(composer);
        range.collapse(false);
        selection?.removeAllRanges();
        selection?.addRange(range);
        document.execCommand('insertText', false, inputText);
        composer.dispatchEvent(new InputEvent('beforeinput', { bubbles: true, data: inputText, inputType: 'insertText' }));
        composer.dispatchEvent(new InputEvent('input', { bubbles: true, data: inputText, inputType: 'insertText' }));
        composer.dispatchEvent(new Event('change', { bubbles: true }));
        return {
          hasText: !!((composer.innerText || '').trim() || (composer.textContent || '').trim()),
          mode: 'contenteditable',
          text: (composer.innerText || '').trim() || (composer.textContent || '').trim(),
        };
      }

      throw new Error('Unsupported Doubao input element');
    })(${JSON.stringify(text)})
  `;
}
function detectDoubaoVerificationScript() {
    return `
    (() => {
      const isVisible = (el) => {
        if (!(el instanceof HTMLElement)) return false;
        const style = window.getComputedStyle(el);
        if (style.display === 'none' || style.visibility === 'hidden') return false;
        const rect = el.getBoundingClientRect();
        return rect.width > 0 && rect.height > 0;
      };

      const challengeSelectors = [
        'iframe[src*="captcha"]',
        'iframe[src*="verify"]',
        'input[placeholder*="验证码"]',
        'input[aria-label*="验证码"]',
      ];
      const selectorMatch = challengeSelectors.find((selector) => {
        return Array.from(document.querySelectorAll(selector)).some((node) => isVisible(node));
      });
      if (selectorMatch) {
        return { detected: true, reason: selectorMatch };
      }

      const phrasePattern = /人机验证|完成安全验证|异常访问|滑动验证|拖动滑块/i;
      const candidateRoots = Array.from(
        document.querySelectorAll('[role="dialog"], [aria-modal="true"], .semi-modal, .modal')
      );
      const match = candidateRoots.find((node) => {
        if (!(node instanceof HTMLElement)) return false;
        if (!isVisible(node)) return false;
        const text = (node.innerText || node.textContent || '').trim();
        if (!text || text.length > 400) return false;
        return phrasePattern.test(text);
      });

      return {
        detected: !!match,
        reason: match ? ((match.innerText || match.textContent || '').trim().slice(0, 80) || 'challenge-ui') : '',
      };
    })()
  `;
}
function clickSendButtonScript() {
    return `
    (() => {
      ${buildDoubaoComposerLocatorScript()}
      const directSendButton = document.querySelector('button#flow-end-msg-send');
      if (directSendButton instanceof HTMLElement && isVisible(directSendButton)) {
        const disabled = directSendButton.getAttribute('disabled') !== null
          || directSendButton.getAttribute('aria-disabled') === 'true';
        if (!disabled) {
          directSendButton.click();
          return true;
        }
      }

      const composer = findComposer();
      if (!(composer instanceof HTMLElement)) return false;

      const composerRect = composer.getBoundingClientRect();
      const rootCandidates = [
        composer.closest('form'),
        composer.closest('[role="form"]'),
        composer.closest('[data-testid="chat_input"]'),
        composer.closest('.chat-input'),
        composer.parentElement,
        composer.parentElement?.parentElement,
      ].filter(Boolean);

      const seen = new Set();
      const buttons = [];
      for (const root of rootCandidates) {
        root.querySelectorAll('button, [role="button"]').forEach((node) => {
          if (!(node instanceof HTMLElement)) return;
          if (seen.has(node)) return;
          seen.add(node);
          buttons.push(node);
        });
      }

      const submitPattern = /send|发送|提交|发消息/i;
      const excludedPattern = /新对话|new chat|快速|视频生成|深入研究|图像生成|帮我写作|音乐生成|更多|上传|upload|麦克风|microphone|模式|mode|工具|tools|设置|settings|云盘|history|历史/i;
      let bestButton = null;
      let bestScore = -Infinity;

      for (const button of buttons) {
        if (!isVisible(button)) continue;
        const disabled = button.getAttribute('disabled') !== null
          || button.getAttribute('aria-disabled') === 'true';
        if (disabled) continue;

        const text = (button.innerText || button.textContent || '').trim();
        const aria = (button.getAttribute('aria-label') || '').trim();
        const title = (button.getAttribute('title') || '').trim();
        const className = String(button.className || '');
        const haystack = [text, aria, title].join(' ').trim();
        if (excludedPattern.test(haystack)) continue;

        const rect = button.getBoundingClientRect();
        const dx = rect.left - composerRect.right;
        const dy = Math.abs((rect.top + rect.height / 2) - (composerRect.top + composerRect.height / 2));
        const distancePenalty = Math.abs(dx) + dy;
        const isSubmitLike = submitPattern.test(haystack)
          || button.getAttribute('type') === 'submit'
          || className.includes('bg-dbx-text-highlight')
          || className.includes('bg-dbx-fill-highlight')
          || className.includes('text-dbx-text-static-white-primary');
        if (!isSubmitLike) continue;
        if (dx < -80 || dx > 280) continue;
        if (dy > 140) continue;

        let score = -distancePenalty;
        if (submitPattern.test(haystack)) score += 5000;
        if (button.getAttribute('type') === 'submit') score += 1200;
        if (button.closest('.chat-input-button')) score += 1200;
        if (className.includes('bg-dbx-text-highlight')) score += 600;
        if (className.includes('bg-dbx-fill-highlight')) score += 600;
        if (className.includes('text-dbx-text-static-white-primary')) score += 400;
        if (dx >= -40 && dx <= 240) score += 120;
        if (rect.left >= composerRect.left - 40) score += 40;

        if (score > bestScore) {
          bestScore = score;
          bestButton = button;
        }
      }

      if (bestButton && bestScore >= 200) {
        bestButton.click();
        return true;
      }

      return false;
    })()
  `;
}
function clickNewChatScript() {
    return `
    (() => {
      const isVisible = (el) => {
        if (!(el instanceof HTMLElement)) return false;
        const style = window.getComputedStyle(el);
        if (style.display === 'none' || style.visibility === 'hidden') return false;
        const rect = el.getBoundingClientRect();
        return rect.width > 0 && rect.height > 0;
      };

      const labels = ['新对话', 'New Chat', '创建新对话'];
      const buttons = Array.from(document.querySelectorAll('button, a, [role="button"]'));

      for (const button of buttons) {
        if (!isVisible(button)) continue;
        const text = (button.innerText || button.textContent || '').trim();
        const aria = (button.getAttribute('aria-label') || '').trim();
        const title = (button.getAttribute('title') || '').trim();
        const haystacks = [text, aria, title];
        if (haystacks.some((value) => labels.some((label) => value.includes(label)))) {
          button.click();
          return text || aria || title || 'new-chat';
        }
      }

      return '';
    })()
  `;
}
function normalizeDoubaoTabs(rawTabs) {
    return rawTabs
        .map((tab, index) => {
        const record = (tab || {});
        return {
            index: typeof record.index === 'number' ? record.index : index,
            url: typeof record.url === 'string' ? record.url : '',
            title: typeof record.title === 'string' ? record.title : '',
            active: record.active === true,
        };
    })
        .filter((tab) => tab.url.includes('doubao.com/chat'));
}
async function selectPreferredDoubaoTab(page) {
    const rawTabs = await page.tabs().catch(() => []);
    if (!Array.isArray(rawTabs) || rawTabs.length === 0)
        return false;
    const tabs = normalizeDoubaoTabs(rawTabs);
    if (tabs.length === 0)
        return false;
    const preferred = [...tabs].sort((left, right) => {
        const score = (tab) => {
            let value = tab.index;
            if (/https:\/\/www\.doubao\.com\/chat\/[A-Za-z0-9_-]+/.test(tab.url))
                value += 1000;
            else if (tab.url.startsWith(DOUBAO_CHAT_URL))
                value += 100;
            if (tab.active)
                value += 25;
            return value;
        };
        return score(right) - score(left);
    })[0];
    if (!preferred)
        return false;
    await page.selectTab(preferred.index);
    await page.wait(0.8);
    return true;
}
export async function ensureDoubaoChatPage(page) {
    let currentUrl = await page.evaluate('window.location.href').catch(() => '');
    if (typeof currentUrl === 'string' && currentUrl.includes('doubao.com/chat')) {
        await page.wait(1);
        return;
    }
    const reusedTab = await selectPreferredDoubaoTab(page);
    if (reusedTab) {
        currentUrl = await page.evaluate('window.location.href').catch(() => '');
        if (typeof currentUrl === 'string' && currentUrl.includes('doubao.com/chat')) {
            await page.wait(1);
            return;
        }
    }
    await page.goto(DOUBAO_CHAT_URL, { waitUntil: 'load', settleMs: 2500 });
    await page.wait(1.5);
}
export async function getDoubaoPageState(page) {
    await ensureDoubaoChatPage(page);
    return await page.evaluate(getStateScript());
}
export async function getDoubaoTurns(page) {
    await ensureDoubaoChatPage(page);
    const turns = await page.evaluate(getTurnsScript());
    if (turns.length > 0)
        return turns;
    const lines = await page.evaluate(getTranscriptLinesScript());
    return lines.map((line) => ({ Role: 'System', Text: line }));
}
export async function getDoubaoVisibleTurns(page) {
    await ensureDoubaoChatPage(page);
    return await page.evaluate(getTurnsScript());
}
export async function getDoubaoTranscriptLines(page) {
    await ensureDoubaoChatPage(page);
    return await page.evaluate(getTranscriptLinesScript());
}
export async function sendDoubaoMessage(page, text) {
    await ensureDoubaoChatPage(page);
    const normalizeComposerText = (value) => value.replace(/\r\n/g, '\n').trim();
    const expectedText = normalizeComposerText(text);
    const prepared = await page.evaluate(prepareDoubaoComposerScript());
    if (!prepared?.ok) {
        throw new CommandExecutionError(prepared?.reason || 'Could not find Doubao input element');
    }
    let hasText = false;
    if (page.nativeType) {
        try {
            await page.nativeType(text);
            await page.wait(0.2);
            await page.evaluate(syncComposerAfterNativeTypeScript());
            const nativeState = await page.evaluate(composerStateScript());
            hasText = !!nativeState?.hasText && normalizeComposerText(nativeState?.text || '') === expectedText;
        }
        catch { }
    }
    if (!hasText) {
        const fallbackState = await page.evaluate(fillComposerScript(text));
        hasText = !!fallbackState?.hasText && normalizeComposerText(fallbackState?.text || '') === expectedText;
    }
    if (!hasText) {
        throw new CommandExecutionError('Failed to insert text into Doubao composer');
    }
    let submittedBy = 'enter';
    const clicked = await page.evaluate(clickSendButtonScript());
    if (clicked) {
        submittedBy = 'button';
    }
    else if (page.nativeKeyPress) {
        try {
            await page.nativeKeyPress('Enter');
        }
        catch {
            await page.pressKey('Enter');
        }
    }
    else {
        await page.pressKey('Enter');
    }
    await page.wait(0.8);
    const verification = await page.evaluate(detectDoubaoVerificationScript());
    if (verification?.detected) {
        throw new CommandExecutionError('Doubao blocked the request with a verification challenge', verification.reason
            ? `Detected challenge signal: ${verification.reason}`
            : 'Please complete the challenge in the browser and try again.');
    }
    return submittedBy;
}
export async function waitForDoubaoResponse(page, beforeLines, beforeTurns, promptText, timeoutSeconds) {
    const beforeTurnSet = new Set(beforeTurns
        .filter((turn) => turn.Role === 'Assistant')
        .map((turn) => `${turn.Role}::${turn.Text}`));
    const sanitizeCandidate = (value) => value
        .replace(promptText, '')
        .replace(/内容由豆包 AI 生成/g, '')
        .replace(/在此处拖放文件/g, '')
        .replace(/文件数量：.*$/g, '')
        .replace(/\{"namedChunks".*$/g, '')
        .replace(/window\\._SSR_DATA.*$/g, '')
        .trim();
    const getCandidate = async () => {
        const verification = await page.evaluate(detectDoubaoVerificationScript());
        if (verification?.detected) {
            throw new CommandExecutionError('Doubao blocked the request with a verification challenge', verification.reason
                ? `Detected challenge signal: ${verification.reason}`
                : 'Please complete the challenge in the browser and try again.');
        }
        const turns = await getDoubaoVisibleTurns(page);
        const assistantCandidate = [...turns]
            .reverse()
            .find((turn) => turn.Role === 'Assistant' && !beforeTurnSet.has(`${turn.Role}::${turn.Text}`));
        const visibleCandidate = assistantCandidate ? sanitizeCandidate(assistantCandidate.Text) : '';
        if (visibleCandidate)
            return visibleCandidate;
        const lines = await getDoubaoTranscriptLines(page);
        const additions = collectDoubaoTranscriptAdditions(beforeLines, lines, promptText, sanitizeCandidate)
            .split('\n')
            .filter(Boolean);
        const shortCandidate = additions.find((line) => line.length <= 120);
        return shortCandidate || additions[additions.length - 1] || '';
    };
    const pollIntervalSeconds = 2;
    const maxPolls = Math.max(1, Math.ceil(timeoutSeconds / pollIntervalSeconds));
    let lastCandidate = '';
    let stableCount = 0;
    for (let index = 0; index < maxPolls; index += 1) {
        await page.wait(index === 0 ? 1.5 : pollIntervalSeconds);
        const candidate = await getCandidate();
        if (!candidate)
            continue;
        if (candidate === lastCandidate) {
            stableCount += 1;
        }
        else {
            lastCandidate = candidate;
            stableCount = 1;
        }
        if (stableCount >= 2 || index === maxPolls - 1) {
            return candidate;
        }
    }
    return lastCandidate;
}
export function isLikelyDoubaoUiNoise(value) {
    const text = value.replace(/\s+/g, '');
    if (!text)
        return false;
    const exactNoise = new Set([
        '快速视频生成深入研究图像生成帮我写作音乐生成更多',
    ]);
    return exactNoise.has(text);
}
function isAlwaysTranscriptUiNoise(value) {
    const text = value.replace(/\s+/g, '');
    if (!text)
        return false;
    const exactNoise = new Set([
        'AI创作云盘更多历史对话',
    ]);
    return exactNoise.has(text);
}
function isLikelyTranscriptUiNoise(rawValue, sanitizedValue, promptText) {
    const normalizeWhitespace = (value) => value.replace(/\s+/g, ' ').trim();
    const normalizedRaw = normalizeWhitespace(rawValue);
    const normalizedPrompt = normalizeWhitespace(promptText);
    if (!normalizedPrompt || !normalizedRaw.startsWith(normalizedPrompt))
        return false;
    const remainder = normalizedRaw.slice(normalizedPrompt.length).trim();
    if (!remainder)
        return true;
    return isLikelyDoubaoUiNoise(remainder) || isLikelyDoubaoUiNoise(sanitizedValue);
}
export function collectDoubaoTranscriptAdditions(beforeLines, currentLines, promptText, sanitize = (value) => value.trim()) {
    const normalizedBefore = new Set(beforeLines.map((line) => sanitize(line)).filter(Boolean));
    return currentLines
        .filter((line) => !beforeLines.includes(line))
        .map((line) => ({ raw: line, sanitized: sanitize(line) }))
        .filter(({ raw, sanitized }) => sanitized
        && sanitized !== promptText
        && !normalizedBefore.has(sanitized)
        && !isAlwaysTranscriptUiNoise(sanitized)
        && !isLikelyTranscriptUiNoise(raw, sanitized, promptText))
        .map(({ sanitized }) => sanitized)
        .join('\n');
}
function getConversationListScript() {
    return `
    (() => {
      const sidebar = document.querySelector('[data-testid="flow_chat_sidebar"]');
      if (!sidebar) return [];

      const items = Array.from(
        sidebar.querySelectorAll('a[data-testid="chat_list_thread_item"]')
      );

      return items
        .map(a => {
          const href = a.getAttribute('href') || '';
          const match = href.match(/\\/chat\\/(\\d{10,})/);
          if (!match) return null;
          const id = match[1];
          const textContent = (a.textContent || a.innerText || '').trim();
          const title = textContent
            .replace(/\\s+/g, ' ')
            .substring(0, 200);
          return { id, title, href };
        })
        .filter(Boolean);
    })()
  `;
}
export async function getDoubaoConversationList(page) {
    await ensureDoubaoChatPage(page);
    const raw = await page.evaluate(getConversationListScript());
    if (!Array.isArray(raw))
        return [];
    return raw.map((item) => ({
        Id: item.id,
        Title: item.title,
        Url: `${DOUBAO_CHAT_URL}/${item.id}`,
    }));
}
export function parseDoubaoConversationId(input) {
    const match = input.match(/(\d{10,})/);
    return match ? match[1] : input;
}
function getConversationDetailScript() {
    return `
    (() => {
      const clean = (v) => (v || '').replace(/\\u00a0/g, ' ').replace(/\\n{3,}/g, '\\n\\n').trim();

      const messageList = document.querySelector('[data-testid="message-list"]');
      if (!messageList) return { messages: [], meeting: null };

      const meetingCard = messageList.querySelector('[data-testid="meeting-minutes-card"]');
      let meeting = null;
      if (meetingCard) {
        const raw = clean(meetingCard.textContent || '');
        const match = raw.match(/^(.+?)(?:会议时间：|\\s*$)(.*)/);
        meeting = {
          title: match ? match[1].trim() : raw,
          time: match && match[2] ? match[2].trim() : '',
        };
      }

      const unions = Array.from(messageList.querySelectorAll('[data-testid="union_message"]'));
      const messages = unions.map(u => {
        const isSend = !!u.querySelector('[data-testid="send_message"]');
        const isReceive = !!u.querySelector('[data-testid="receive_message"]');
        const textEl = u.querySelector('[data-testid="message_text_content"]');
        const text = textEl ? clean(textEl.innerText || textEl.textContent || '') : '';
        return {
          role: isSend ? 'User' : isReceive ? 'Assistant' : 'System',
          text,
          hasMeetingCard: !!u.querySelector('[data-testid="meeting-minutes-card"]'),
        };
      }).filter(m => m.text);

      return { messages, meeting };
    })()
  `;
}
export async function navigateToConversation(page, conversationId) {
    const url = `${DOUBAO_CHAT_URL}/${conversationId}`;
    const currentUrl = await page.evaluate('window.location.href').catch(() => '');
    if (typeof currentUrl === 'string' && currentUrl.includes(`/chat/${conversationId}`)) {
        await page.wait(1);
        return;
    }
    await page.goto(url, { waitUntil: 'load', settleMs: 3000 });
    await page.wait(2);
}
export async function getConversationDetail(page, conversationId) {
    await navigateToConversation(page, conversationId);
    const raw = await page.evaluate(getConversationDetailScript());
    const messages = (raw.messages || []).map((m) => ({
        Role: m.role,
        Text: m.text,
        HasMeetingCard: m.hasMeetingCard,
    }));
    return { messages, meeting: raw.meeting };
}
// ---------------------------------------------------------------------------
// Meeting minutes panel helpers
// ---------------------------------------------------------------------------
function clickMeetingCardScript() {
    return `
    (() => {
      const card = document.querySelector('[data-testid="meeting-minutes-card"]');
      if (!card) return false;
      card.click();
      return true;
    })()
  `;
}
function readMeetingSummaryScript() {
    return `
    (() => {
      const panel = document.querySelector('[data-testid="canvas_panel_container"]');
      if (!panel) return { error: 'no panel' };

      const summary = panel.querySelector('[data-testid="meeting-summary-todos"]');
      const summaryText = summary
        ? (summary.innerText || summary.textContent || '').trim()
        : '';

      return { summary: summaryText };
    })()
  `;
}
function clickTextNotesTabScript() {
    return `
    (() => {
      const panel = document.querySelector('[data-testid="canvas_panel_container"]');
      if (!panel) return false;
      const tabs = panel.querySelectorAll('[role="tab"], .semi-tabs-tab');
      for (const tab of tabs) {
        if ((tab.textContent || '').trim().includes('文字')) {
          tab.click();
          return true;
        }
      }
      return false;
    })()
  `;
}
function readTextNotesScript() {
    return `
    (() => {
      const panel = document.querySelector('[data-testid="canvas_panel_container"]');
      if (!panel) return '';
      const textNotes = panel.querySelector('[data-testid="meeting-text-notes"]');
      if (!textNotes) return '';
      return (textNotes.innerText || textNotes.textContent || '').trim();
    })()
  `;
}
function normalizeTranscriptLines(text) {
    return text
        .split('\n')
        .map(line => line.trim())
        .filter(Boolean);
}
function containsLineSequence(haystack, needle) {
    if (needle.length === 0)
        return true;
    if (needle.length > haystack.length)
        return false;
    for (let start = 0; start <= haystack.length - needle.length; start += 1) {
        let matched = true;
        for (let offset = 0; offset < needle.length; offset += 1) {
            if (haystack[start + offset] !== needle[offset]) {
                matched = false;
                break;
            }
        }
        if (matched)
            return true;
    }
    return false;
}
export function mergeTranscriptSnapshots(existing, incoming) {
    const currentLines = normalizeTranscriptLines(existing);
    const nextLines = normalizeTranscriptLines(incoming);
    if (nextLines.length === 0)
        return currentLines.join('\n');
    if (currentLines.length === 0)
        return nextLines.join('\n');
    if (containsLineSequence(currentLines, nextLines))
        return currentLines.join('\n');
    const maxOverlap = Math.min(currentLines.length, nextLines.length);
    for (let overlap = maxOverlap; overlap > 0; overlap -= 1) {
        let matched = true;
        for (let index = 0; index < overlap; index += 1) {
            if (currentLines[currentLines.length - overlap + index] !== nextLines[index]) {
                matched = false;
                break;
            }
        }
        if (matched) {
            return [...currentLines, ...nextLines.slice(overlap)].join('\n');
        }
    }
    return [...currentLines, ...nextLines].join('\n');
}
function clickChapterTabScript() {
    return `
    (() => {
      const panel = document.querySelector('[data-testid="canvas_panel_container"]');
      if (!panel) return false;
      const tabs = panel.querySelectorAll('[role="tab"], .semi-tabs-tab');
      for (const tab of tabs) {
        if ((tab.textContent || '').trim().includes('章节')) {
          tab.click();
          return true;
        }
      }
      return false;
    })()
  `;
}
function readChapterScript() {
    return `
    (() => {
      const panel = document.querySelector('[data-testid="canvas_panel_container"]');
      if (!panel) return '';
      const chapter = panel.querySelector('[data-testid="meeting-ai-chapter"]');
      if (!chapter) return '';
      return (chapter.innerText || chapter.textContent || '').trim();
    })()
  `;
}
function triggerTranscriptDownloadScript() {
    return `
    (() => {
      const panel = document.querySelector('[data-testid="canvas_panel_container"]');
      if (!panel) return { error: 'no panel' };

      const downloadIcon = panel.querySelector('[class*="DownloadMeetingAudio"] span[role="img"]');
      if (!downloadIcon) return { error: 'no download icon' };

      downloadIcon.click();
      return { clicked: 'icon' };
    })()
  `;
}
function clickTranscriptDownloadBtnScript() {
    return `
    (() => {
      const btn = document.querySelector('[data-testid="minutes-download-text-btn"]');
      if (!btn) return { error: 'no download text btn' };
      btn.click();
      return { clicked: 'transcript' };
    })()
  `;
}
export async function openMeetingPanel(page, conversationId) {
    await navigateToConversation(page, conversationId);
    const clicked = await page.evaluate(clickMeetingCardScript());
    if (!clicked)
        return false;
    await page.wait(2);
    return true;
}
export async function getMeetingSummary(page) {
    const result = await page.evaluate(readMeetingSummaryScript());
    return result.summary || '';
}
export async function getMeetingChapters(page) {
    await page.evaluate(clickChapterTabScript());
    await page.wait(1.5);
    return await page.evaluate(readChapterScript());
}
function scrollTextNotesPanelScript() {
    return `
    (() => {
      const panel = document.querySelector('[data-testid="canvas_panel_container"]');
      if (!panel) return 0;
      const textNotes = panel.querySelector('[data-testid="meeting-text-notes"]');
      if (!textNotes) return 0;

      const scrollable = textNotes.closest('[class*="overflow"]')
        || textNotes.parentElement
        || textNotes;
      const maxScroll = scrollable.scrollHeight - scrollable.clientHeight;
      if (maxScroll > 0) {
        scrollable.scrollTop = scrollable.scrollHeight;
      }
      return maxScroll;
    })()
  `;
}
export async function getMeetingTranscript(page) {
    await page.evaluate(clickTextNotesTabScript());
    await page.wait(2);
    let merged = '';
    let stableRounds = 0;
    for (let i = 0; i < 10; i++) {
        await page.evaluate(scrollTextNotesPanelScript());
        await page.wait(1);
        const snapshot = await page.evaluate(readTextNotesScript());
        const nextMerged = mergeTranscriptSnapshots(merged, snapshot);
        if (nextMerged === merged && snapshot.length > 0) {
            stableRounds += 1;
            if (stableRounds >= 2)
                break;
        }
        else {
            stableRounds = 0;
            merged = nextMerged;
        }
    }
    return merged;
}
export async function triggerTranscriptDownload(page) {
    const iconResult = await page.evaluate(triggerTranscriptDownloadScript());
    if (iconResult.error)
        return false;
    await page.wait(1);
    const btnResult = await page.evaluate(clickTranscriptDownloadBtnScript());
    return !btnResult.error;
}
export const __test__ = {
    clickSendButtonScript,
    composerStateScript,
    detectDoubaoVerificationScript,
    getTurnsScript,
    getTranscriptLinesScript,
};
export async function startNewDoubaoChat(page) {
    await ensureDoubaoChatPage(page);
    const clickedLabel = await page.evaluate(clickNewChatScript());
    if (clickedLabel) {
        await page.wait(1.5);
        return clickedLabel;
    }
    await page.goto(DOUBAO_NEW_CHAT_URL, { waitUntil: 'load', settleMs: 2000 });
    await page.wait(1.5);
    return 'navigate';
}
