/**
 * Google Web Search via browser DOM extraction.
 * Uses browser mode to navigate google.com and extract results from the DOM.
 *
 * Extraction strategy (2026-03): Google no longer uses `.g` class containers.
 * Instead, we find all `a` tags containing `h3` within `#rso`, then walk up
 * to the result container (`div.tF2Cxc` or closest `div[data-hveid]`) to find
 * snippets. This approach is resilient to class name changes.
 */
import { cli, Strategy } from '@jackwener/opencli/registry';
import { CliError } from '@jackwener/opencli/errors';
cli({
    site: 'google',
    name: 'search',
    access: 'read',
    description: 'Search Google',
    domain: 'google.com',
    strategy: Strategy.PUBLIC,
    browser: true,
    args: [
        { name: 'keyword', positional: true, required: true, help: 'Search query' },
        { name: 'limit', type: 'int', default: 10, help: 'Number of results (1-100)' },
        { name: 'lang', default: 'en', help: 'Language short code (e.g. en, zh)' },
    ],
    columns: ['type', 'title', 'url', 'snippet'],
    func: async (page, args) => {
        const limit = Math.max(1, Math.min(Number(args.limit), 100));
        const keyword = encodeURIComponent(args.keyword);
        const lang = encodeURIComponent(args.lang);
        const url = `https://www.google.com/search?q=${keyword}&hl=${lang}&num=${limit}`;
        await page.goto(url);
        // Wait until at least one SERP title link is present. On Chrome 148 /
        // Linux Wayland, DOM stability can be reached before #rso anchors are
        // populated, making browser execution look visually correct while the
        // adapter extracts an empty array.
        try {
            await page.wait({ selector: '#rso a h3', timeout: 5 });
        }
        catch {
            await page.wait(2);
        }
        const wrapper = await page.evaluate(`
      (function() {
        var results = [];
        var seenUrls = {};
        var rso = document.querySelector('#rso');
        if (!rso) return {items: results};

        // -- Featured snippet (scoped to #rso to avoid matching unrelated elements) --
        var featuredEl = rso.querySelector('.xpdopen .hgKElc')
                      || rso.querySelector('.IZ6rdc');
        if (featuredEl) {
          var parentBlock = featuredEl.closest('[data-hveid]') || featuredEl.parentElement;
          var fLink = parentBlock ? parentBlock.querySelector('a[href]') : null;
          var fUrl = fLink ? fLink.href : '';
          if (fUrl) seenUrls[fUrl] = true;
          results.push({
            type: 'snippet',
            title: featuredEl.textContent.trim().slice(0, 200),
            url: fUrl,
            snippet: '',
          });
        }

        // -- Standard search results --
        // Strategy: find all links containing h3 within #rso
        var allLinks = rso.querySelectorAll('a');
        for (var i = 0; i < allLinks.length; i++) {
          var link = allLinks[i];
          var h3 = link.querySelector('h3');
          if (!h3) continue;

          var href = link.href || '';
          // Skip non-http, Google internal links, and duplicates
          if (!(href.startsWith('http://') || href.startsWith('https://'))) continue;
          if (href.indexOf('google.com/search') !== -1) continue;
          if (seenUrls[href]) continue;
          seenUrls[href] = true;

          // Walk up to find result container for snippet extraction
          var container = link;
          for (var j = 0; j < 6; j++) {
            if (container.parentElement && container.parentElement !== rso) {
              container = container.parentElement;
            }
            // Stop at a known result boundary
            if (container.getAttribute && container.getAttribute('data-hveid')) break;
          }

          // Find snippet: look for descriptive text, skip breadcrumbs and metadata
          var snippetText = '';
          var titleText = h3.textContent.trim();
          var candidates = container.querySelectorAll('span, div');
          for (var k = 0; k < candidates.length; k++) {
            var el = candidates[k];
            if (el.querySelector('h3') || el.querySelector('a[href]')) continue;
            var text = el.textContent.trim();
            if (text.length < 40 || text.length > 500) continue;
            if (text === titleText) continue;
            // Skip URL breadcrumbs (e.g. "https://example.com › path..." or "Site Namehttps://...")
            if (text.indexOf('\u203A') !== -1) continue;
            if (new RegExp('https?://').test(text.slice(0, 60))) continue;
            snippetText = text;
            break;
          }

          results.push({
            type: 'result',
            title: h3.textContent.trim(),
            url: href,
            snippet: snippetText.slice(0, 300),
          });
        }

        // -- People Also Ask --
        var paaContainers = document.querySelectorAll('[data-sgrd="true"]');
        for (var i = 0; i < paaContainers.length; i++) {
          var questionEl = paaContainers[i].querySelector('span.CSkcDe');
          if (questionEl) {
            results.push({
              type: 'paa',
              title: questionEl.textContent.trim(),
              url: '',
              snippet: '',
            });
          }
        }

        return {items: results};
      })()
    `);
        const results = (wrapper && wrapper.items) || [];
        if (results.length === 0) {
            throw new CliError('NOT_FOUND', 'No search results found', 'Try a different keyword or check for CAPTCHA');
        }
        return results;
    },
});
