
$box.on((event, data) => {
	console.log("Message received : " + event);

	if (event == 'stop') {
		console.log("Stopping");
		TaskManager.container.stop();
	}

});


/*-------------------------- On Load ------------------------*/

document.addEventListener('DOMContentLoaded', () => {

    window.dispatchEvent(new CustomEvent('GOOGLE_SEARCH_API', {
        'detail': {
            'data': null,
            'url': window.location.href,
            'isParsed': true
        }
    }));
});

const sleep = ms => new Promise(r => setTimeout(r, ms));
const GOOGLE_SEARCH_PAGE_SIZE = 20;
let delayBetweenPages = 5;

$(() => {
	console.log("Content.js loaded");
	$box.getLocal(local => {
		// TaskManager.startPendingTask(local);
		if (local.status) {
			console.log("Starting Process");
			delayBetweenPages = getDelayBetweenPages(local.delayBetweenPages);

			// openEachStore();
			startExtraction();
		}
	});

});



function startExtraction() {

	const script = document.createElement('script');
	script.src = chrome.runtime.getURL('controllers/ajax-hook.js');
	document.documentElement.appendChild(script);

	let paginationTimer = null;

	script.onload = () => {
		console.log('Ajax-hook js file injected.');

		window.addEventListener('GOOGLE_SEARCH_API', async (res) => {
			console.log('Google /search request found');

			// A small wait for list to get rendered properly.
			await sleep(1 * 1000);

			// let results;
			// try {
			// 	results = await dataParser();
			// } catch (err) {
			// 	console.log(err);
			// 	console.log(res.detail);
			// }
			let count = 0;
			try {
				for await (const item of dataParser()) {
					saveToLocal(() => {}, [item])
					count += 1;
				}
			} catch (err) {
				console.error("Scraping error:", err);
			}


			if (count === 0) {
				// no results meaning no more pages
				stopExtraction();
				return;
			}

			// saveToLocal(() => {
			// 	console.log('callback called.')
			// }, results);

			while (document.readyState != 'complete') {
				console.log('page is loading')
				await sleep(3 * 1000);
			}

			if (paginationTimer) {
				clearTimeout(paginationTimer);
			}

			paginationTimer = setTimeout(async () => {
				console.log("Timer finished! It's quiet, time to move to the next page.");
				nextPage();
			}, delayBetweenPages * 1000);

		});

		console.log('fetching initial data');
		window.dispatchEvent(new CustomEvent('GOOGLE_SEARCH_API', {
				'detail': {
					'data': null,
					'url': null,
					'isParsed': true
				}
			}));

	};
}


function stopExtraction() {

	console.log('stopping extraction');

	$box.getLocal(local => {
		if (!Array.isArray(local.taskList) || local.taskList.length === 0) {
			local.status = false;
			$box.setLocal(local, {
				onSet: () => {
					console.log("stopping");
					$box.send('stop');
				}
			});
			return;
		}

		local.taskList.shift();
		$box.setLocal(local, {
			onSet: () => {
				console.log("Sending send message");
				$box.send('start');
			}
		});

	})
}



function getDelayBetweenPages(value) {
	const delay = Number(value);
	return Number.isFinite(delay) && delay >= 0 ? delay : 5;
}

function getSearchStartParam(url) {
	const rawStart = url.searchParams.get('start');
	if (!rawStart) {
		return 0;
	}

	const start = Number(rawStart);
	return Number.isInteger(start) && start >= 0 ? start : 0;
}

function getFallbackSearchQuery(local = {}) {
	if (local.activeQuery) {
		return local.activeQuery;
	}

	if (Array.isArray(local.taskList) && local.taskList[0]) {
		return local.taskList[0].replace('~in~', ' in ');
	}

	return "";
}

function buildNextSearchResultsUrl(currentHref, activeQuery = "") {
	let url;
	try {
		url = new URL(currentHref);
	} catch (err) {
		console.warn('Unable to parse current search URL:', err);
		url = new URL('https://www.google.com/search');
	}

	const query = url.searchParams.get('q') || activeQuery;
	if (!query) {
		return null;
	}

	const nextStart = getSearchStartParam(url) + GOOGLE_SEARCH_PAGE_SIZE;
	url.searchParams.set('q', query);
	url.searchParams.set('udm', '1');
	url.searchParams.set('start', String(nextStart));

	['tbm', 'rflfq', 'num', 'sa'].forEach(param => {
		url.searchParams.delete(param);
	});

	return url.toString();
}

