import { cli, Strategy } from '@jackwener/opencli/registry';
import { htmlToMarkdown } from '@jackwener/opencli/utils';
import { ArgumentError, CommandExecutionError, TimeoutError } from '@jackwener/opencli/errors';
import {
    YUANBAO_DOMAIN,
    IS_VISIBLE_JS,
    authRequired,
    isOnYuanbao,
    ensureYuanbaoPage,
    hasLoginGate,
    normalizeBooleanFlag,
    sendYuanbaoMessage,
} from './shared.js';
const YUANBAO_RESPONSE_POLL_INTERVAL_SECONDS = 2;
const YUANBAO_MIN_WAIT_MS = 8_000;
const YUANBAO_STABLE_POLLS_REQUIRED = 3;
function sendFailure(reason, detail) {
    const suffix = detail ? ` Detail: ${detail}` : '';
    return new CommandExecutionError(`${reason || 'Unknown Yuanbao send failure.'}${suffix}`, 'Make sure the Yuanbao chat composer is visible and ready before retrying.');
}
function normalizeText(value) {
    return typeof value === 'string' ? value.trim() : '';
}
export function convertYuanbaoHtmlToMarkdown(value) {
    return htmlToMarkdown(value, (td) => {
        td.addRule('table', {
            filter: 'table',
            replacement: (content) => `\n\n${content}\n\n`,
        });
        td.addRule('tableSection', {
            filter: ['thead', 'tbody', 'tfoot'],
            replacement: (content) => content,
        });
        td.addRule('tableRow', {
            filter: 'tr',
            replacement: (content, node) => {
                const element = node;
                const cells = Array.from(element.children);
                const isHeaderRow = element.parentElement?.tagName === 'THEAD'
                    || (cells.length > 0 && cells.every((cell) => cell.tagName === 'TH'));
                const row = `${content}\n`;
                if (!isHeaderRow)
                    return row;
                const separator = `| ${cells.map(() => '---').join(' | ')} |\n`;
                return `${row}${separator}`;
            },
        });
        td.addRule('tableCell', {
            filter: ['th', 'td'],
            replacement: (content, node) => {
                const element = node;
                const index = element.parentElement ? Array.from(element.parentElement.children).indexOf(element) : 0;
                const prefix = index === 0 ? '| ' : ' ';
                return `${prefix}${content.trim()} |`;
            },
        });
    });
}
export function sanitizeYuanbaoResponseText(value, promptText) {
    let sanitized = value
        .replace(/内容由AI生成，仅供参考/gi, '')
        .replace(/重新回答/gi, '')
        .trim();
    if (/^(正在搜索资料|搜索资料中|正在思考|思考中)[.。…]*$/u.test(sanitized)) {
        return '';
    }
    const prompt = promptText.trim();
    if (!prompt)
        return sanitized;
    if (sanitized === prompt)
        return '';
    for (const separator of ['\n\n', '\n', '\r\n\r\n', '\r\n', ' ']) {
        const prefix = `${prompt}${separator}`;
        if (sanitized.startsWith(prefix)) {
            sanitized = sanitized.slice(prefix.length).trim();
            break;
        }
    }
    return sanitized;
}
export function collectYuanbaoTranscriptAdditions(beforeLines, currentLines, promptText) {
    const beforeSet = new Set(beforeLines);
    const additions = currentLines
        .filter((line) => !beforeSet.has(line))
        .map((line) => sanitizeYuanbaoResponseText(line, promptText))
        .filter((line) => line && line !== promptText);
    return additions.join('\n').trim();
}
export function pickLatestYuanbaoAssistantCandidate(messages, baselineCount, promptText) {
    const freshMessages = messages
        .slice(Math.max(0, baselineCount))
        .map((message) => sanitizeYuanbaoResponseText(message, promptText))
        .filter(Boolean);
    for (let i = freshMessages.length - 1; i >= 0; i -= 1) {
        if (freshMessages[i] !== promptText.trim())
            return freshMessages[i];
    }
    return '';
}
export function updateStableState(previousText, stableCount, nextText) {
    if (!nextText)
        return { previousText: '', stableCount: 0 };
    if (nextText === previousText)
        return { previousText, stableCount: stableCount + 1 };
    return { previousText: nextText, stableCount: 0 };
}
function getTranscriptLinesScript() {
    return `
    (() => {
      const clean = (value) => (value || '')
        .replace(/\\u00a0/g, ' ')
        .replace(/\\n{3,}/g, '\\n\\n')
        .trim();

      const root = (
        document.querySelector('.agent-dialogue__content--common')
        || document.querySelector('.agent-dialogue__content')
        || document.querySelector('.agent-dialogue')
        || document.body
      ).cloneNode(true);

      const removableSelectors = [
        '.agent-dialogue__content--common__input',
        '.agent-dialogue__tool',
        '.agent-dialogue__content-copyright',
        '.index_chatLandingBox__G7hAT',
        '.index_chatLandingBoxMobile__J8i8v',
        '.index_chatLandingHintList__M69Lr',
        '.yb-nav',
        '.agent-dialogue__content--common__input .ql-toolbar',
        '.agent-dialogue__content--common__input .ql-container',
        '.agent-dialogue__content--common__input .ql-editor',
        '[role="dialog"]',
        'iframe',
        'button',
        'script',
        'style',
        'noscript',
      ];

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

      const stopLines = new Set([
        '元宝',
        'DeepSeek',
        '深度思考',
        '联网搜索',
        '工具',
        '登录',
        '安装电脑版',
        '内容由AI生成，仅供参考',
        '有问题，尽管问，shift+enter换行',
        '立即创建团队',
        '微信',
        '手机',
        'QQ',
        '微信扫码登录',
        '扫码默认已阅读并同意',
        '用户服务协议',
        '隐私协议',
      ]);

      const noisyPatterns = [
        /^支持文件格式[:：]/,
        /^文件拖动到此处即可上传/,
        /^下载元宝电脑版/,
      ];

      return clean(root.innerText || root.textContent || '')
        .split('\\n')
        .map((line) => clean(line))
        .filter((line) => line
          && line.length <= 4000
          && !stopLines.has(line)
          && !noisyPatterns.some((pattern) => pattern.test(line)));
    })()
  `;
}
async function getYuanbaoTranscriptLines(page) {
    const result = await page.evaluate(getTranscriptLinesScript());
    return Array.isArray(result) ? result.map(normalizeText).filter(Boolean) : [];
}
async function getYuanbaoAssistantMessages(page) {
    const result = await page.evaluate(`(() => {
    ${IS_VISIBLE_JS}

    const roots = Array.from(document.querySelectorAll('.agent-chat__list__item--ai'))
      .filter((node) => isVisible(node));

    return roots.map((root) => {
      const doneContent = root.querySelector('.hyc-content-md-done');
      const markdownContent = doneContent || root.querySelector('.hyc-content-md');
      const speechContent = root.querySelector('.agent-chat__speech-text');
      const bubbleContent = root.querySelector('.agent-chat__bubble__content');
      const content = markdownContent || speechContent || bubbleContent;

      if (content instanceof HTMLElement) {
        return content.innerHTML || content.textContent || '';
      }

      return root instanceof HTMLElement ? (root.innerHTML || root.textContent || '') : '';
    }).filter(Boolean);
  })()`);
    return Array.isArray(result)
        ? result
            .map((value) => convertYuanbaoHtmlToMarkdown(typeof value === 'string' ? value : ''))
            .map(normalizeText)
            .filter(Boolean)
        : [];
}
async function getYuanbaoInternetSearchState(page) {
    const result = await page.evaluate(`(() => {
    ${IS_VISIBLE_JS}

    const button = Array.from(document.querySelectorAll('[dt-button-id="internet_search"]'))
      .find((node) => isVisible(node));

    if (!(button instanceof HTMLElement)) return { found: false, enabled: false };

    const attr = button.getAttribute('dt-internet-search') || '';
    const className = button.className || '';
    return {
      found: true,
      enabled: attr === 'openInternetSearch' || className.includes('index_v2_active__'),
    };
  })()`);
    return result;
}
async function setYuanbaoInternetSearch(page, enabled) {
    const current = await getYuanbaoInternetSearchState(page);
    if (!current.found || current.enabled === enabled)
        return;
    await page.evaluate(`(() => {
    ${IS_VISIBLE_JS}

    const button = Array.from(document.querySelectorAll('[dt-button-id="internet_search"]'))
      .find((node) => isVisible(node));

    if (button instanceof HTMLElement) button.click();
  })()`);
    await page.wait(0.5);
}
async function getYuanbaoDeepThinkState(page) {
    const result = await page.evaluate(`(() => {
    ${IS_VISIBLE_JS}

    const button = Array.from(document.querySelectorAll('[dt-button-id="deep_think"]'))
      .find((node) => isVisible(node));

    if (!(button instanceof HTMLElement)) return { found: false, enabled: false };

    const className = button.className || '';
    return {
      found: true,
      enabled: className.includes('ThinkSelector_selected__'),
    };
  })()`);
    return result;
}
async function setYuanbaoDeepThink(page, enabled) {
    const current = await getYuanbaoDeepThinkState(page);
    if (!current.found || current.enabled === enabled)
        return;
    await page.evaluate(`(() => {
    ${IS_VISIBLE_JS}

    const button = Array.from(document.querySelectorAll('[dt-button-id="deep_think"]'))
      .find((node) => isVisible(node));

    if (button instanceof HTMLElement) button.click();
  })()`);
    await page.wait(0.5);
}
async function waitForYuanbaoResponse(page, baselineAssistantCount, beforeLines, prompt, timeoutSeconds) {
    const startTime = Date.now();
    let previousText = '';
    let stableCount = 0;
    let latestCandidate = '';
    while (Date.now() - startTime < timeoutSeconds * 1000) {
        await page.wait(YUANBAO_RESPONSE_POLL_INTERVAL_SECONDS);
        if (await hasLoginGate(page))
            return 'blocked';
        const assistantMessages = await getYuanbaoAssistantMessages(page);
        const assistantCandidate = pickLatestYuanbaoAssistantCandidate(assistantMessages, baselineAssistantCount, prompt);
        const candidate = assistantCandidate || collectYuanbaoTranscriptAdditions(beforeLines, await getYuanbaoTranscriptLines(page), prompt);
        if (!candidate)
            continue;
        latestCandidate = candidate;
        const nextState = updateStableState(previousText, stableCount, candidate);
        previousText = nextState.previousText;
        stableCount = nextState.stableCount;
        const waitedLongEnough = Date.now() - startTime >= YUANBAO_MIN_WAIT_MS;
        if (waitedLongEnough && stableCount >= YUANBAO_STABLE_POLLS_REQUIRED)
            return candidate;
    }
    return latestCandidate || null;
}
export const askCommand = cli({
    site: 'yuanbao',
    name: 'ask',
    access: 'write',
    description: 'Send a prompt to Yuanbao web chat and wait for the assistant response',
    domain: YUANBAO_DOMAIN,
    strategy: Strategy.COOKIE,
    browser: true,
    siteSession: 'persistent',
    navigateBefore: false,
    defaultFormat: 'plain',
    args: [
        { name: 'prompt', required: true, positional: true, help: 'Prompt to send' },
        { name: 'timeout', type: 'int', required: false, help: 'Max seconds to wait (default: 60)', default: 60 },
        { name: 'search', type: 'boolean', required: false, help: 'Enable Yuanbao internet search (default: true)', default: true },
        { name: 'think', type: 'boolean', required: false, help: 'Enable Yuanbao deep thinking (default: false)', default: false },
    ],
    columns: ['Role', 'Text'],
    func: async (page, kwargs) => {
        const prompt = kwargs.prompt;
        const timeout = kwargs.timeout;
        if (!Number.isInteger(timeout) || timeout < 1) {
            throw new ArgumentError('--timeout must be a positive integer (seconds)');
        }
        const useSearch = normalizeBooleanFlag(kwargs.search, true);
        const useThink = normalizeBooleanFlag(kwargs.think, false);
        await ensureYuanbaoPage(page);
        if (await hasLoginGate(page)) {
            throw authRequired('Yuanbao opened a login gate before sending the prompt.');
        }
        await setYuanbaoInternetSearch(page, useSearch);
        await setYuanbaoDeepThink(page, useThink);
        const beforeAssistantMessages = await getYuanbaoAssistantMessages(page);
        const beforeLines = await getYuanbaoTranscriptLines(page);
        const sendResult = await sendYuanbaoMessage(page, prompt);
        if (!sendResult?.ok) {
            if (await hasLoginGate(page)) {
                throw authRequired('Yuanbao opened a login gate instead of accepting the prompt.');
            }
            throw sendFailure(sendResult?.reason, sendResult?.detail);
        }
        const response = await waitForYuanbaoResponse(page, beforeAssistantMessages.length, beforeLines, prompt, timeout);
        if (response === 'blocked') {
            throw authRequired('Yuanbao opened a login gate instead of returning a chat response.');
        }
        if (!response) {
            throw new TimeoutError('yuanbao ask', timeout, 'No Yuanbao response was observed before the timeout. Retry with --timeout, and verify the current browser session is still interactive.');
        }
        return [
            { Role: 'User', Text: prompt },
            { Role: 'Assistant', Text: response },
        ];
    },
});
export const __test__ = {
    collectYuanbaoTranscriptAdditions,
    convertYuanbaoHtmlToMarkdown,
    isOnYuanbao,
    normalizeBooleanFlag,
    pickLatestYuanbaoAssistantCandidate,
    sanitizeYuanbaoResponseText,
    updateStableState,
};
