// content/cloudflare-temp-email.js — Content script for Cloudflare Temp Email admin page

const CLOUDFLARE_TEMP_EMAIL_PREFIX = '[MultiPage:cloudflare-temp-email]';
const isTopFrame = window === window.top;

const {
  combineDistinctTextParts = (parts = []) => parts
    .map((part) => String(part || '').replace(/\s+/g, ' ').trim())
    .filter(Boolean)
    .filter((part, index, values) => values.indexOf(part) === index)
    .join(' '),
  extractVerificationCode = () => null,
  generateReadableLocalPart = () => `mp${Date.now().toString(36)}${Math.random().toString(36).slice(2, 6)}`.slice(0, 18),
  normalizeDomainSuffix = (value) => String(value || '').trim().replace(/^@+/, '').toLowerCase(),
  parseCloudflareMailboxCredential = () => null,
  pickRandomSuffix = (options = []) => normalizeDomainSuffix(options[0] || ''),
  selectVerificationMessage = () => null,
} = globalThis.MultiPageCloudflareTempEmail || {};

console.log(CLOUDFLARE_TEMP_EMAIL_PREFIX, 'Content script loaded on', location.href, 'frame:', isTopFrame ? 'top' : 'child');

if (!isTopFrame) {
  console.log(CLOUDFLARE_TEMP_EMAIL_PREFIX, 'Skipping child frame');
} else {
  chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
    if (message.type !== 'CREATE_CLOUDFLARE_TEMP_EMAIL' && message.type !== 'POLL_EMAIL') {
      return;
    }

    resetStopState();

    const handler = message.type === 'CREATE_CLOUDFLARE_TEMP_EMAIL'
      ? createCloudflareTempEmail
      : pollCloudflareTempEmail;

    handler(message.step, message.payload || {}).then((result) => {
      sendResponse(result);
    }).catch((err) => {
      if (isStopError(err)) {
        if (message.step) {
          log(`Step ${message.step}: Stopped by user.`, 'warn');
        } else {
          log('Cloudflare Temp Email: Stopped by user.', 'warn');
        }
        sendResponse({ stopped: true, error: err.message });
        return;
      }

      if (message.step) {
        reportError(message.step, err.message);
      }
      sendResponse({ error: err.message });
    });

    return true;
  });

  function getElementText(el) {
    return combineDistinctTextParts([
      el?.innerText,
      el?.textContent,
      el?.getAttribute?.('aria-label'),
      el?.getAttribute?.('title'),
      el?.value,
    ]);
  }

  function normalizeText(value) {
    return String(value || '').replace(/\s+/g, ' ').trim();
  }

  function normalizeEmail(value) {
    return normalizeText(value).toLowerCase();
  }

  function isVisible(el) {
    if (!el) return false;
    if (el.hidden) return false;
    const style = window.getComputedStyle(el);
    if (style.display === 'none' || style.visibility === 'hidden' || style.opacity === '0') {
      return false;
    }
    return Boolean(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
  }

  function findVisibleButton(pattern) {
    return Array.from(document.querySelectorAll('button'))
      .filter(isVisible)
      .find((button) => pattern.test(normalizeText(getElementText(button))));
  }

  function findVisibleTab(pattern, occurrence = 'first') {
    const tabs = Array.from(document.querySelectorAll('.n-tabs-tab'))
      .filter(isVisible)
      .filter((tab) => pattern.test(normalizeText(getElementText(tab))));

    return occurrence === 'last' ? tabs[tabs.length - 1] || null : tabs[0] || null;
  }

  async function waitForCondition(predicate, timeout, message) {
    const startedAt = Date.now();

    while (Date.now() - startedAt < timeout) {
      throwIfStopped();
      const value = predicate();
      if (value) {
        return value;
      }
      await sleep(150);
    }

    throw new Error(message);
  }

  async function clickTab(pattern, occurrence = 'first', timeout = 10000) {
    await waitForCondition(
      () => findVisibleTab(pattern, occurrence),
      timeout,
      `Timed out waiting for tab ${pattern}`
    );

    const tab = findVisibleTab(pattern, occurrence);
    if (!tab) {
      throw new Error(`Could not find tab ${pattern}`);
    }

    await humanPause(120, 260);
    simulateClick(tab);
    await sleep(400);
    return tab;
  }

  function getCreateAddressInput() {
    return Array.from(document.querySelectorAll('input[placeholder="请输入"]')).find(isVisible) || null;
  }

  function getPrefixSwitch() {
    return Array.from(document.querySelectorAll('[role="switch"], .n-switch')).find(isVisible) || null;
  }

  function getCreateInputGroup() {
    return getCreateAddressInput()?.closest('.n-input-group') || null;
  }

  function getCreateDomainSelect() {
    const group = getCreateInputGroup();

    return Array.from(group?.querySelectorAll('.n-base-selection') || []).find(isVisible)
      || Array.from(group?.querySelectorAll('.n-select') || []).find(isVisible)
      || null;
  }

  function getCurrentDomain() {
    const groupText = normalizeText(getCreateInputGroup()?.textContent || '');
    const match = groupText.match(/@([a-z0-9.-]+\.[a-z]{2,})/i);
    return normalizeDomainSuffix(match ? match[1] : '');
  }

  function getVisibleDomainOptionEntries() {
    const seen = new Set();
    const entries = [];

    for (const option of Array.from(document.querySelectorAll('.n-base-select-option, [role="option"]'))) {
      if (!isVisible(option)) {
        continue;
      }

      const value = normalizeDomainSuffix(getElementText(option));
      if (!value || seen.has(value)) {
        continue;
      }

      seen.add(value);
      entries.push({ option, value });
    }

    return entries;
  }

  async function ensureCreateAccountPage() {
    await clickTab(/^账号$/, 'first');
    await clickTab(/^创建账号$/);
    await waitForCondition(
      () => getCreateAddressInput(),
      10000,
      'Cloudflare Temp Email create form did not load.'
    );
  }

  async function ensureMailPage() {
    await clickTab(/^邮件$/, 'first');
    await clickTab(/^邮件$/, 'last');
    await waitForCondition(
      () => document.querySelector('input[placeholder="留空查询所有地址"]'),
      10000,
      'Cloudflare Temp Email mail page did not load.'
    );
  }

  async function ensurePrefixDisabled() {
    const prefixSwitch = getPrefixSwitch();
    if (!prefixSwitch) {
      log('Cloudflare Temp Email: Prefix switch not present, assuming prefix is already disabled', 'info');
      return;
    }

    if (prefixSwitch.getAttribute('aria-checked') === 'true') {
      await humanPause(120, 280);
      simulateClick(prefixSwitch);
      await waitForCondition(
        () => prefixSwitch.getAttribute('aria-checked') === 'false',
        5000,
        'Cloudflare Temp Email prefix switch did not turn off.'
      );
      log('Cloudflare Temp Email: Prefix disabled', 'ok');
    }
  }

  async function selectRandomDomainSuffix() {
    const domainSelect = await waitForCondition(
      () => getCreateDomainSelect(),
      5000,
      'Could not find the Cloudflare Temp Email suffix selector.'
    );

    await humanPause(120, 260);
    simulateClick(domainSelect);
    await sleep(300);

    const optionEntries = await waitForCondition(
      () => {
        const entries = getVisibleDomainOptionEntries();
        return entries.length > 0 ? entries : null;
      },
      5000,
      'Could not find any available Cloudflare Temp Email suffix options.'
    );

    const suffix = pickRandomSuffix(optionEntries.map((entry) => entry.value));
    const selectedEntry = optionEntries.find((entry) => entry.value === suffix);

    if (!suffix || !selectedEntry?.option) {
      throw new Error('Could not choose a Cloudflare Temp Email suffix from the available options.');
    }

    await humanPause(120, 260);
    simulateClick(selectedEntry.option);
    await waitForCondition(
      () => getCurrentDomain() === suffix,
      5000,
      `Cloudflare Temp Email suffix did not switch to ${suffix}.`
    );

    log(`Cloudflare Temp Email: Selected suffix ${suffix}`, 'info');
    return suffix;
  }

  function generateLocalPart() {
    return generateReadableLocalPart();
  }

  function findCredentialDialog() {
    return Array.from(
      document.querySelectorAll('[role="dialog"], .n-dialog, .n-modal, .n-base-modal, .n-card')
    ).find((el) => isVisible(el) && /邮箱地址凭证/.test(getElementText(el)));
  }

  function extractCredentialToken(root) {
    if (!root) return '';

    const candidates = [
      getElementText(root),
      ...Array.from(root.querySelectorAll('textarea, input, pre, code')).map((el) => el.value || el.textContent || ''),
    ];

    for (const candidate of candidates) {
      const match = String(candidate || '').match(/[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/);
      if (match) {
        return match[0];
      }
    }

    return '';
  }

  async function waitForCredentialDetails(timeout = 15000) {
    return waitForCondition(() => {
      const dialog = findCredentialDialog();
      const token = extractCredentialToken(dialog);
      const credential = parseCloudflareMailboxCredential(token);
      if (!credential?.email) {
        return null;
      }
      return credential;
    }, timeout, 'Timed out waiting for Cloudflare Temp Email credential dialog.');
  }

  async function dismissCredentialDialog() {
    const dialog = findCredentialDialog();
    if (!dialog) return;

    const closeButton = Array.from(dialog.querySelectorAll('button, .n-base-close'))
      .find((el) => isVisible(el) && (/关闭|确定|取消/.test(getElementText(el)) || el.classList?.contains('n-base-close')));

    if (closeButton) {
      simulateClick(closeButton);
      await sleep(300);
      return;
    }

    document.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', bubbles: true }));
    await sleep(300);
  }

  async function createCloudflareTempEmail(step, payload = {}) {
    const { generateNew = true } = payload;

    await ensureCreateAccountPage();
    await ensurePrefixDisabled();
    await selectRandomDomainSuffix();

    const input = getCreateAddressInput();
    if (!input) {
      throw new Error('Could not find the Cloudflare Temp Email address input.');
    }

    const createButton = findVisibleButton(/^创建新邮箱$/);
    if (!createButton) {
      throw new Error('Could not find the "创建新邮箱" button.');
    }

    for (let attempt = 1; attempt <= 3; attempt++) {
      const localPart = generateLocalPart();
      fillInput(input, localPart);
      await humanPause(120, 260);
      simulateClick(createButton);
      log(`Cloudflare Temp Email: Creating mailbox attempt ${attempt}`, 'info');

      try {
        const credential = await waitForCredentialDetails(10000);
        await dismissCredentialDialog();

        return {
          ...credential,
          domain: credential.domain || getCurrentDomain(),
          generated: Boolean(generateNew),
        };
      } catch (err) {
        if (attempt === 3) {
          throw err;
        }
        log(`Cloudflare Temp Email: Mailbox attempt ${attempt} did not complete, retrying`, 'warn');
        await dismissCredentialDialog().catch(() => {});
        await sleep(500);
      }
    }

    throw new Error('Cloudflare Temp Email mailbox creation did not succeed.');
  }

  function getMessageRows() {
    return Array.from(document.querySelectorAll('.n-thing')).filter(isVisible);
  }

  function parseMessageRow(row) {
    const rowText = row?.innerText || getElementText(row);
    const subject = normalizeText(
      row.querySelector('.n-thing-header__title')?.textContent
        || row.querySelector('h3, h4, h2')?.textContent
        || ''
    );
    const messageId = rowText.match(/ID:\s*([^\s]+)/i)?.[1] || null;
    const timestampText = rowText.match(/\d{4}\/\d{1,2}\/\d{1,2}\s+\d{1,2}:\d{2}:\d{2}/)?.[0] || '';
    const sender = normalizeText(rowText.match(/FROM:\s*([^\n]+)/i)?.[1] || '');
    const matchedEmail = normalizeEmail(rowText.match(/TO:\s*([^\n]+)/i)?.[1] || '');

    return {
      combinedText: normalizeText(rowText),
      emailTimestamp: null,
      matchedEmail,
      messageId,
      row,
      sender,
      subject,
      timestampText,
    };
  }

  function findMessageDetailRoot() {
    const deleteButton = Array.from(document.querySelectorAll('button'))
      .find((button) => isVisible(button) && /^删除$/.test(normalizeText(getElementText(button))));

    let current = deleteButton?.parentElement || null;
    while (current && current !== document.body) {
      const text = getElementText(current);
      if (/FROM:/i.test(text) && /TO:/i.test(text)) {
        return current;
      }
      current = current.parentElement;
    }

    return null;
  }

  async function openMessageRow(row, subject) {
    await humanPause(80, 180);
    simulateClick(row);
    await waitForCondition(() => {
      const detailRoot = findMessageDetailRoot();
      const detailText = normalizeText(getElementText(detailRoot));
      if (!detailText) return null;
      if (!subject || detailText.includes(subject)) {
        return detailRoot;
      }
      return null;
    }, 4000, `Timed out opening message ${subject || ''}`.trim());
  }

  function buildMessageDetailText() {
    return normalizeText(getElementText(findMessageDetailRoot()));
  }

  async function collectMessagesForTarget(targetEmail) {
    const normalizedTargetEmail = normalizeEmail(targetEmail);
    const rows = getMessageRows();
    const messages = [];

    for (const row of rows) {
      const message = parseMessageRow(row);
      if (!message.matchedEmail || message.matchedEmail !== normalizedTargetEmail) {
        continue;
      }

      if (!extractVerificationCode(`${message.subject} ${message.combinedText}`)) {
        await openMessageRow(row, message.subject);
        message.combinedText = `${message.combinedText} ${buildMessageDetailText()}`.trim();
      }

      messages.push(message);
    }

    return messages;
  }

  async function runMailQuery(targetEmail) {
    const queryInput = document.querySelector('input[placeholder="留空查询所有地址"]');
    if (!queryInput) {
      throw new Error('Could not find the admin mail query input.');
    }

    const queryButton = findVisibleButton(/^查询$/);
    if (!queryButton) {
      throw new Error('Could not find the admin mail query button.');
    }

    fillInput(queryInput, targetEmail);
    await humanPause(80, 180);
    simulateClick(queryButton);
    await sleep(800);
  }

  async function refreshMailList() {
    const refreshButton = findVisibleButton(/^刷新$/);
    if (!refreshButton) {
      throw new Error('Could not find the admin mail refresh button.');
    }

    simulateClick(refreshButton);
    await sleep(1000);
  }

  async function pollCloudflareTempEmail(step, payload = {}) {
    const {
      filterAfterTimestamp = 0,
      intervalMs = 3000,
      maxAttempts = 20,
      senderFilters = [],
      subjectFilters = [],
      targetEmail = '',
    } = payload;

    if (!targetEmail) {
      throw new Error('No target email provided for Cloudflare Temp Email polling.');
    }

    await ensureMailPage();
    await runMailQuery(targetEmail);

    log(`Step ${step}: Starting Cloudflare Temp Email poll for ${targetEmail}`, 'info');

    for (let attempt = 1; attempt <= maxAttempts; attempt++) {
      log(`Step ${step}: Polling Cloudflare Temp Email... attempt ${attempt}/${maxAttempts}`, 'info');
      await refreshMailList();

      const messages = await collectMessagesForTarget(targetEmail);
      const match = selectVerificationMessage(messages, {
        filterAfterTimestamp,
        senderFilters,
        subjectFilters,
        targetEmail,
      });

      if (match?.code) {
        log(
          `Step ${step}: Code found: ${match.code} (subject: ${(match.subject || '').slice(0, 60)})`,
          'ok'
        );
        return {
          ok: true,
          code: match.code,
          emailTimestamp: match.emailTimestamp,
          matchedEmail: match.matchedEmail,
          messageId: match.messageId,
          subject: match.subject,
        };
      }

      if (attempt < maxAttempts) {
        await sleep(intervalMs);
      }
    }

    const newerSuffix = Number(filterAfterTimestamp) > 0
      ? ' newer than the previous verification message'
      : '';

    throw new Error(
      `No matching verification email${newerSuffix} was found for ${targetEmail} after ${(maxAttempts * intervalMs / 1000).toFixed(0)}s.`
    );
  }
}
