/* Utility functions for search results parser. */
function add(r, n, t) { for (var e = [], u = Math.max(r.length, n.length), l = 0, o = 0; o < u || l;) { var a, i = l + (o < r.length ? r[o] : 0) + (o < n.length ? n[o] : 0); e.push(i % t), l = Math.floor(i / t), o++ } return e } function multiplyByNumber(r, n, t) { if (r < 0) return null; if (0 == r) return []; for (var e = [], u = n; 1 & r && (e = add(e, u, t)), 0 != (r >>= 1);)u = add(u, u, t); return e } function parseToDigitsArray(r, n) { for (var t = r.split(""), e = [], u = t.length - 1; u >= 0; u--) { var l = parseInt(t[u], n); if (isNaN(l)) return null; e.push(l) } return e } function decToHex(r) { var n = convertBase(r, 10, 16); return n ? "0x" + n : null } function convertBase(r, n, t) { var e = parseToDigitsArray(r, n); if (null === e) return null; for (var u = [], l = [1], o = 0; o < e.length; o++)e[o] && (u = add(u, multiplyByNumber(e[o], l, t), t)), l = multiplyByNumber(n, l, t); for (var a = "", o = u.length - 1; o >= 0; o--)a += u[o].toString(t); return a } function hexToDec(r) { return "0x" === r.substring(0, 2) && (r = r.substring(2)), convertBase(r = r.toLowerCase(), 16, 10) }
const getText = (element, selector) => {
    if (!element) return null;
    const el = element.querySelector(selector);
    return el ? el.innerText.trim() : null;
};
function getAddressFromLink(element) {
    if (!element) return null;
    const url = element.getAttribute('href');
    
    if (!url) return null;
    const match = url.match(/\/maps\/dir\/\/(.*?)\/data=/);

    if (match && match[1]) {
        const withSpaces = match[1].replace(/\+/g, ' ');
        return decodeURIComponent(withSpaces);
    }

    return null;
}
const isOnlyDigits = (str) => {
  if (!str) return false;

  // 1. Remove ALL whitespace characters globally
  const cleanedStr = String(str).replace(/\s/g, ''); 
  
  // 2. Now check the cleaned string for strict digits
  const isOnlyDigits = /^\d+$/.test(cleanedStr);
  
  return isOnlyDigits
};