function nextPage() {
	$box.getLocal(local => {
		const nextUrl = buildNextSearchResultsUrl(window.location.href, getFallbackSearchQuery(local));

		if (!nextUrl) {
			console.log('Unable to build the next Google search results URL.');
			stopExtraction();
			return;
		}

		console.log('Opening next Google search results page:', nextUrl);
		window.location.assign(nextUrl);
	});
}

function normalizeCollectIdentityPart(value) {
	return String(value || '').trim().toLowerCase();
}

function getCollectIdentityBase(item = {}) {
	const parts = [
		item.keyword,
		item.location,
		item.company_name
	].map(normalizeCollectIdentityPart).filter(Boolean);

	return parts.length ? parts.join('|') : "";
}

function getCollectIdentityCandidates(item = {}) {
	const candidates = [];

	if (item.cid) {
		candidates.push(`cid:${normalizeCollectIdentityPart(item.cid)}`);
	}

	const baseKey = getCollectIdentityBase(item);
	if (!baseKey) {
		return candidates;
	}

	['address', 'phone', 'website', 'category'].forEach(field => {
		const value = normalizeCollectIdentityPart(item[field]);
		if (value) {
			candidates.push(`${field}:${baseKey}|${value}`);
		}
	});

	if (candidates.length === (item.cid ? 1 : 0)) {
		candidates.push(`base:${baseKey}`);
	}

	return _.uniq(candidates);
}

