Object.entries(require('./common.js')).forEach(([name, exported]) => global[name] = exported);
const parse = require('csv-parse/lib/sync');
const pc = /^([a-z]\d){3}$/i;
const apm = /^(\d{1,2}):(\d{2})\s?(([ap])\.?m\.?)?$/i;
async function alberta(browser, page) {
	if(page === undefined) {
		page = await browser.newPage();
	}
	page = await mnmalism(page);
	await page.goto('https://aglc.ca/cannabis/retail-cannabis/cannabis-licensee-search');
	let ab = (await page.$eval('#cannabis-results > div > table > tbody', e => e.innerText)).split('\n');
	page.close();
	let idx = [];
	let names = [];
	let address = [];
	let city = [];
	let postal_code = [];
	for(let i = 0; i < ab.length; ++i) {
		ab[i] = ab[i].split('\t');
		idx.push(i);
		names.push(ab[i][1] = sanitize_name(ab[i][1]));
		address.push(ab[i][2]);
		postal_code.push(ab[i][3]);
		city.push(ab[i][0]);
	}
	await postgres.query(
		`create temporary table alberta as 
		select *
		from unnest (
			$1::smallint[],
			$2::citext[],
			$3::citext[],
			$4::citext[],
			$5::citext[]
		) as t(idx, joined_name, address, city, postal_code)`,
		[idx, names, address, city, postal_code]
	);
	idx = (await postgres.query(`
		select idx
		from alberta
		where not exists (
			select
			from store_temp
			where
				joined_name = alberta.joined_name and
				address = alberta.address and
				address2 = '' and
				city = alberta.city and
				region = 'alberta' and
				country = 'canada' and
				postal_code = alberta.postal_code
		)
	`)).rows.map(x => x.idx);
	names = [];
	address = [];
	let address2 = [];
	city = [];
	let region = [];
	let country = [];
	postal_code = [];
	let longitude = [];
	let latitude = [];
	let osrm_hint_foot = [];
	let osrm_hint_car = [];
	let timezone = [];
	let phone = [];
	for(let i = 0; i < idx.length; ++i) {
		let t = ab[idx[i]];
		let r;
		try {
			r = await g(`${t[2]} ${t[0]} alberta ${t[3]} canada`);
		} catch(e) {
			console.error(e);
			console.log(t);
			continue;
		}
		if(!pc.test(r[0].postal_code)) {
			t[3] = t[3].replace(/\s+/g, '');
			if(r[0].postal_code.length === 3 && t[3].length === 6) {
				r[0].postal_code = t[3];
			} else {
				console.error('postal_code goofup', r, t);
				continue;
			}
		}
		names.push(t[1]);
		address.push(r[0].address);
		address2.push(r[0].address2);
		city.push(r[0].city);
		region.push(r[0].region);
		country.push(r[0].country);
		postal_code.push(r[0].postal_code);
		longitude.push(r[1].lng);
		latitude.push(r[1].lat);
		osrm_hint_foot.push(r[3]);
		osrm_hint_car.push(r[4]);
		timezone.push(r[2]);
		phone.push(t[4]);
	}try{
	console.log((await postgres.query(`insert into store (
		joined_name,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone,
		partner,
		delivery,
		pickup,
		prepayment,
		name
	) select i.*, false, false, false, false, i.joined_name from unnest (
		$1::citext[],
		$2::citext[],
		$3::citext[],
		$4::citext[],
		$5::citext[],
		$6::citext[],
		$7::citext[],
		$8::decimal[],
		$9::decimal[],
		$10::text[],
		$11::text[],
		$12::text[],
		$13::packed_phone_number[]
	) as i(
		joined_name,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone
	) where not exists (select 1 from store_history where
		store_history.joined_name = i.joined_name and
		store_history.address = i.address and
		store_history.address2 is not distinct from i.address2 and
		store_history.city = i.city and
		store_history.region = i.region and
		store_history.country = i.country and
		store_history.postal_code = i.postal_code
	) or exists (select 1 from store where
		store.joined_name = i.joined_name and
		store.address = i.address and
		store.address2 is not distinct from i.address2 and
		store.city = i.city and
		store.region = i.region and
		store.country = i.country and
		store.postal_code = i.postal_code
	) on conflict (joined_name, address, address2, city, region, country, postal_code)
	where address is not null and address2 is not null and city is not null and postal_code is not null
	do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
	where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
		names,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone
	])).rowCount, 'alberta');} catch(e) {console.log(e); console.log('alberta failed')}
}
async function manitoba(browser, page) {
	if(page === undefined) {
		page = await browser.newPage();
	}
	page = await mnmalism(page);
	await page.goto('https://lgcamb.ca/cannabis/store-list/');
	let results = await page.$$eval('.e2272-10.x-text ul', x => {
		for(let i = x.length - 1; i >= 0; --i) {
			x[i] = x[i].textContent.trim();
			if(x[i].length === 0) {
				x.splice(i, 1);
			} else {
				x[i] = x[i].split('\n');
				for(let j = 0; j < x[i].length; ++j) {
					let idx = x[i][j].indexOf(",");
					x[i][j] = [x[i][j].substring(0, idx).trim(), x[i][j].substring(idx + 1).trim()];
				}
			}
		}
		return x;
	});
	let idk = await page.$$eval('.e2272-10.x-text strong', e => e.map(x => x.textContent.replace(':', '').trim()));
	page.close();
	if(results.length === idk.length) {
		let idx = [];
		let jdx = [];
		let names = [];
		let address = [];
		let city = [];
		for(let i = 0; i < idk.length; ++i) {
			for(let j = 0; j < results[i].length; ++j) {
				idx.push(i);
				jdx.push(j);
				names.push(results[i][j][0] = sanitize_name(results[i][j][0]));
				address.push(results[i][j][1]);
				city.push(idk[i]);
			}
		}
		await postgres.query(
			`create temporary table manitoba as 
			select *
			from unnest (
				$1::smallint[],
				$2::smallint[],
				$3::citext[],
				$4::citext[],
				$5::citext[]
			) as t(idx, jdx, joined_name, address, city)`,
			[idx, jdx, names, address, city]
		);
		let temp = (await postgres.query(`
			select idx, jdx
			from manitoba
			where not exists (
				select
				from store_temp
				where
					joined_name = manitoba.joined_name and
					address = manitoba.address and
					address2 = '' and
					city = manitoba.city and
					region = 'manitoba' and
					country = 'canada' and
					postal_code = ''
			)
		`)).rows;
		idx = temp.map(x => x.idx);
		jdx = temp.map(x => x.jdx);
		names = [];
		address = [];
		let address2 = [];
		city = [];
		let region = [];
		let country = [];
		let postal_code = [];
		let longitude = [];
		let latitude = [];
		let osrm_hint_foot = [];
		let osrm_hint_car = [];
		let timezone = [];
		for(let k = 0; k < idx.length; ++k) {
			const i = idx[k];
			const j = jdx[k];
			if(results[i][j][1] === 'Otineka Mall' && results[i][j][0] === 'Meta') {
				names.push(results[i][j][0]);
				address.push('Otineka Mall Highway 10 North');//gmaps ok
				address2.push('Unit 128');
				city.push('Opaskwayak');
				region.push('Manitoba');
				country.push('Canada');
				postal_code.push('R0B2J0');
				longitude.push('-101.261585');
				latitude.push('53.834096');
				osrm_hint_foot.push(await request(`http://127.0.0.1:5000/nearest/v1/fuck/-101.261585,53.834096`).then(x => JSON.parse(x).waypoints[0].hint));
				osrm_hint_car.push(await request(`http://127.0.0.1:5001/nearest/v1/fuck/-101.261585,53.834096`).then(x => JSON.parse(x).waypoints[0].hint));
				timezone.push('America/Winnipeg');
			} else {
				let r;
				try {
					r = await g(`${results[i][j][1]} ${idk[i]} manitoba canada`);
				} catch(e) {
					console.error(e);
					console.log(results[i][j]);
					continue;
				}
				if(!pc.test(r[0].postal_code)) {
					if(results[i][j][1] === '300A North Railway Street') {
						r[0].postal_code = 'R6M1S7';
					} else {
						console.error('postal_code goofup', r, results[i][j], idk[i]);
						continue;
					}
				}
				names.push(results[i][j][0]);
				address.push(r[0].address);
				address2.push(results[i][j][1] === '300A North Railway Street' ? 'A' : r[0].address2);
				city.push(r[0].city);
				region.push(r[0].region);
				country.push(r[0].country);
				postal_code.push(r[0].postal_code);
				longitude.push(r[1].lng);
				latitude.push(r[1].lat);
				osrm_hint_foot.push(r[3]);
				osrm_hint_car.push(r[4]);
				timezone.push(r[2]);
			}
		}try{
		console.log((await postgres.query(`insert into store (
			joined_name,
			address,
			address2,
			city,
			region,
			country,
			postal_code,
			longitude,
			latitude,
			osrm_hint_foot,
			osrm_hint_car,
			timezone,
			partner,
			delivery,
			pickup,
			prepayment,
			name
		) select i.*, false, false, false, false, i.joined_name from unnest (
			$1::citext[],
			$2::citext[],
			$3::citext[],
			$4::citext[],
			$5::citext[],
			$6::citext[],
			$7::citext[],
			$8::decimal[],
			$9::decimal[],
			$10::text[],
			$11::text[],
			$12::text[]
		) as i(
			joined_name,
			address,
			address2,
			city,
			region,
			country,
			postal_code,
			longitude,
			latitude,
			osrm_hint_foot,
			osrm_hint_car,
			timezone
		) where not exists (select 1 from store_history where
			store_history.joined_name = i.joined_name and
			store_history.address = i.address and
			store_history.address2 is not distinct from i.address2 and
			store_history.city = i.city and
			store_history.region = i.region and
			store_history.country = i.country and
			store_history.postal_code = i.postal_code
		) or exists (select 1 from store where
			store.joined_name = i.joined_name and
			store.address = i.address and
			store.address2 is not distinct from i.address2 and
			store.city = i.city and
			store.region = i.region and
			store.country = i.country and
			store.postal_code = i.postal_code
		) on conflict (joined_name, address, address2, city, region, country, postal_code)
		where address is not null and address2 is not null and city is not null and postal_code is not null
		do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
		where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
			names,
			address,
			address2,
			city,
			region,
			country,
			postal_code,
			longitude,
			latitude,
			osrm_hint_foot,
			osrm_hint_car,
			timezone
		])).rowCount, 'manitoba');} catch(e) {console.log(e); console.log('manitoba failed')}
	} else {
		console.error("https://lgcamb.ca/cannabis/store-list/ city mismatch")
	}
}
async function saskatchewan(browser, page) {
	if(page === undefined) {
		page = await browser.newPage();
	}
	page = await mnmalism(page);
	await page.goto('https://www.slga.com/permits-and-licences/cannabis-permits/cannabis-retailing/cannabis-retailers-in-saskatchewan');
	let results = await page.$$eval('#content h1 + p', e => e.map(x => {
		let t = x.textContent.split('\n');
		if(t.length === 1) {
			while((x = x.nextElementSibling) && x.textContent.trim().toLowerCase() !== 'go to top') {
				t.push(x.textContent.trim());
			}
		}
		return t;
	}));
	page.close();
	let idx = [];
	let names = [];
	let address = [];
	let city = [];
	for(let i = 0; i < results.length; ++i) {
		idx.push(i);
		names.push(results[i][0] = sanitize_name(results[i][0]));
		address.push(results[i][1]);
		city.push(results[i][2]);
	}
	await postgres.query(
		`create temporary table saskatchewan as 
		select *
		from unnest (
			$1::smallint[],
			$2::citext[],
			$3::citext[],
			$4::citext[]
		) as t(idx, joined_name, address, city)`,
		[idx, names, address, city]
	);
	idx = (await postgres.query(`
		select idx
		from saskatchewan
		where not exists (
			select
			from store_temp
			where
				joined_name = saskatchewan.joined_name and
				address = saskatchewan.address and
				address2 = '' and
				city = saskatchewan.city and
				region = 'saskatchewan' and
				country = 'canada' and
				postal_code = ''
		)
	`)).rows.map(x => x.idx);
	names = [];
	address = [];
	let address2 = [];
	city = [];
	let region = [];
	let country = [];
	let postal_code = [];
	let longitude = [];
	let latitude = [];
	let osrm_hint_foot = [];
	let osrm_hint_car = [];
	let timezone = [];
	let url = [];
	for(let j = 0; j < idx.length; ++j) {
		const i = idx[j];
		if(results[i][1] === '82B Battlefords Crossing') {
			names.push('Fire & Flower');
			address.push('Battleford Crossing');//googleable, but osm and gmaps no results. fallback to lng/lat
			address2.push('82B');
			city.push('Battleford');
			region.push('Saskatchewan');
			country.push('Canada');
			postal_code.push('S0M0E0');
			longitude.push('-108.3195177');//https://www.openstreetmap.org/node/6257183759
			latitude.push('52.7406683');
			osrm_hint_foot.push(await request(`http://127.0.0.1:5000/nearest/v1/fuck/-108.3195177,52.7406683`).then(x => JSON.parse(x).waypoints[0].hint));
			osrm_hint_car.push(await request(`http://127.0.0.1:5001/nearest/v1/fuck/-108.3195177,52.7406683`).then(x => JSON.parse(x).waypoints[0].hint));
			timezone.push('America/Regina');
			url.push(results[i][3]);
		} else if(results[i][0] === 'Vatic') {
			names.push('Vatic');
			address.push('Building # 1 – Unit #15 SE 6-18-18-Parcel B Plan #101924726');//gmaps ok
			address2.push('');
			city.push('Edenwold No. 158');
			region.push('Saskatchewan');
			country.push('Canada');
			postal_code.push('S0G3Z0');
			longitude.push('-104.459866');
			latitude.push('50.488856');
			osrm_hint_foot.push(await request(`http://127.0.0.1:5000/nearest/v1/fuck/-104.459866,50.488856`).then(x => JSON.parse(x).waypoints[0].hint));
			osrm_hint_car.push(await request(`http://127.0.0.1:5001/nearest/v1/fuck/-104.459866,50.488856`).then(x => JSON.parse(x).waypoints[0].hint));
			timezone.push('America/Regina');
			url.push(results[i][3]);
		} else {
			results[i][2] = results[i][2].replace(/\bsk\b/i, 'saskatchewan');
			if(results[i][2].indexOf('saskatchewan') === -1) {
				results[i][2] += 'saskatchewan';
			}
			let r;
			try {
				r = await g(`${results[i][1]} ${results[i][2]} canada`);
			} catch(e) {
				console.error(e);
				console.log(results[i]);
				continue;
			}
			if(!pc.test(r[0].postal_code)) {
				console.error('postal_code goofup', r, results[i]);
				continue;
			}
			names.push(results[i][0]);
			address.push(r[0].address);
			address2.push(r[0].address2);
			city.push(r[0].city);
			region.push(r[0].region);
			country.push(r[0].country);
			postal_code.push(r[0].postal_code);
			longitude.push(r[1].lng);
			latitude.push(r[1].lat);
			osrm_hint_foot.push(r[3]);
			osrm_hint_car.push(r[4]);
			timezone.push(r[2]);
			url.push(results[i][3]);
		}
	}try {
	console.log((await postgres.query(`insert into store (
		joined_name,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		url,
		partner,
		delivery,
		pickup,
		prepayment,
		name
	) select i.*, false, false, false, false, i.joined_name from unnest (
		$1::citext[],
		$2::citext[],
		$3::citext[],
		$4::citext[],
		$5::citext[],
		$6::citext[],
		$7::citext[],
		$8::decimal[],
		$9::decimal[],
		$10::text[],
		$11::text[],
		$12::text[],
		$13::text[]
	) as i(
		joined_name,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		url
	) where not exists (select 1 from store_history where
		store_history.joined_name = i.joined_name and
		store_history.address = i.address and
		store_history.address2 is not distinct from i.address2 and
		store_history.city = i.city and
		store_history.region = i.region and
		store_history.country = i.country and
		store_history.postal_code = i.postal_code
	) or exists (select 1 from store where
		store.joined_name = i.joined_name and
		store.address = i.address and
		store.address2 is not distinct from i.address2 and
		store.city = i.city and
		store.region = i.region and
		store.country = i.country and
		store.postal_code = i.postal_code
	) on conflict (joined_name, address, address2, city, region, country, postal_code)
	where address is not null and address2 is not null and city is not null and postal_code is not null
	do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
	where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
		names,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		url
	])).rowCount, 'saskatchewan');} catch(e) {console.log(e); console.log('saskatchewan failed')}
}
async function newbrunswick(browser, page) {
	if(page === undefined) {
		page = await browser.newPage();
	}
	page = await mnmalism(page);
	await page.goto('https://www.cannabis-nb.com/stores/');
	let results = await page.$$eval('.visible-lg tbody > tr', e => e.map(x => Array.from(x.querySelectorAll('td')).map((y, i) => {
		if(i === 2) {
			return y.textContent.trim().split('\n').map(yy => {
				//does not work monday 8am to wednesday 2pm, for example
				let temp = yy.trim().replace(/-|,/g, ' ').split(/\s+/);
				let d = 0;
				switch(temp[0]) {
					case 'Tue': d = 1440; break;
					case 'Wed': d = 2880; break;
					case 'Thu': d = 4320; break;
					case 'Fri': d = 5760; break;
					case 'Sat': d = 7200; break;
					case 'Sun': d = 8640; break; 
				}
				let out = [];
				for(let j = 2; j < temp.length; j += 2) {
					let beg = temp[j - 1].split(':').map(Number);
					beg = beg[0] * 60 + beg[1];
					let end = temp[j].split(':').map(Number);
					end = end[0] * 60 + end[1];
					let rtn = [d + beg, d + end];
					if(end < beg) {
						if(d === 8640) {
							out.push([rtn[0], 10080]);
							rtn[0] = 0;
							rtn[1] -= 8640;
						} else {
							rtn[1] += 1440;
						}
					}
					out.push(rtn);
				}
				return out;
			});
		} else {
			return y.textContent.trim();
		}
	})));
	page.close();
	let idx = [];
	let address = [];
	for(let i = 0; i < results.length; ++i) {
		idx.push(i);
		address.push(results[i][1]);
	}
	await postgres.query(
		`create temporary table newbrunswick as 
		select *
		from unnest (
			$1::smallint[],
			$2::citext[]
		) as t(idx, address)`,
		[idx, address]
	);
	idx = (await postgres.query(`
		select idx
		from newbrunswick
		where not exists (
			select
			from store_temp
			where
				joined_name = '' and
				address = newbrunswick.address and
				address2 = '' and
				city = '' and
				region = 'newbrunswick' and
				country = 'canada' and
				postal_code = ''
		)
	`)).rows.map(x => x.idx);
	address = [];
	let address2 = [];
	let city = [];
	let region = [];
	let country = [];
	let postal_code = [];
	let longitude = [];
	let latitude = [];
	let osrm_hint_foot = [];
	let osrm_hint_car = [];
	let timezone = [];
	let open = [];
	for(let j = 0; j < idx.length; ++j) {
		const i = idx[j];
		if(results[i][1] === '16 Allee De La Cooperative, Richibucto, NB E4W 5V8, Canada') {
			address.push('16 Cooperative Street');
			address2.push('');
			city.push('Richibucto');
			country.push('Canada');
			postal_code.push('E4W3W7');
			longitude.push('-64.8737465');//https://www.openstreetmap.org/node/7062087062
			latitude.push('46.6731998');
			osrm_hint_foot.push(await request(`http://127.0.0.1:5000/nearest/v1/fuck/-64.8737465,46.6731998`).then(x => JSON.parse(x).waypoints[0].hint));
			osrm_hint_car.push(await request(`http://127.0.0.1:5001/nearest/v1/fuck/-64.8737465,46.6731998`).then(x => JSON.parse(x).waypoints[0].hint));
			timezone.push('America/Halifax');
		} else if(results[i][1] === '20 F. Tribe Road, Perth-Andover, NB E7H 3R6, Canada') {
			address.push('20 F. Tribe Road');
			address2.push('');
			city.push('Perth-Andover');
			country.push('Canada');
			postal_code.push('E7H3R6');
			longitude.push('-67.713059');//saw construction via street view
			latitude.push('46.745906');
			osrm_hint_foot.push(await request(`http://127.0.0.1:5000/nearest/v1/fuck/-67.713059,46.745906`).then(x => JSON.parse(x).waypoints[0].hint));
			osrm_hint_car.push(await request(`http://127.0.0.1:5001/nearest/v1/fuck/-67.713059,46.745906`).then(x => JSON.parse(x).waypoints[0].hint));
			timezone.push('America/Halifax');
		} else {
			let r;
			try {
				r = await g(results[i][1]);
			} catch(e) {
				console.error(e);
				console.log(results[i][1]);
				continue;
			}
			if(!pc.test(r[0].postal_code)) {
				console.error('postal_code goofup', r, results[i][1]);
				continue;
			}
			address.push(r[0].address);
			address2.push(r[0].address2);
			city.push(r[0].city);
			country.push(r[0].country);
			postal_code.push(r[0].postal_code);
			longitude.push(r[1].lng);
			latitude.push(r[1].lat);
			osrm_hint_foot.push(r[3]);
			osrm_hint_car.push(r[4]);
			timezone.push(r[2]);
		}
		region.push('New Brunswick');
		open.push(JSON.stringify(results[i][2].flat()));
	} try {
	console.log((await postgres.query(`insert into store (
		joined_name,
		name,
		url,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		open,
		partner,
		delivery,
		pickup,
		prepayment
	) select
		'Cannabis NB',
		'Cannabis NB',
		'cannabis-nb.com',
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		(select range_agg(int4range((x->>0)::int, (x->>1)::int), true, true) from json_array_elements(open) as t(x)),
		false,
		false,
		false,
		false
	from unnest (
		$1::citext[],
		$2::citext[],
		$3::citext[],
		$4::citext[],
		$5::citext[],
		$6::citext[],
		$7::decimal[],
		$8::decimal[],
		$9::text[],
		$10::text[],
		$11::text[],
		$12::json[]
	) as i(
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		open
	) where not exists (select 1 from store_history where
		store_history.joined_name = 'Cannabis NB' and
		store_history.address = i.address and
		store_history.address2 is not distinct from i.address2 and
		store_history.city = i.city and
		store_history.region = i.region and
		store_history.country = i.country and
		store_history.postal_code = i.postal_code
	) or exists (select 1 from store where
		store.joined_name = 'Cannabis NB' and
		store.address = i.address and
		store.address2 is not distinct from i.address2 and
		store.city = i.city and
		store.region = i.region and
		store.country = i.country and
		store.postal_code = i.postal_code
	) on conflict (joined_name, address, address2, city, region, country, postal_code)
	where address is not null and address2 is not null and city is not null and postal_code is not null
	do update set open = excluded.open, longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
	where store.open is distinct from excluded.open and store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
		address,//select rows where they haven't been previously deleted
		address2,//what is previously deleted?
		city,//present in history table and not present in current table
		region,//not deleted means not present in history table or present in current table
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		open
	])).rowCount, 'newbrunswick');} catch(e) {console.log(e); console.log('newbrunswick failed')}
}
async function britishcolumbia() {
	//todo: https://justice.gov.bc.ca/cannabislicensing/api/establishments/map contains longitude & latitude, so google maps api unnecessary if address2 can be parsed and short forms (ave, rd) are dealt with
	let bc = await request('https://justice.gov.bc.ca/cannabislicensing/api/establishments/map-json').then(JSON.parse);
	let idx = [];
	let names = [];
	let address = [];
	let city = [];
	let postal_code = [];
	for(let i = 0; i < bc.length; ++i) {
		if(bc[i].Status === 'Open') {
			idx.push(i);
			names.push(bc[i].Name = sanitize_name(bc[i].Name));
			address.push(bc[i].Address);
			city.push(bc[i].City);
			postal_code.push(bc[i].Postal);
		}
	}
	await postgres.query(
		`create temporary table britishcolumbia as 
		select *
		from unnest (
			$1::smallint[],
			$2::citext[],
			$3::citext[],
			$4::citext[],
			$5::citext[]
		) as t(idx, joined_name, address, city, postal_code)`,
		[idx, names, address, city, postal_code]
	);
	idx = (await postgres.query(`
		select idx
		from britishcolumbia
		where not exists (
			select
			from store_temp
			where
				joined_name = britishcolumbia.joined_name and
				address = britishcolumbia.address and
				address2 = '' and
				city = britishcolumbia.city and
				region = 'britishcolumbia' and
				country = 'canada' and
				postal_code = britishcolumbia.postal_code
		)
	`)).rows.map(x => x.idx);
	names = [];
	address = [];
	let address2 = [];
	city = [];
	let region = [];
	let country = [];
	postal_code = [];
	let longitude = [];
	let latitude = [];
	let osrm_hint_foot = [];
	let osrm_hint_car = [];
	let timezone = [];
	let phone = [];
	for(let j = 0; j < idx.length; ++j) {
		const i = idx[j];
		let r;
		try {
			r = await g(`${bc[i].Address} ${bc[i].City} british columbia ${bc[i].Postal} canada`);
		} catch(e) {
			console.error(e);
			console.log(bc[i]);
			continue;
		}
		if(!pc.test(r[0].postal_code)) {
			bc[i].Postal = bc[i].Postal.replace(/\s+/g, '');
			if(r[0].postal_code.length === 3 && bc[i].Postal.length === 6) {
				r[0].postal_code = bc[i].Postal
			} else {
				console.error('postal_code goofup', r, bc[i]);
				continue;
			}
		}
		names.push(bc[i].Name);
		address.push(r[0].address);
		address2.push(r[0].address2);
		city.push(r[0].city);
		region.push(r[0].region);
		country.push(r[0].country);
		postal_code.push(r[0].postal_code);
		longitude.push(r[1].lng);
		latitude.push(r[1].lat);
		osrm_hint_foot.push(r[3]);
		osrm_hint_car.push(r[4]);
		timezone.push(r[2]);
		phone.push(bc[i].Phone);
	} try{
	console.log((await postgres.query(`insert into store (
		joined_name,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone,
		partner,
		delivery,
		pickup,
		prepayment,
		name
	) select i.*, false, false, false, false, i.joined_name from unnest (
		$1::citext[],
		$2::citext[],
		$3::citext[],
		$4::citext[],
		$5::citext[],
		$6::citext[],
		$7::citext[],
		$8::decimal[],
		$9::decimal[],
		$10::text[],
		$11::text[],
		$12::text[],
		$13::packed_phone_number[]
	) as i(
		joined_name,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone
	) where not exists (select 1 from store_history where
		store_history.joined_name = i.joined_name and
		store_history.address = i.address and
		store_history.address2 is not distinct from i.address2 and
		store_history.city = i.city and
		store_history.region = i.region and
		store_history.country = i.country and
		store_history.postal_code = i.postal_code
	) or exists (select 1 from store where
		store.joined_name = i.joined_name and
		store.address = i.address and
		store.address2 is not distinct from i.address2 and
		store.city = i.city and
		store.region = i.region and
		store.country = i.country and
		store.postal_code = i.postal_code
	) on conflict (joined_name, address, address2, city, region, country, postal_code)
	where address is not null and address2 is not null and city is not null and postal_code is not null
	do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
	where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
		names,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone
	])).rowCount, 'bc');} catch(e) {console.log(e); console.log('bc failed')}
}
async function newfoundland() {
	let nl = (await request('https://stores.boldapps.net/front-end/get_surrounding_stores.php?shop=nlc-production.myshopify.com&latitude=50&longitude=-59&limit=0').then(JSON.parse)).stores;
	let idx = [];
	let names = [];
	let address = [];
	let address2 = [];
	let city = [];
	let postal_code = [];
	for(let i = 0; i < nl.length; ++i) {
		idx.push(i);
		names.push(nl[i].name = sanitize_name(nl[i].name));
		address.push(nl[i].address);
		address2.push(nl[i].address2);
		city.push(nl[i].city);
		postal_code.push(nl[i].postal_zip);
	}
	await postgres.query(
		`create temporary table newfoundland as 
		select *
		from unnest (
			$1::smallint[],
			$2::citext[],
			$3::citext[],
			$4::citext[],
			$5::citext[],
			$6::citext[]
		) as t(idx, joined_name, address, address2, city, postal_code)`,
		[idx, names, address, address2, city, postal_code]
	);
	idx = (await postgres.query(`
		select idx
		from newfoundland
		where not exists (
			select
			from store_temp
			where
				joined_name = newfoundland.joined_name and
				address = newfoundland.address and
				address2 = newfoundland.address2 and
				city = newfoundland.city and
				region = 'newfoundland' and
				country = 'canada' and
				postal_code = newfoundland.postal_code
		)
	`)).rows.map(x => x.idx);
	names = [];
	address = [];
	address2 = [];
	city = [];
	let region = [];
	let country = [];
	postal_code = [];
	let longitude = [];
	let latitude = [];
	let osrm_hint_foot = [];
	let osrm_hint_car = [];
	let timezone = [];
	let phone = [];
	let extension = [];
	for(let j = 0; j < idx.length; ++j) {
		const i = idx[j];
		let r;
		try {
			r = await g(`${nl[i].address} ${nl[i].address2} ${nl[i].city} newfoundland ${nl[i].postal_zip} canada`);
		} catch(e) {
			console.error(e);
			console.log(nl[i]);
			continue;
		}
		if(!pc.test(r[0].postal_code)) {
			r[0].postal_code = nl[i].postal_zip.replace(/\s+/, '');
			//https://www.zip-codes.com/canadian/postal-code.asp?postalcode=a0p+1c0 27 aspen road is closer to here
			//https://www.zip-codes.com/canadian/postal-code.asp?postalcode=a0p+1e0 than here
			//google geocode gives only a0p :(
			//https://www.google.com/maps/place/Corner+Brook,+NL+A2H+4C8 means provided postal code is correct (polygon contains tweed)
			//google geocode gives only a2h :(
			//google maps says tweed's official address is 62 Broadway, Corner Brook, NL A2H 6H4, but a2h6h4 totally wrong plaza
			//source says 62 Broadway Avenue Corner Brook, NL A2H 4C8, Canada. wrong address right postal code
		}
		names.push(nl[i].name);
		address.push(r[0].address);
		address2.push(r[0].address2);
		city.push(r[0].city);
		region.push(r[0].region);
		country.push(r[0].country);
		postal_code.push(r[0].postal_code);
		longitude.push(r[1].lng);
		latitude.push(r[1].lat);
		osrm_hint_foot.push(r[3]);
		osrm_hint_car.push(r[4]);
		timezone.push(r[2]);
		phone.push(nl[i].phone.trim().replace(/(?<!^)\(.+\)/g, '').trim());
		extension.push(nl[i].phone.match(/ext (.+)\)/)?.[1]?.trim());
	} try {
	console.log((await postgres.query(`insert into store (
		joined_name,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone,
		extension,
		partner,
		delivery,
		pickup,
		prepayment,
		name
	) select i.*, false, false, false, false, i.joined_name from unnest (
		$1::citext[],
		$2::citext[],
		$3::citext[],
		$4::citext[],
		$5::citext[],
		$6::citext[],
		$7::citext[],
		$8::decimal[],
		$9::decimal[],
		$10::text[],
		$11::text[],
		$12::text[],
		$13::packed_phone_number[],
		$14::citext[]
	) as i(
		joined_name,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone,
		extension
	) where not exists (select 1 from store_history where
		store_history.joined_name = i.joined_name and
		store_history.address = i.address and
		store_history.address2 is not distinct from i.address2 and
		store_history.city = i.city and
		store_history.region = i.region and
		store_history.country = i.country and
		store_history.postal_code = i.postal_code
	) or exists (select 1 from store where
		store.joined_name = i.joined_name and
		store.address = i.address and
		store.address2 is not distinct from i.address2 and
		store.city = i.city and
		store.region = i.region and
		store.country = i.country and
		store.postal_code = i.postal_code
	) on conflict (joined_name, address, address2, city, region, country, postal_code)
	where address is not null and address2 is not null and city is not null and postal_code is not null
	do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
	where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
		names,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone,
		extension
	])).rowCount, 'newfoundland'); } catch(e) {console.log(e); console.log('newfoundland failed')}
}
async function novascotia() {
	let x = await request('https://www.mynslc.com/skins/mynslc/scripts/locations.json').then(JSON.parse);
	let idx = [];
	let address = [];
	let address2 = [];
	let city = [];
	let postal_code = [];
	for(let i = 0; i < x.length; ++i) {
		for(let j = 0; j < x[i].features.length; ++j) {
			if(x[i].features[j].featureId === 'CP') {
				idx.push(i);
				address.push(x[i].addressInfo.address1);
				address2.push(x[i].addressInfo.address2 ?? '');
				city.push(x[i].addressInfo.city);
				postal_code.push(x[i].addressInfo.postal);
				break;
			}
		}
	}
	await postgres.query(
		`create temporary table novascotia as 
		select *
		from unnest (
			$1::smallint[],
			$2::citext[],
			$3::citext[],
			$4::citext[],
			$5::citext[]
		) as t(idx, address, address2, city, postal_code)`,
		[idx, address, address2, city, postal_code]
	);
	idx = (await postgres.query(`
		select idx
		from novascotia
		where not exists (
			select
			from store_temp
			where
				joined_name = '' and
				address = novascotia.address and
				address2 = novascotia.address2 and
				city = novascotia.city and
				region = 'novascotia' and
				country = 'canada' and
				postal_code = novascotia.postal_code
		)
	`)).rows.map(x => x.idx);
	address = [];
	address2 = [];
	city = [];
	let region = [];
	let country = [];
	postal_code = [];
	let longitude = [];
	let latitude = [];
	let osrm_hint_foot = [];
	let osrm_hint_car = [];
	let timezone = [];
	let open = [];
	let phone = [];
	for(let j = 0; j < idx.length; ++j) {
		const i = idx[j];
		let r;
		try {
			r = await g(`${x[i].addressInfo.address1} ${x[i].addressInfo.address2 ?? ''} ${x[i].addressInfo.city} nova scotia ${x[i].addressInfo.postal} canada`);
		} catch(e) {
			console.error(e);
			console.log(x[i].name);
			continue;
		}
		if(!pc.test(r[0].postal_code)) {
			x[i].postal = x[i].postal.replace(/\s+/g, '');
			if(r[0].postal_code.length === 3 && x[i].postal.length === 6) {
				r[0].postal_code = x[i].postal
			} else {
				console.error('postal_code goofup', r, x[i]);
				continue;
			}
		}
		address.push(r[0].address);
		address2.push(r[0].address2);
		city.push(r[0].city);
		region.push(r[0].region);
		country.push(r[0].country);
		postal_code.push(r[0].postal_code);
		longitude.push(r[1].lng);
		latitude.push(r[1].lat);
		osrm_hint_foot.push(r[3]);
		osrm_hint_car.push(r[4]);
		timezone.push(r[2]);
		phone.push(x[i].phone);
		let tim = [];
		for(let k = 0; k < x[i].openingHours.length; ++k) {
			let d = 0;
			switch(x[i].openingHours[k].dayOfWeek) {
				case 'Tuesday': d = 1440; break;
				case 'Wednesday': d = 2880; break;
				case 'Thursday': d = 4320; break;
				case 'Friday': d = 5760; break;
				case 'Saturday': d = 7200; break;
				case 'Sunday': d = 8640; break; 
			}
			let beg = x[i].openingHours[k].opens.split(':').map(Number);
			beg = beg[0] * 60 + beg[1];
			let end = x[i].openingHours[k].closes.split(':').map(Number);
			end = end[0] * 60 + end[1];
			let rtn = [d + beg, d + end];
			if(end < beg) {
				if(d === 8640) {
					tim.push([rtn[0], 10080]);
					rtn[0] = 0;
					rtn[1] -= 8640;
				} else {
					rtn[1] += 1440;
				}
			}
			tim.push(rtn);
		}
		open.push(JSON.stringify(tim));
	} try {
	console.log((await postgres.query(`insert into store (
		joined_name,
		name,
		url,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		open,
		phone,
		partner,
		delivery,
		pickup,
		prepayment
	) select
		'NSLC',
		'NSLC',
		'cannabis.mynslc.com',
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		(select range_agg(int4range((x->>0)::int, (x->>1)::int), true, true) from json_array_elements(open) as t(x)),
		phone,
		false,
		false,
		false,
		false
	from unnest (
		$1::citext[],
		$2::citext[],
		$3::citext[],
		$4::citext[],
		$5::citext[],
		$6::citext[],
		$7::decimal[],
		$8::decimal[],
		$9::text[],
		$10::text[],
		$11::text[],
		$12::json[],
		$13::packed_phone_number[]
	) as i(
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		open,
		phone
	) where not exists (select 1 from store_history where
		store_history.joined_name = 'NSLC' and
		store_history.address = i.address and
		store_history.address2 is not distinct from i.address2 and
		store_history.city = i.city and
		store_history.region = i.region and
		store_history.country = i.country and
		store_history.postal_code = i.postal_code
	) or exists (select 1 from store where
		store.joined_name = 'NSLC' and
		store.address = i.address and
		store.address2 is not distinct from i.address2 and
		store.city = i.city and
		store.region = i.region and
		store.country = i.country and
		store.postal_code = i.postal_code
	) on conflict (joined_name, address, address2, city, region, country, postal_code)
	where address is not null and address2 is not null and city is not null and postal_code is not null
	do update set open = excluded.open, longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
	where store.open is distinct from excluded.open and store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		open,
		phone
	])).rowCount, 'nova scotia');} catch(e) {console.log(e); console.log('novascotia failed')}
}
async function pei(browser, page) {
	if(page === undefined) {
		page = await browser.newPage();
	}
	page = await mnmalism(page);
	await page.goto('https://peicannabiscorp.com/pages/contact');
	let results = await page.$$eval('.location', e => e.map(x => x.innerText.trim().split('\n')));
	page.close();
	let idx = [];
	let address = [];
	const dayOfWeek = /^(Mon|Tue|Wed|Thurs|Fri|Sat|Sun)/;
	for(let i = 0; i < results.length; ++i) {
		let adr = '';
		let phone;
		let first = false;
		let second = true;
		let tim = [];
		for(let j = 0; j < results[i].length; ++j) {
			if(results[i][j]) {
				if(first) {
					if(second && results[i][j].startsWith('Phone:')) {
						phone = results[i][j].replace('Phone:', '');
						second = false;
					} else if(!second && dayOfWeek.test(results[i][j])) {
						let idx = results[i][j].indexOf(":");
						let days = results[i][j].substring(0, idx).split('-');
						let hours = results[i][j].substring(idx + 1).split('-');
						let beg = hours[0].trim().match(apm);
						//12am -> 0
						//1am -> 1
						//11am -> 11
						//12pm -> 12
						//1pm -> 13
						//11pm -> 23
						beg = (Number(beg[1]) % 12 + (beg[4]?.toLowerCase() === 'p' ? 12 : 0)) * 60 + Number(beg[2]);
						let end = hours[1].trim().match(apm);
						end = (Number(end[1]) % 12 + (end[4]?.toLowerCase() === 'p' ? 12 : 0)) * 60 + Number(end[2]);
						switch(days[0]) {
							case 'Mon': days[0] = 0; break;
							case 'Tue': days[0] = 1; break;
							case 'Wed': days[0] = 2; break;
							case 'Thurs': days[0] = 3; break;
							case 'Fri': days[0] = 4; break;
							case 'Sat': days[0] = 5; break;
							case 'Sun': days[0] = 6; break;
						}
						if(days[1]) {
							switch(days[1]) {
								case 'Mon': days[1] = 0; break;
								case 'Tue': days[1] = 1; break;
								case 'Wed': days[1] = 2; break;
								case 'Thurs': days[1] = 3; break;
								case 'Fri': days[1] = 4; break;
								case 'Sat': days[1] = 5; break;
								case 'Sun': days[1] = 6; break;
							}
							//sat to tue
							//5 to 1
							//5,6,0,1
							//5 to 8
							//5,6,7,8
							if(days[1] < days[0]) {
								days[1] += 7;
							}
							for(let k = days[0]; k <= days[1]; ++k) {
								let d = k % 7 * 1440;
								let rtn = [d + beg, d + end];
								if(end < beg) {
									if(d === 8640) {
										tim.push([rtn[0], 10080]);
										rtn[0] = 0;
										rtn[1] -= 8640;
									} else {
										rtn[1] += 1440;
									}
								}
								tim.push(rtn);
							}
						} else {
							let d = days[0] * 1440;
							let rtn = [d + beg, d + end];
							if(end < beg) {
								if(d === 8640) {
									tim.push([rtn[0], 10080]);
									rtn[0] = 0;
									rtn[1] -= 8640;
								} else {
									rtn[1] += 1440;
								}
							}
							tim.push(rtn);
						}
					} else if(results[i][j] === 'Store Hours') {
						second = false;
					} else if(second) {
						adr += results[i][j];
					}
				} else {
					first = true;
				}
			}
		}
		results[i] = {adr, tim, phone};
		idx.push(i);
		address.push(adr);
	}
	await postgres.query(
		`create temporary table pei as
		select *
		from unnest (
			$1::smallint[],
			$2::citext[]
		) as t(idx, address)`,
		[idx, address]
	);
	idx = (await postgres.query(`
		select idx
		from pei
		where not exists (
			select
			from store_temp
			where
				joined_name = '' and
				address = pei.address and
				address2 = '' and
				city = '' and
				region = 'pei' and
				country = 'canada' and
				postal_code = ''
		)
	`)).rows.map(x => x.idx);
	address = [];
	let address2 = [];
	let city = [];
	let region = [];
	let country = [];
	let postal_code = [];
	let longitude = [];
	let latitude = [];
	let osrm_hint_foot = [];
	let osrm_hint_car = [];
	let timezone = [];
	let open = [];
	let phone = [];
	for(let j = 0; j < idx.length; ++j) {
		const i = idx[j];
		try {
			r = await g(`${results[i].adr} canada`);
		} catch(e) {
			console.error(e);
			console.log(results[i].adr);
			continue;
		}
		if(!pc.test(r[0].postal_code)) {
			console.error('postal_code goofup', r, results[i].adr);
			continue;
		}
		address.push(r[0].address);
		address2.push(r[0].address2);
		city.push(r[0].city);
		region.push(r[0].region);
		country.push(r[0].country);
		postal_code.push(r[0].postal_code);
		longitude.push(r[1].lng);
		latitude.push(r[1].lat);
		osrm_hint_foot.push(r[3]);
		osrm_hint_car.push(r[4]);
		timezone.push(r[2]);
		open.push(JSON.stringify(results[i].tim));
		phone.push(results[i].phone);
	} try {
	console.log((await postgres.query(`insert into store (
		joined_name,
		name,
		url,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		open,
		phone,
		partner,
		delivery,
		pickup,
		prepayment
	) select
		'PEI',
		'PEI',
		'peicannabiscorp.com',
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		(select range_agg(int4range((x->>0)::int, (x->>1)::int), true, true) from json_array_elements(open) as t(x)),
		phone,
		false,
		false,
		false,
		false
	from unnest (
		$1::citext[],
		$2::citext[],
		$3::citext[],
		$4::citext[],
		$5::citext[],
		$6::citext[],
		$7::decimal[],
		$8::decimal[],
		$9::text[],
		$10::text[],
		$11::text[],
		$12::json[],
		$13::packed_phone_number[]
	) as i(
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		open,
		phone
	) where not exists (select 1 from store_history where
		store_history.joined_name = 'PEI' and
		store_history.address = i.address and
		store_history.address2 is not distinct from i.address2 and
		store_history.city = i.city and
		store_history.region = i.region and
		store_history.country = i.country and
		store_history.postal_code = i.postal_code
	) or exists (select 1 from store where
		store.joined_name = 'PEI' and
		store.address = i.address and
		store.address2 is not distinct from i.address2 and
		store.city = i.city and
		store.region = i.region and
		store.country = i.country and
		store.postal_code = i.postal_code
	) on conflict (joined_name, address, address2, city, region, country, postal_code)
	where address is not null and address2 is not null and city is not null and postal_code is not null
	do update set open = excluded.open, longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
	where store.open is distinct from excluded.open and store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		open,
		phone
	])).rowCount, 'pei');} catch(e) {console.log(e); console.log('pei failed')}
}
async function yukon(browser, page) {
	if(page === undefined) {
		page = await browser.newPage();
	}
	page = await mnmalism(page);
	await page.goto('https://cannabisyukon.org/store-locations');
	let idx = [];
	let n = (await page.$$eval('.component-store-location__title', x => x.map(y => y.textContent.trim()))).map(sanitize_name);
	let address = [];
	let results = await page.$$eval('.component-store-location__address', x => x.map(y => Array.from(y.querySelectorAll('p')).map(z => z.textContent.trim())));
	page.close();
	for(let i = 0; i < results.length; ++i) {
		idx.push(i);
		address.push(results[i][0]);
	}
	await postgres.query(
		`create temporary table yukon as 
		select *
		from unnest (
			$1::smallint[],
			$2::citext[],
			$3::citext[]
		) as t(idx, joined_name, address)`,
		[idx, n, address]
	);
	idx = (await postgres.query(`
		select idx
		from yukon
		where not exists (
			select
			from store_temp
			where
				joined_name = yukon.joined_name and
				address = yukon.address and
				address2 = '' and
				city = '' and
				region = 'yukon' and
				country = 'canada' and
				postal_code = ''
		)
	`)).rows.map(x => x.idx);
	let names = [];
	address = [];
	let address2 = [];
	let city = [];
	let region = [];
	let country = [];
	let postal_code = [];
	let longitude = [];
	let latitude = [];
	let osrm_hint_foot = [];
	let osrm_hint_car = [];
	let timezone = [];
	let phone = [];
	let url = [];
	for(let j = 0; j < idx.length; ++j) {
		const i = idx[j];
		let r;
		try {
			r = await g(`${results[i][0].replace('\n', ',')},canada`);
		} catch(e) {
			console.error(e);
			console.log(n[i], results[i]);
			continue;
		}
		if(!pc.test(r[0].postal_code)) {
			console.error('postal_code goofup', r, n[i], results[i]);
			continue;
		}
		names.push(n[i]);
		address.push(r[0].address);
		address2.push(r[0].address2);
		city.push(r[0].city);
		region.push(r[0].region);
		country.push(r[0].country);
		postal_code.push(r[0].postal_code);
		longitude.push(r[1].lng);
		latitude.push(r[1].lat);
		osrm_hint_foot.push(r[3]);
		osrm_hint_car.push(r[4]);
		timezone.push(r[2]);
		let p;
		let u;
		for(let k = 1; k < results[i].length; ++k) {
			if(results[i][k].startsWith('http')) {
				u = results[i][k];
			} else if(results[i][k].replace(/\D/g, '').length === 10) {
				p = results[i][k];
			}
			if(u && p) {
				break;
			}
		}
		phone.push(p);
		url.push(u);
	} try {
	console.log((await postgres.query(`insert into store (
		joined_name,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone,
		url,
		partner,
		delivery,
		pickup,
		prepayment,
		name
	) select i.*, false, false, false, false, i.joined_name from unnest (
		$1::citext[],
		$2::citext[],
		$3::citext[],
		$4::citext[],
		$5::citext[],
		$6::citext[],
		$7::citext[],
		$8::decimal[],
		$9::decimal[],
		$10::text[],
		$11::text[],
		$12::text[],
		$13::packed_phone_number[],
		$14::text[]
	) as i(
		joined_name,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone,
		url
	) where not exists (select 1 from store_history where
		store_history.joined_name = i.joined_name and
		store_history.address = i.address and
		store_history.address2 is not distinct from i.address2 and
		store_history.city = i.city and
		store_history.region = i.region and
		store_history.country = i.country and
		store_history.postal_code = i.postal_code
	) or exists (select 1 from store where
		store.joined_name = i.joined_name and
		store.address = i.address and
		store.address2 is not distinct from i.address2 and
		store.city = i.city and
		store.region = i.region and
		store.country = i.country and
		store.postal_code = i.postal_code
	) on conflict (joined_name, address, address2, city, region, country, postal_code)
	where address is not null and address2 is not null and city is not null and postal_code is not null
	do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
	where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
		names,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone,
		url
	])).rowCount, 'yukon');} catch(e) {console.log(e); console.log('yukon failed')}
}
async function nwt(browser, page) {
	if(page === undefined) {
		page = await browser.newPage();
	}
	page = await mnmalism(page);
	await page.goto('https://www.ntlcc.ca/en/where-buy-cannabis');
	let results = await page.$$eval('#content h3', x => {
		let out = [];
		for(let i = 0; i < x.length; ++i) {
			let stuff = x[i].textContent.trim();
			if(stuff === 'Norman Wells') {
				stuff = 'Norman Wells Liquor Agency';
			}
			let store = [stuff];
			x[i] = x[i].nextElementSibling;
			if(x[i]?.tagName === 'P') {
				store = store.concat(x[i].textContent.split('\n').map(y => y.trim()));
			}
			if(stuff === 'Norman Wells Liquor Agency' && store[1] === 'Franklin Ave') {
				store[1] = '15 Franklin Ave';
			}
			out.push(store);
		}
		return out;
	});
	page.close();
	let idx = [];
	let names = [];
	let address = [];
	for(let i = 0; i < results.length; ++i) {
		idx.push(i);
		names.push(results[i][0] = sanitize_name(results[i][0]));
		address.push(results[i][1]);
	}
	await postgres.query(
		`create temporary table nwt as 
		select *
		from unnest (
			$1::smallint[],
			$2::citext[],
			$3::citext[]
		) as t(idx, joined_name, address)`,
		[idx, names, address]
	);
	idx = (await postgres.query(`
		select idx
		from nwt
		where not exists (
			select
			from store_temp
			where
				joined_name = nwt.joined_name and
				address = nwt.address and
				address2 = '' and
				city = '' and
				region = 'nwt' and
				country = 'canada' and
				postal_code = ''
		)
	`)).rows.map(x => x.idx);
	names = [];
	address = [];
	let address2 = [];
	let city = [];
	let region = [];
	let country = [];
	let postal_code = [];
	let longitude = [];
	let latitude = [];
	let osrm_hint_foot = [];
	let osrm_hint_car = [];
	let timezone = [];
	let phone = [];
	for(let j = 0; j < idx.length; ++j) {
		const i = idx[j];
		let r;
		let adr;
		let fun;
		if(results[i][0] === 'Hay River Liquor') {
			adr = '76 Capital Dr, Hay River, X0E 1G2';
			fun = '101';
		} else {
			adr = `${results[i][0]}, ${results[i][1]}`
		}
		try {
			r = await g(`${adr}, northwest territories, canada`);
		} catch(e) {
			console.error(e);
			console.log(results[i]);
			continue;
		}
		if(!pc.test(r[0].postal_code)) {
			if(r[0].address === '10021 100 Street') {
				r[0].postal_code = 'X0E0N0';
			} else {
				console.error('postal_code goofup', r, results[i]);
				continue;
			}
		}
		names.push(results[i][0]);
		address.push(r[0].address);
		address2.push(fun ?? r[0].address2);
		city.push(r[0].city);
		region.push(r[0].region);
		country.push(r[0].country);
		postal_code.push(r[0].postal_code);
		longitude.push(r[1].lng);
		latitude.push(r[1].lat);
		osrm_hint_foot.push(r[3]);
		osrm_hint_car.push(r[4]);
		timezone.push(r[2]);
		phone.push(results[i][2]);
	} try{
	console.log((await postgres.query(`insert into store (
		joined_name,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone,
		partner,
		delivery,
		pickup,
		prepayment,
		name
	) select i.*, false, false, false, false, i.joined_name from unnest (
		$1::citext[],
		$2::citext[],
		$3::citext[],
		$4::citext[],
		$5::citext[],
		$6::citext[],
		$7::citext[],
		$8::decimal[],
		$9::decimal[],
		$10::text[],
		$11::text[],
		$12::text[],
		$13::packed_phone_number[]
	) as i(
		joined_name,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone
	) where not exists (select 1 from store_history where
		store_history.joined_name = i.joined_name and
		store_history.address = i.address and
		store_history.address2 is not distinct from i.address2 and
		store_history.city = i.city and
		store_history.region = i.region and
		store_history.country = i.country and
		store_history.postal_code = i.postal_code
	) or exists (select 1 from store where
		store.joined_name = i.joined_name and
		store.address = i.address and
		store.address2 is not distinct from i.address2 and
		store.city = i.city and
		store.region = i.region and
		store.country = i.country and
		store.postal_code = i.postal_code
	) on conflict (joined_name, address, address2, city, region, country, postal_code)
	where address is not null and address2 is not null and city is not null and postal_code is not null
	do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
	where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
		names,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		phone
	])).rowCount, 'nwt');} catch(e) {console.log(e); console.log('nwt failed')}
}
async function ontario() {
	let on = parse(await request('https://www.agco.ca/sites/default/files/opendata/AGCOWebSiteCannabisMapData.csv'), {skip_empty_lines: true, from_line: 2});
	let idx = [];
	let names = [];
	let address = [];
	for(let i = 0; i < on.length; ++i) {
		if(on[i][3] === 'Authorized to Open') {
			idx.push(i);
			names.push(on[i][6] = sanitize_name(on[i][6])
				.replace(new RegExp('(?<!^)' + (on[i][7] + ' (east|west|north|south)').split(/\s+/).map((n, idx) => {
					n = n.toLowerCase().replace(/\./g, '');
					if(n === 'st') {
						n = 'st(reet)?';
					} else if(n === 'rd') {
						n = 'r(oa)?d';
					} else if(n === 'ave') {
						n = 'ave(nue)?';
					} else if(n === 'dr') {
						n = 'dr(ive)?';
					}
					return `(\\b${n}\\b)${idx !== 1 ? '?' : ''}`;
				}).join('\\s?'), 'i'), '')
				.replace(new RegExp('(?<!^)\\b' + on[i][8] + '\\b', 'gi'), '')
			);
			address.push(on[i][11]);
		}
	}
	await postgres.query(
		`create temporary table ontario as 
		select *
		from unnest (
			$1::smallint[],
			$2::citext[],
			$3::citext[]
		) as t(idx, joined_name, address)`,
		[idx, names, address]
	);
	idx = (await postgres.query(`
		select idx
		from ontario
		where not exists (
			select
			from store_temp
			where
				joined_name = ontario.joined_name and
				address = ontario.address and
				address2 = '' and
				city = '' and
				region = 'ontario' and
				country = 'canada' and
				postal_code = ''
		)
	`)).rows.map(x => x.idx);
	names = [];
	address = [];
	let address2 = [];
	let city = [];
	let region = [];
	let country = [];
	let postal_code = [];
	let longitude = [];
	let latitude = [];
	let osrm_hint_foot = [];
	let osrm_hint_car = [];
	let timezone = [];
	for(let j = 0; j < idx.length; ++j) {
		const i = idx[j];
		let r;
		try {
			r = await g(on[i][11].replace(/OSPREY MIIKAN RD/i, 'OSPREY MIIKAN'));
		} catch(e) {
			console.error(e);
			console.log(on[i]);
			continue;
		}
		if(!pc.test(r[0].postal_code)) {
			on[i][10] = on[i][10].replace(/\s+/g, '');
			if(r[0].postal_code.length === 3 && on[i][10].length === 6) {
				r[0].postal_code = on[i][10];
			} else {
				console.error('postal_code goofup', r, on[i]);
				continue;
			}
		}
		names.push(on[i][6]);
		address.push(r[0].address);
		address2.push(r[0].address2);
		city.push(r[0].city);
		region.push(r[0].region);
		country.push(r[0].country);
		postal_code.push(r[0].postal_code);
		longitude.push(r[1].lng);
		latitude.push(r[1].lat);
		osrm_hint_foot.push(r[3]);
		osrm_hint_car.push(r[4]);
		timezone.push(r[2]);
	} try {
	console.log((await postgres.query(`insert into store (
		joined_name,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		partner,
		delivery,
		pickup,
		prepayment,
		name
	) select i.*, false, false, false, false, i.joined_name from unnest (
		$1::citext[],
		$2::citext[],
		$3::citext[],
		$4::citext[],
		$5::citext[],
		$6::citext[],
		$7::citext[],
		$8::decimal[],
		$9::decimal[],
		$10::text[],
		$11::text[],
		$12::text[]
	) as i(
		joined_name,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone
	) where not exists (select 1 from store_history where
		store_history.joined_name = i.joined_name and
		store_history.address = i.address and
		store_history.address2 is not distinct from i.address2 and
		store_history.city = i.city and
		store_history.region = i.region and
		store_history.country = i.country and
		store_history.postal_code = i.postal_code
	) or exists (select 1 from store where
		store.joined_name = i.joined_name and
		store.address = i.address and
		store.address2 is not distinct from i.address2 and
		store.city = i.city and
		store.region = i.region and
		store.country = i.country and
		store.postal_code = i.postal_code
	) on conflict (joined_name, address, address2, city, region, country, postal_code)
	where address is not null and address2 is not null and city is not null and postal_code is not null
	do update set longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
	where store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
		names,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone
	])).rowCount, 'ontario'); } catch(e) {console.log(e); console.log('ontario failed')}
}
async function quebec() {
	let results = (await request({method: 'post', url: 'https://www.sqdc.ca/api/storelocator/stores', headers: {
		'X-Requested-With': 'XMLHttpRequest',
		'Accept-Language': 'en-CA',
		'Content-Type': 'application/json'
	}, body: '{"page":1,"pageSize":900}'}).then(JSON.parse)).Stores;
	let idx = [];
	let address = [];
	let address2 = [];
	let city = [];
	let postal_code = [];
	for(let i = 0; i < results.length; ++i) {
		idx.push(i);
		address.push(results[i].Address.Line1);
		address2.push(results[i].Address.Line2 ?? '');
		city.push(results[i].Address.City);
		postal_code.push(results[i].Address.PostalCode);
	}
	await postgres.query(
		`create temporary table quebec as 
		select *
		from unnest (
			$1::smallint[],
			$2::citext[],
			$3::citext[],
			$4::citext[],
			$5::citext[]
		) as t(idx, address, address2, city, postal_code)`,
		[idx, address, address2, city, postal_code]
	);
	idx = (await postgres.query(`
		select idx
		from quebec
		where not exists (
			select
			from store_temp
			where
				joined_name = '' and
				address = quebec.address and
				address2 = quebec.address2 and
				city = quebec.city and
				region = 'quebec' and
				country = 'canada' and
				postal_code = quebec.postal_code
		)
	`)).rows.map(x => x.idx);
	address = [];
	address2 = [];
	city = [];
	//let region = [];
	let country = [];
	postal_code = [];
	let longitude = [];
	let latitude = [];
	let osrm_hint_foot = [];
	let osrm_hint_car = [];
	let timezone = [];
	let phone = [];
	let open = [];
	for(let j = 0; j < idx.length; ++j) {
		const i = idx[j];
		try {
			r = await g(`${results[i].Address.Line1}${results[i].Address.Line2 ? ',' + results[i].Address.Line2 : ''}, ${results[i].Address.City}, ${results[i].Address.RegionName}, ${results[i].Address.PostalCode}, canada`);
		} catch(e) {
			console.error(e);
			console.log(results[i]);
			continue;
		}
		if(!pc.test(r[0].postal_code)) {
			results[i].Address.PostalCode = results[i].Address.PostalCode.replace(/\s+/g, '');
			if(r[0].postal_code.length === 3 && results[i].Address.PostalCode.length === 6) {
				r[0].postal_code = results[i].Address.PostalCode
			} else {
				console.error('postal_code goofup', r, results[i]);
				continue;
			}
		}
		address.push(r[0].address);
		address2.push(results[i].Address.Line1 === '110-1, rue St-Germain Ouest' ? 1 : r[0].address2);
		//google maps is clearly wrong: 1 Rue Saint Germain O #110, Rimouski, Quebec G5L 4B5. it's 100-1 in the picture
		city.push(r[0].city);
		//region.push(r[0].region);
		country.push(r[0].country);
		postal_code.push(r[0].postal_code);
		longitude.push(r[1].lng);
		latitude.push(r[1].lat);
		osrm_hint_foot.push(r[3]);
		osrm_hint_car.push(r[4]);
		timezone.push(r[2]);
		phone.push(results[i].PhoneNumber);
		//todo: wait for holiday and see what OpeningHourExceptions is
		let tim = [];
		for(let j = 0; j < results[i].Schedule.OpeningHours.length; ++j) {
			//todo: does IsOpenedAllDay: true imply OpeningTimes empty array? null?
			let d = 0;
			switch(results[i].Schedule.OpeningHours[j].LocalizedDay) {
				case 'Tuesday': d = 1440; break;
				case 'Wednesday': d = 2880; break;
				case 'Thursday': d = 4320; break;
				case 'Friday': d = 5760; break;
				case 'Saturday': d = 7200; break;
				case 'Sunday': d = 8640; break; 
			}
			for(let k = 0; k < results[i].Schedule.OpeningHours[j].OpeningTimes.length; ++k) {
				let beg = results[i].Schedule.OpeningHours[j].OpeningTimes[k].BeginTime.match(apm);
				beg = (Number(beg[1]) % 12 + (beg[4]?.toLowerCase() === 'p' ? 12 : 0)) * 60 + Number(beg[2]);
				let end = results[i].Schedule.OpeningHours[j].OpeningTimes[k].EndTime.match(apm);
				end = (Number(end[1]) % 12 + (end[4]?.toLowerCase() === 'p' ? 12 : 0)) * 60 + Number(end[2]);
				let rtn = [d + beg, d + end];
				if(end < beg) {
					if(d === 8640) {
						tim.push([rtn[0], 10080]);
						rtn[0] = 0;
						rtn[1] -= 8640;
					} else {
						rtn[1] += 1440;
					}
				}
				tim.push(rtn);
			}
		}
		open.push(JSON.stringify(tim));
	} try {
	console.log((await postgres.query(`insert into store (
		joined_name,
		name,
		url,
		address,
		address2,
		city,
		region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		open,
		phone,
		partner,
		delivery,
		pickup,
		prepayment
	) select
		'SQDC',
		'SQDC',
		'sqdc.ca',
		address,
		address2,
		city,
		'Quebec',
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		(select range_agg(int4range((x->>0)::int, (x->>1)::int), true, true) from json_array_elements(open) as t(x)),
		phone,
		false,
		false,
		false,
		false
	from unnest (
		$1::citext[],
		$2::citext[],
		$3::citext[],
		$4::citext[],
		$5::citext[],
		$6::decimal[],
		$7::decimal[],
		$8::text[],
		$9::text[],
		$10::text[],
		$11::json[],
		$12::packed_phone_number[]
	) as i(
		address,
		address2,
		city,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		open,
		phone
	) where not exists (select 1 from store_history where
		store_history.joined_name = 'SQDC' and
		store_history.address = i.address and
		store_history.address2 is not distinct from i.address2 and
		store_history.city = i.city and
		store_history.region = 'Quebec' and
		store_history.country = i.country and
		store_history.postal_code = i.postal_code
	) or exists (select 1 from store where
		store.joined_name = 'SQDC' and
		store.address = i.address and
		store.address2 is not distinct from i.address2 and
		store.city = i.city and
		store.region = 'Quebec' and
		store.country = i.country and
		store.postal_code = i.postal_code
	) on conflict (joined_name, address, address2, city, region, country, postal_code)
	where address is not null and address2 is not null and city is not null and postal_code is not null
	do update set open = excluded.open, longitude = excluded.longitude, latitude = excluded.latitude, timezone = excluded.timezone
	where store.open is distinct from excluded.open and store.longitude != excluded.longitude and store.latitude != excluded.latitude and store.timezone != excluded.timezone`, [
		address,
		address2,
		city,
		//region,
		country,
		postal_code,
		longitude,
		latitude,
		osrm_hint_foot,
		osrm_hint_car,
		timezone,
		open,
		phone
	])).rowCount, 'quebec');} catch(e) {console.log(e); console.log('quebec failed')}
}
(async function() {
	const [browser,] = await Promise.all([
		puppeteer.launch({
			args: ["--no-sandbox"],
			headless: true
		}),
		postgres.connect().then(() => postgres.query('begin'))
	]);
	await Promise.all([
		alberta(browser, (await browser.pages())[0]),
		britishcolumbia(),
		manitoba(browser),
		saskatchewan(browser),
		novascotia(),
		newbrunswick(browser),
		newfoundland(),
		pei(browser),
		quebec(),
		yukon(browser),
		nwt(browser),
		ontario()
	]);
	await postgres.query(`
		create temporary table store_temp_temp as
		select
			joined_name,
			address,
			'' as address2,
			city,
			'alberta' as region,
			'canada' as country,
			postal_code
		from alberta
		union all
		select
			joined_name,
			address,
			'',
			city,
			'manitoba',
			'canada',
			''
		from manitoba
		union all
		select
			joined_name,
			address,
			'',
			city,
			'saskatchewan',
			'canada',
			''
		from saskatchewan
		union all
		select
			'',
			address,
			'',
			'',
			'newbrunswick',
			'canada',
			''
		from newbrunswick
		union all
		select
			joined_name,
			address,
			'',
			city,
			'britishcolumbia',
			'canada',
			postal_code
		from britishcolumbia
		union all
		select
			joined_name,
			address,
			address2,
			city,
			'newfoundland',
			'canada',
			postal_code
		from newfoundland
		union all
		select
			'',
			address,
			address2,
			city,
			'novascotia',
			'canada',
			postal_code
		from novascotia
		union all
		select
			'',
			address,
			'',
			'',
			'pei',
			'canada',
			''
		from pei
		union all
		select
			joined_name,
			address,
			'',
			'',
			'yukon',
			'canada',
			''
		from yukon
		union all
		select
			joined_name,
			address,
			'',
			'',
			'nwt',
			'canada',
			''
		from nwt
		union all
		select
			joined_name,
			address,
			'',
			'',
			'ontario',
			'canada',
			''
		from ontario
		union all
		select
			'',
			address,
			address2,
			city,
			'quebec',
			'canada',
			postal_code
		from quebec
	`);
	let removeQ = (await postgres.query(`
		select *
		from store_temp
		where not exists (
			select
			from store_temp_temp
			where
				joined_name = store_temp.joined_name and
				address = store_temp.address and
				address2 = store_temp.address2 and
				city = store_temp.city and
				region = store_temp.region and
				country = store_temp.country and
				postal_code = store_temp.postal_code
		)
	`)).rows;
	if(removeQ.length) {
		console.log('human, should these be removed?', removeQ);
	}
	await postgres.query('truncate store_temp');
	await postgres.query('insert into store_temp select * from store_temp_temp');
	await Promise.all([
		browser.close(),
		postgres.query('commit').then(() => postgres.end())
	]);
})();