// Extracts the CID from itemNode, which is in hex format.
// Thanks to Claude4.7.
function getCID(itemNode) {
  if (!itemNode) return null;
  const link = itemNode.querySelector('a[href*="/maps/dir/"], a[data-url*="/maps/dir/"]');
  if (!link) return null;
  const href = link.getAttribute('href') || link.getAttribute('data-url') || '';
  const urls = [href, link.getAttribute('data-url') || ''].filter((url, index, list) => url && list.indexOf(url) === index);
  const match = href.match(/1s0x[0-9a-f]+:0x([0-9a-f]+)/i);
  if (match) return BigInt('0x' + match[1]).toString();;
  
  // Fallback: decimal CID in URL params / hash
  for (const url of urls) {
    const m = url.match(/[#&?](?:cid|ludocid|rlimm)=(\d+)/);
    if (m) return m[1];
  }

  // Second Fallback: 
  return null;
}

// Claude
function getPhone(itemNode, panelNode) {
	let phone = null;
	const details = itemNode?.querySelector('.rllt__details');
	if (details) {
		for (const row of details.querySelectorAll(':scope > div')) {
		const text = (row.textContent || '').trim();
		for (const part of text.split(/\s·\s/)) {
			const t = part.trim();
			if (!/^[+\d\s\-()]+$/.test(t)) continue;
			const digits = (t.match(/\d/g) || []).length;
			if (digits >= 7 && digits <= 15) { phone = t; break; }
		}
		if (phone) return phone.replace(/\s+/g, '');
		}
	}

	if (!phone) {
		phone = panelNode?.querySelector('[data-phone-number]')?.getAttribute('data-phone-number');
	}

	return phone;
}

// content.js:78 Scraping error: TypeError: Cannot read properties of null (reading 'replace')
//     at isOnlyDigits (parser.js:22:26)
//     at dataParser (parser.js:198:8)
//     at dataParser.next (<anonymous>)
//     at content.js:73:22

// Claude
function getWebsite(panelNode) {
	let website = null;
	const anchors = panelNode?.querySelectorAll('a[href], a[data-url]') || [];
	for (const a of anchors) {
		const href = a.getAttribute('href') || '';
		if (/^https?:\/\//i.test(href) && !/^https?:\/\/(?:[\w-]+\.)*google\./i.test(href)) {
		website = href;
		break;
		}
	}
	return website;
}

function getAddress(itemNode, panelNode) {
  if (!itemNode) return null;
  const roots = [
    itemNode,
    panelNode
  ].filter(Boolean);

  // Primary: Google Places popup / knowledge panel address row.
  const addressSelectors = [
    '[data-attrid="kc:/location/location:address"] .LrzXr',
    '[data-local-attribute="d3adr"] .LrzXr',
    '[data-attrid="kc:/location/location:address"] [data-dtype="d3ifr"]',
    '[data-attrid="kc:/location/location:address"]',
  ];

  const addSpan = panelNode?.querySelector('[jsname="Q7Zjwb"]')?.querySelector('span')?.innerText;
  if (addSpan) {return addSpan}

  for (const root of roots) {
    for (const selector of addressSelectors) {
      const el = root.querySelector(selector);
      const text = (el?.textContent || '').replace(/^Address:\s*/i, '').trim();
      if (text) return text;
    }
  }

  // Fallback: older list layout where address and phone share the same row.
  for (const root of roots) {
    const details = root.querySelector('.rllt__details');
    if (details) {
      for (const row of details.querySelectorAll(':scope > div')) {
        const text = (row.textContent || '').trim();
        const parts = text.split(/\s·\s/).map(p => p.trim()).filter(Boolean);
        let hasPhone = false;
        const others = [];
        for (const p of parts) {
          const digits = (p.match(/\d/g) || []).length;
          if (/^[+\d\s\-()]+$/.test(p) && digits >= 7 && digits <= 15) {
            hasPhone = true;
          } else {
            others.push(p);
          }
        }
        if (hasPhone && others.length) return others.join(' · ');
      }
    }
  }

  // Fallback: parse from Directions URL
  for (const root of roots) {
    const anchors = root.querySelectorAll('a[href], a[data-url]');
    for (const a of anchors) {
      const href = a.getAttribute('href') || a.getAttribute('data-url') || '';
      const m = href.match(/\/maps\/dir\/\/([^/]+)\/data=/);
      if (m) return decodeURIComponent(m[1].replace(/\+/g, ' '));
    }
  }

  return null;
}

async function* dataParser () {
	const items = document.querySelectorAll('[jscontroller="AtSb"]');
	
	if (!items.length) {
		console.log('No results found');
		return null;
	}

	for (const item of items) {
		const data = item.querySelector('.rllt__details');
		if (!data) {
			console.log('Skipping result without details node.');
			continue;
		}

		const links = Array.from(item.querySelectorAll('a'));

		const name = getText(data, '[role="heading"]');
		
		const cidData = item.querySelector('[data-cid]');
		let cid = cidData?.getAttribute('data-cid');
		// let lat = cidData?.getAttribute('data-lat');
		// let long = cidData?.getAttribute('data-lng');
		let rating = getText(data, '.yi40Hd');

		let numberOfReviews = getText(data, '.RDApEe');
		if (numberOfReviews) {
			// Remove parentheses
			numberOfReviews = numberOfReviews.replace(/[()]/g, '').trim();
		}

		let category = null;
		const ratingContainer = data.querySelector('.Y0A0hc')?.closest('div');
		
		if (ratingContainer) {
			const text = ratingContainer.innerText;
			if (text.includes('·')) {
			category = text.split('·').pop().trim();
			}
		}

		let phoneNumber = null;
		const divs = Array.from(data.children);
		
		for (const div of divs) {
			const text = div.innerText;
			if (text.includes('·')) {
				const parts = text.split('·');
				const potentialPhone = parts[parts.length - 1].trim();
				
				if (potentialPhone !== category && /\d/.test(potentialPhone)) {
					phoneNumber = potentialPhone;
				}
			}
		}
		if (!phoneNumber || !isOnlyDigits(phoneNumber)) {
			phoneNumber = "";
		}

		// Address
		let full_address = null;
		let address = null;
		const addLink = links.find(a => {
			const href = a.getAttribute('href');
			return href && href.includes('/maps/dir');
		});

		let websiteLink = links.slice(1).find(l => l.textContent.toLowerCase().includes('website'));
		let website = websiteLink?.href;
		
		if (true) { // click the panel for every result.
			const heading = data.querySelector('[role="heading"]');
			if (heading) {
				heading.click();
				await sleep(3000);

				const panel =  document.querySelector('[data-viewer-type]') || document.querySelector('[decode-data-ved]');
				// full_address = getText(panel, '.LrzXr');

				phoneNumber = getPhone(item, panel) || "";
				website = getWebsite(panel) || "";
				full_address = getAddress(item, panel) || "";
			} else {
				full_address = getAddressFromLink(addLink);
			}

			if (!cid) {
				// This doesn't work as of 18/04/26;
				// But Google might change the format to this.
				cid = item.querySelector('[data-cid]')?.getAttribute('data-cid');
				
				if (!cid) {
					cid = getCID(item);
				}
			}

		} else {
			full_address = getAddressFromLink(addLink);
		}
		let pincode = "";
		let city = "";
		let state = "";

		if (full_address && !isOnlyDigits(full_address)) {
			address = full_address;
			const parts = address.split(',').map(p => p.trim()).filter(Boolean);
			const last = parts[parts.length - 1] || '';
			const pincodeMatch = address.match(/\b\d{6}\b/);
			pincode = pincodeMatch ? pincodeMatch[0] : "";
			state = last.replace(/\b\d{6}\b/, '').trim();
			city = (parts[parts.length - 2] || '').replace(/\b\d{6}\b/, '').trim();
		}
		

		
		
		// if there is no website, then check if it's availabe in panel
		
		yield {
			company_name: name,
			cid: cid ? cid : "",
			category: category,
			phone: phoneNumber,
			website: website,
			review: numberOfReviews,
			rating_count: rating,
			city: city,
			state: state,
			// lat: lat,
			// long: long,
			pincode: pincode,
			address: address
		}
	}
}