function buildCollectRowKey(item = {}) {
	if (item.row_key) {
		return item.row_key;
	}

	const candidates = getCollectIdentityCandidates(item);
	const stableKey = candidates.find(candidate => !candidate.startsWith('cid:')) || candidates[0];
	return stableKey || `generated:${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
}

function hasCollectValue(value) {
	return !_.isNil(value) && (!(typeof value === 'string') || value.trim() !== "");
}

function mergeCollectRecord(existing = {}, incoming = {}) {
	const merged = { ...existing };

	Object.keys(incoming).forEach(key => {
		const value = incoming[key];
		if (hasCollectValue(value) || !hasCollectValue(merged[key])) {
			merged[key] = value;
		}
	});

	merged.row_key = existing.row_key || incoming.row_key || buildCollectRowKey(merged);
	return merged;
}

function findCollectRecordIndex(collect = [], incoming = {}) {
	const incomingCandidates = new Set(getCollectIdentityCandidates(incoming));
	if (!incomingCandidates.size) {
		return -1;
	}

	return collect.findIndex(item =>
		getCollectIdentityCandidates(item).some(candidate => incomingCandidates.has(candidate))
	);
}

function saveToLocal(callback, results) {
	$box.getLocal(local => {
		if (!Array.isArray(local.collect)) {
			local.collect = [];
		}
		results = Array.isArray(results) ? results : [];

		const activeTask = Array.isArray(local.taskList) && local.taskList[0] ? local.taskList[0] : "";
		const [keyword = "", location = ""] = activeTask.split("~in~");

		for (let i = 0; i < results.length; i++) {
			let data = { ...results[i] };

			// Add data
			data.keyword = keyword.trim() || local.activeKeyword || "";
			data.location = location.trim() || local.activeLocation || "";

			// // if guest user
			if (!local._auth?.hasValidKey && local.collect.length >= 50) {
				console.log('** exceeded free quota...');
				local.showLoginModal = true;
				break;
			}

			const existingIndex = findCollectRecordIndex(local.collect, data);
			if (existingIndex === -1) {
				data.row_key = buildCollectRowKey(data);
				local.collect.push(data);
			} else {
				local.collect[existingIndex] = mergeCollectRecord(local.collect[existingIndex], data);
			}

		}

		console.log("** Saving Data...");

		$box.setLocal(local, {
			onSet: () => {
				console.log('** set successfully...');

				console.log("Calling callback");

				// if guest user
				if (!local._auth?.hasValidKey && local.collect.length >= 50) {
					$box.send('stop');
				} else {
					console.log("Calling callback");
					// calling callback
					callback(local);
				}
			}
		});

	})
}




var startTime = new Date();


function extractResults(app_ini_state = true) {
	/**
	 * Called when GoogleMaps search page is loaded,
	 * Saves the results and scrolls further for more results to load
	 * and saves them recursively.
	 */
	$box.getLocal(local => {
		console.log('** Extracting Google Maps data...');
		var timeDelay = Math.floor(Math.random() * (3 - 1)) + 3;
		saveResults((local) => {
			console.log('** saveResults callback called...');
			setTimeout(() => {
				// check if 'End of List' element is present on the page
				if (!endOfListExists()) {
					var timeDelay = Math.floor(Math.random() * (5 - 3)) + 3;
					scrollPage(); // scroll the page
					setTimeout(() => {

						extractResults(false); // now only scrapes data from requests
					}, timeDelay * 1000);
				} else {
					console.log("** End of list reached, opening new search query...");

					local.taskList.shift();
					$box.setLocal(local, {
						onSet: () => {
							console.log("Sendig send message");
							$box.send('start');
						}
					});
				}

			}, timeDelay * 1000)

		}, app_ini_state)
	})
}

function saveResults(callback, app_ini_state) {
	/**
	 * Saves the GoogleMaps result to storage.
	 * First time takes scrapes using Window.APP_INITIALTION method.
	 * From next time on scrolls the page, and intercepts the requests.
	 */
	$box.getLocal(local => {
		var results;

		if (app_ini_state) {
			// first time, scrape using window.APP_INIITIALIZATION

			// extracting data from JSON
			results = voegToeAanCollection(document.getElementsByTagName('html')[0].innerHTML);

		} else {

			// try parsing results from newDataHolder, fall to callback if fails
			try {
				// from next on, scrape by checking 'newData' element on the page
				results = leesCollection(document.getElementById('newDataHolder').getAttribute('newdata'));
				startTime = new Date();
			} catch (e) {
				// mydata is not valid, call callback
				console.log('** newdata not valid, calling callback...');
				setTimeout(() => {
					callback(local);
				}, 1000);
				return;
			}

		}

		// saving to storage
		saveToLocal(callback, results);

	})
}



// setTimeout(() => {
// 	/**
// 	 * Addes a requests intercepting script. Which will appen the data
// 	 * to 'newData' element on the page. Which will be used to add further data. 
// 	 */

// 	// adding a new element to DOM
// 	const dataHolder = document.createElement("div");
// 	dataHolder.setAttribute("id", "newDataHolder");
// 	document.body.appendChild(dataHolder);

// 	// observer for listening changes to added element
// 	const observer = new MutationObserver(function(mutations) {
// 		// pass
// 	});
// 	observer.observe(dataHolder, {
// 		//configure it to listen to attribute changes
// 		attributes: true
// 	});

// 	// script element for intercepting the requests
// 	const injectScript = () => {
// 		console.log("** Injecting Script");
// 		var script = document.createElement("script");
// 		script.src = chrome.runtime.getURL('/controllers/injectedScript.js');
// 		(document.head || document.documentElement).appendChild(script);
// 		script.remove();
// 	};

// 	injectScript(); // injecting the script to dom
// 	console.log("** Requests interceptions started...");
// }, 2* 1000)


// HELPER FUNCTIONS
function scrollPage() {
	/**
	 * Scrolls the results panel
	 */
	let currentTime = new Date();
	let minutesDifference = Math.abs(currentTime.getMinutes() - startTime.getMinutes());
	if (minutesDifference > 2) {
		console.log(`** ${minutesDifference} minutes elapsed before getting new data, refreshing the page...`);
		$box.getLocal(local => {
			// local.taskList.shift();
			$box.setLocal(local, {
				onSet: () => {
					console.log("Sendig send message");
					$box.send('start');
				}
			});
		})
	}
	console.log('** Scrolling page...', minutesDifference);

	document.querySelectorAll(".m6QErb .DxyBCb .kA9KIf")[0].scrollTop = 1000000;
	return;
}

function endOfListExists() {
	/**
	 * Return Existence of 'End of List' div element on Page.
	 */

	if (document.querySelectorAll(".fontHeadlineLarge").length) {
		console.log('single page result...');
		return true;
	}
	return document.getElementsByClassName('HlvSq').length;
}

// setTimeout(() => {
// 	/**
// 	 * Automattically reloading page after 30 secs to avoid stuck loading issue.
// 	 */
// 	console.log('** Quiting scrapping session after 5 minutes...');

// 	$box.getLocal(local => {
// 		local.taskList.shift();
// 		$box.setLocal(local, {
// 			onSet: () => {
// 				console.log("Sendig send message");
// 				$box.send('start');
// 			}
// 		});
// 	})

// }, 300 * 1000)


// SCRAPING FUNCTIONS
function voegToeAanCollection(data) {
	try {
		collectedFlag = true;
		var filteredData = null;
		if (!data.includes("window.APP_INITIALIZATION_STATE")) {
			filteredData = JSON.parse(data.slice(0, -6))
				.d;
		} else {
			filteredData = JSON.parse(data.slice((data.indexOf("window.APP_INITIALIZATION_STATE=") + 32), data.indexOf(";window.APP_FLAGS")))[3][2];
		}

		if (filteredData === null) {
			return;
		}

		var ew = filteredData.substr(4);
		if (ew) {
			return leesCollection(ew);
		}
	} catch (error) {
		if (error.response) {
			console.log(error.response.data);
			console.log(error.response.status);
			console.log(error.response.headers);
		} else if (error.request) {
			console.log(error.request);
		} else {
			console.log('Error', error.message);
		}
		console.log(error);
	}

	return;
}

function leesCollection(values) {
	var data = JSON.parse(values)[0][1];
	var amount = data.length;

	var results = [];
	for (i = 0; i < amount; i++) {
		if (data[i].length == 15) {
			result = {}

			/*
				1. company_name - done
				2. website - done
				3. cid - done
				4. review - done
				5. rating_count - done
				6. address - done
				7. pincode - done
				8. subtitle
				9. category - done
				10. state - done
				11. city - done
			*/

			// company_name
			result.company_name = data[i][14][11];

			// website
			var url = '';
			if (data[i][14][7]) {
				url = data[i][14][7][0];
			}
			result.website = url;

			// cid and google maps url
			cid = hexToDec(data[i][14][10].split(':')[1]);
			gmu = 'https://www.google.com/maps?cid=' + cid;

			result.cid = cid;

			// phone
			try {
				if (data[i][14][178][0][0]) {
					var phone = data[i][14][178][0][3];
				}
			} catch (e) {
				var phone = '';
			}
			result.phone = phone;

			// review
			try {
				if (data[i][14][4][8]) {
					var total_review = data[i][14][4][8];
				}
			} catch (e) {
				var total_review = '';
			}
			result.rating_count = total_review;


			// ratings
			try {
				if (data[i][14][4][7]) {
					var rating = data[i][14][4][7];
				}
			} catch (e) {
				var rating = '';
			}
			result.review = rating;

			// address
			var address = ''
			if (data[i][14][39]) {
				address = data[i][14][39];
			}
			result.address = address;

			// pincode
			result.pincode = result.address.match(/\b[1000-999999]+\b/g);
			result.pincode = result.pincode ? result.pincode : [];
			result.pincode = result.pincode.length ? result.pincode.pop() : "";

			// category
			try {
				var category = data[i][14][13][0];
			} catch (e) {
				var category = '';
			}
			result.category = category;

			// city
			try {
				var last_add = data[i][14][2][data[i][14][2].length - 1];
			} catch (e) {
				var last_add = ''
			}

			try {
				var city = last_add.split(',')[0].trim();
			} catch (e) {
				var city = '';
			}
			result.city = city;

			// state
			try {
				var state = last_add.split(',')[1].replace(result.pincode, '').trim();
			} catch (e) {
				var state = '';
			}

			result.state = state;

			// latitude
			var lat = data[i][14][9][2];
			result.lat = lat;

			// longitude
			var long = data[i][14][9][3];
			result.long = long;

			results.push(result);
		}
	}
	return results;
}

/*-------------------------- On Load ------------------------*/


/*---------------------- Extension Toolbar ---------------------
$(function(){

	let extensionToolbar = {};
	extensionToolbar.height = "50px";
	extensionToolbar.url = chrome.extension.getURL("views/extensionToolbar.html");

	$('body').css('-webkit-transform','translateY('+extensionToolbar.height+')');
	$('html').append(`
	<iframe src="${extensionToolbar.url}" id="simple-chrome-extension-toolbar" style="height:${extensionToolbar.height};"></iframe>
	`);

});
---------------------- Extension Toolbar ---------------------*/
