Promise = require("bluebird");
Promise.config({longStackTraces: true});
require('dotenv').config({path: require('path').resolve(__dirname, '../.env')});
const Big = require('big.js');
const util = require('util');
util.inspect.defaultOptions.maxArrayLength = null;
util.inspect.defaultOptions.showHidden = true;
util.inspect.defaultOptions.depth = Infinity;
//merge regexs
const inBrackets = /\s*\(.*\)\s*/;
const specialqty = /(?:(\d+)\s*[Xx]\s*)?(\d*\.?\d+)\s*(?:[A-WYZa-wyz]{1,2})(?:\b|$)/;
const pg = require('pg');
const postgres = new pg.Client();
const googleMapsClient = new (require("@googlemaps/google-maps-services-js").Client)({});
const gay = /^Friendly Stranger/i;
const gay2 = /^stash\s+(and|&)\s+co/i;
const gay8 = /^fire\s+(and|&)\s+flower/i;
const gay3 = /^tokyo\s+smoke/i;
const gay4 = /^spiritleaf/i;
const gay5 = /^CANNACO/i;
const gay6 = /^one\s+plant/i;
const gay7 = /^sessions/i;
module.exports = {
	sanitize_name: function (n) {
		if(n === 'Cannabis Boutique Inc.') {
			n = 'Cannabis Boutique';
		} else if(n === 'MARY JANE RIGS & CANNABIS') {
			n = 'Mary Jane Rigs & Cannabis';
		} else if(n === 'HELLO CANNABIS SSM') {
			n = 'HELLO CANNABIS'
		} else if(gay2.test(n)) {
			n = 'Stash & Co.';
		} else if(gay3.test(n)) {
			n = 'Tokyo Smoke';
		} else if(gay4.test(n)) {
			n = 'SPIRITLEAF';
		} else if(gay5.test(n)) {
			n = 'CANNACO';
		} else if(gay6.test(n)) {
			n = 'One Plant';
		} else if(gay7.test(n)) {
			n = 'Sessions';
		} else if(gay8.test(n)) {
			n = 'Fire & Flower';
		} else if(n.toLowerCase().trim() === 'rainbow thunder bay cannabis accessories') {
			n = 'Rainbow';
		} else if(n !== 'Made In Cannabis' && n !== 'Mary Jane on Penny Lane' && n !== 'This Is Cannabis' && n !== 'BURNSIDE BUDS.CA' && n !== 'The Cannabis Guys') {
			n = n.replace(/\./g, '');
			let first = n.split(/\s+/);
			//if(first.length > 2) {
				[first, ...n] = first;
				n = n.join(' ');
				//todo: replace if not prefixed by "of", "the", etc. what are those words called?
				n = n.split('-')[0].split(/\bat\b/i)[0].split(/\bon\b/i)[0].replace(/\b(sale(s?)|shop|retail|boutique|market|premium|cannabis|store(s?)|corp(orat(ed|ion))?|inc(orporat(ed|ion))?|co(mpany)?|supply|retailer(s?)|limited|ltd|calgary|bridgeland|downtown|kensington|barrhaven|recreational|dispensary)\b|\(.+\)/gi,'').trim();
				n = (first + ' ' + n).trim().replace(/\s+/g, ' ');
				if(n === '420') {
					//console.log('dumbass');
					n = 'FOUR20';
				} else if(n === 'HOBO') {
					n = 'Hobo';
				} else if(n === 'NUMO') {
					n = 'Numo';
				} else if(gay.test(n)) {
					n = "Friendly Stranger";
				}
			//}
		}
		if(n.toUpperCase() === n) {//todo: check how many are title cased, all caps, all lower.
			//console.log(`don't yell at me :( ${n}`);
		}
		return n;
	},
	g: async function (address) {
		let addr = {address: '', address2: ''};
		let location = (await googleMapsClient.geocode({params: {address, key: process.env.GAPI}})).data;
		if(location.status === 'OK') {
			let wow = 0;
			for(let j = 0; j < location.results[0].address_components.length; ++j) {
				if(location.results[0].address_components[j].types.includes('administrative_area_level_1')) {
					addr.region = location.results[0].address_components[j].long_name;
					++wow;
				} else if(location.results[0].address_components[j].types.includes('country')) {
					addr.country = location.results[0].address_components[j].long_name;
					++wow;
				} else if(location.results[0].address_components[j].types.includes('locality')) {
					addr.city = location.results[0].address_components[j].long_name;
					++wow;
				} else if(location.results[0].address_components[j].types.includes('postal_code')) {
					addr.postal_code = location.results[0].address_components[j].long_name.replace(/\s+/, '');
					++wow;
				} else if(location.results[0].address_components[j].types.includes('route')) {
					addr.address += ' ' + location.results[0].address_components[j].long_name;
					++wow;
				} else if(location.results[0].address_components[j].types.includes('street_number')) {
					addr.address += location.results[0].address_components[j].long_name;
					++wow;
				} else if(location.results[0].address_components[j].types.includes('subpremise')) {
					addr.address2 = location.results[0].address_components[j].long_name;
				}
			}
			if(wow < 6) {
				console.log('hardcode me', address, location);
				throw('unparseable address');
			}
			location = location.results[0].geometry.location;
			location.lng = (new Big(location.lng)).toFixed(7);
			location.lat = (new Big(location.lat)).toFixed(7);
			let [tz, foot, car] = await Promise.all([
				googleMapsClient.timezone({params: {location, timestamp: 0, key: process.env.GAPI}}),
				request(`http://127.0.0.1:5000/nearest/v1/fuck/${location.lng},${location.lat}`).then(x => JSON.parse(x).waypoints[0].hint),
				request(`http://127.0.0.1:5001/nearest/v1/fuck/${location.lng},${location.lat}`).then(x => JSON.parse(x).waypoints[0].hint)
			]);
			if(tz.data.status === 'OK') {
				return [addr, location, tz.data.timeZoneId, foot, car];
			} else {
				throw tz;
			}
		} else {
			throw location;
		}
	},
	util: util,
	Big: Big,
	cloudinary: require('cloudinary').v2,
	exec: util.promisify(require('child_process').exec),
	puppeteer: require('puppeteer'),
	mnmalism: async function (page) {
		await page.setUserAgent((await page.evaluate('navigator.userAgent')).replace('Headless', ''));
		await page.setJavaScriptEnabled(false);
		await page.setRequestInterception(true);
		page.on('request', request => {
			if(request.resourceType() === 'document') {
				request.continue();
			} else {
				request.abort();
			}
		});
		return page;
	},
	cliProgressBar: new (require('cli-progress').Bar)({
		stopOnComplete: true,
		format: '{bar} {percentage}% | ETA: {eta_formatted} | Elasped: {duration_formatted} | {value}/{total}'
	}),
	request: require('request-promise-native'),
	percent2proportion: function(percent) {
		return (new Big(percent)).div(100).toString();
	},
	pg,
	postgres,
	qty: /(?:(\d+)\s*[Xx]\s*)?(\d*\.?\d+)/,//([Xx]) and /g for alberta. unify?
	concentration: /(thc|cbd):?\s*(\d*\.?\d+)(?:[-−](\d*\.?\d+))?/gi,
	inBrackets: inBrackets,
	groupBy: function(xs, key) {
		return xs.reduce(function(rv, x) {
			(rv[x[key]] = rv[x[key]] || []).push(x);
			return rv;
		}, {});
	},
	goto: async function(page, url, path, attempt) {
		let status;
		try {
			status = (await page.goto(url + path)).status();
		} catch(e) {
			console.error(e);
			status = -1;
		}
		if(status === 200) {
			return;
		} else if(attempt < 3) {
			//doesn't make sense to retry 404s?
			let filename = +new Date() + '.png';
			console.error(status, page.url(), filename);
			try {
				await Promise.all([
					new Promise(resolve => setTimeout(resolve, 30000 * ++attempt)),
					page.screenshot({path: filename, fullPage: true})
				]);
			} catch(e) {
				console.error("failed to take screenshot")
			}
			return goto(page, url, path, attempt);
		} else {
			console.error(status, 'shopify is on to us? watch headful');
			process.exit(1);
		}
	},
	shopify_stock: async function(page, url, id, attempt) {
		let result = await page.evaluate(async (id, url) => {
			let init = {
				headers: {
					"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
					"x-requested-with": "XMLHttpRequest"
				},
				body: "id=" + id + "&quantity=",
				method: "POST"
			};
			let init2 = Object.assign({}, init);
			init.body += '1';
			init2.body += '9000';
			let one = await fetch(url + "cart/add.js", init);
			if(one.status === 422) {
				return "dumfuck ocs didn't use the algolia plugin with shopify";
			} else {
				return (await fetch(url + "cart/change.js", init2)).text();
			}
		}, id, url);
		try {
			if(result === "dumfuck ocs didn't use the algolia plugin with shopify") {
				return 0;
			} else {
				result = JSON.parse(result);
				let item = result.items[0];
				if(item.id === id) {
					return item.quantity;
				} else {
					throw 'mismatch';
				}
			}
		} catch(e) {
			if(attempt < 3) {
				await new Promise(resolve => setTimeout(resolve, 30000 * ++attempt));
				return shopify_stock(page, url, id, attempt);
			} else {
				console.error(id);
				console.error(e);
				console.error(result);
				console.error('maybe it was a bad idea to strip those headers...');
				process.exit(1);
			}
		}
	},
	roman2number(s) {//https://leetcode.com/problems/roman-to-integer/
		s = s.replace(/\s/g, '');
		let sum = 0;
		const m = {
			I: 1,
			V: 5,
			X: 10,
			L: 50,
			C: 100,
			D: 500,
			M: 1000
		}
		for(let i = s.length - 1; i >= 0; --i) {
			sum += m[s[i]];
			if(//lol thank god that s[-1] is undefined lmao instead of crash
				s[i - 1] === 'I' && (s[i] === 'V' || s[i] === 'X') ||
				s[i - 1] === 'X' && (s[i] === 'L' || s[i] === 'C') ||
				s[i - 1] === 'C' && (s[i] === 'D' || s[i] === 'M')
			) {
				sum -= m[s[--i]];
			}
		}
		return ' ' + sum + ' ';
	},
	strain_clean(s) {
		if(!s) {
			return null;
		}
		s = s
		.normalize("NFD")//https://stackoverflow.com/a/37511463
		.replace(/[\u0300-\u036f]/g, "")
		//.replace(/((^|\s)(?=[MDCLXVI])M*(C[MD]|D?C{0,3})(X[CL]|L?X{0,3})(I[XV]|V?I{0,3})($|\s))/g, roman2number) fuck you "Josh D OG"
		//https://stackoverflow.com/a/6713327
		//https://stackoverflow.com/a/36576402
		//https://stackoverflow.com/a/11622109
		//perhaps we should delete words like og, kush?
		.replace(/(^|\s)(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII|XIII|XIV|XV|XVI|XVII|XVIII|XIX|XX|XXI|XXII|XXIII|XXIV|XXV|XXVI|XXVII|XXVIII|XXIX|XXX)($|\s)/g, roman2number)
		.toLowerCase()
		.replace(/\b(bc|cbd)\b/, "")
		.replace(/\bgrand\s+daddy\b/, 'granddaddy')//todo: better space detection. lots of strains can be spelt with or without spaces... which will throw off the sort
		.replace(/\bpurps\b/, 'purple')
		.replace(/[^a-z0-9 ]/g, "")
		.split(/\s+/)
		.sort()
		.join('');
		switch(s) {
			case 'cland':
				s = 'candyland';
				break;
			case 'cheese':
				s = 'cheeseuk';
				break;
			case 'chemdawg':
				s = 'chemdog';
				break;
			case 'cookiesgirlscout':
				s = 'gsc';
				break;

		}
		return s;
	},
	normalize(brand, name, type, producer_in, description) {
		function pin() {
			if(producer_in) {
				producer = producer_in.toLowerCase().replace('.', '').replace(/&|(\b(and|health|therapeutics|co|company|inc|cannabis)\b)/g, '').trim();
				if(producer.includes('aurora')) {
					return 'Aurora';
				} else if(producer.includes('aphria')) {
					return 'aphria';
				} else if(/high\s*park\s*/.test(producer)) {
					return 'TILRAY';
				} else {
					console.log(producer, brand, 'need to normalize producer_in');
				}
			} else if(description) {
				producer = description.match(/by ([a-z0-9 ]+?)\./i)?.[1];
				if(!producer) {
					console.log('not in description :(', brand, name);
				}
			}
		}
		let producer;
		if(name.includes('(')) {
			//console.log('fuck', name);
		}
		let originalName = name.split(/\s+/);
		name = name.toLowerCase();
		if(name.includes('fiveup') || /5\s*up/.test(name)) {
			return ['TILRAY', 'Canaca', '5up', '5up'];
		} else if(name.includes('tenup') || /10\s*up/.test(name)) {
			return ['TILRAY', 'Canaca', '10up', '10up'];
		} else {
			brand = brand.toLowerCase();
			let q = name.match(specialqty);
			if(q !== null) {
				q = q[0].match(/\d*\.?\d+/)[0];
			}
			name = name
				.replace(inBrackets, '')
				.replace(brand, '')
				.replace(/twd(\.?)|"|-||%|&|'/g, '')
				.replace(/\b(aces|liquid|and|oral|drop(s?)|((hard|soft)(-|| )?)?(capsule|cap)(s?)|pre(-|| )?roll(s?)|pack(s?)|joint(s?)|oil|flower|spray|(?<!no\.\s?)510|cartridge|thread|disposable|vape|pen|kit|starter|powder|(?<!(iced|green)\s*)tea|(soft)?\s?chew(s?))\b/g, '')
				.replace(specialqty, '')
			;
			if(type === 'spray') {
				name = name.replace('mist', '');
			}
			//logo is canonical capitalization and spelling
			if(brand.includes('edison')) {
				producer = 'ORGANIGRAM';
				if(name.includes('reserve') || brand.includes('reserve')) {
					name = name.replace('reserve', '');
					brand = 'EDISON RESERVE';
				} else if(name.includes('bytes') || type === 'chocolate' || brand.includes('bytes')) {
					name = name.replace('bytes', '');
					brand = 'EDISON BYTES';
				} else {
					brand = 'EDISON';
				}
				if(/casa\s*blanca/.test(name)) {
					name = 'casablanca';
				}
			} else if(brand.includes('jwc')) {
				producer = 'JWC';
				brand = '';
			} else if(brand.includes('avitas')) {
				producer = '48North';
				brand = 'AVITAS';
			} else if(/muskoka\s*grown/.test(brand)) {
				producer = 'MUSKOKA GROWN';
				brand = '';
			} else if(/re(\s*|-)up/.test(brand)) {
				producer = 'Zenabis';
				brand = 'Re-Up';
			} else if(brand.includes('palmetto')) {
				producer = 'SUNDIAL';
				brand = 'PALMETTO';
			} else if(brand.includes('sundial')) {
				producer = 'SUNDIAL';
				//interesting behavior with .match and /g but we only use [0] anyways
				//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match#Return_value
				brand = brand.match(/\b(calm|ease|flow|lift|spark)\b/) || name.match(/\b(calm|ease|flow|lift|spark)\b/);
				if(brand === null) {
					brand = '';
				} else {
					brand = brand[0].toUpperCase();
				}
				name = name.replace(/\b(calm|ease|flow|lift|spark)\b/, '');
			} else if(/alta\s*vie/.test(brand)) {
				producer = 'Aurora';
				brand = 'AltaVie';
			} else if(brand.includes('robinsons')) {
				producer = 'ROBINSONS';
				brand = '';
			} else if(brand.includes('buds')) {
				producer = 'ORGANIGRAM';
				brand = 'TRAILER PARK BUDS';
			} else if(brand.includes('affirma')) {
				producer = 'TIDAL';
				brand = 'Affirma';
			} else if(brand.includes('blissco')) {
				producer = 'Supreme';
				brand = 'blissco';
			} else if(/five\s*founders/i.test(brand)) {
				producer = 'AgMEDICA';
				brand = 'five founders';
			} else if(/medipharm\s*labs/i.test(brand)) {
				producer = 'MediPharm Labs';
				brand = '';
			} else if(brand.includes('veryvell')) {
				producer = 'HEXO';
				brand = 'veryvell';
			} else if(brand.includes('phyto')) {
				producer = 'Namaste Technologies';
				brand = 'PHYTO EXTRACTIONS';
			} else if(/made\s*by/i.test(brand)) {
				producer = 'VALENS';
				brand = 'made by';
			} else if(brand.includes('fume')) {
				producer = '48North';
				brand = 'FUME';
			} else if(brand.includes('summit')) {
				producer = 'VALENS';
				brand = 'SUMMIT';
			} else if(brand.includes('house of terpenes')) {
				producer = 'HEXO';
				brand = 'HOUSE of TEPRENES';
			} else if(brand.includes('basecamp')) {
				producer = 'VALENS';
				brand = 'BASECAMP';
			} else if(/deep\s*space/i.test(brand)) {
				producer = 'CANOPY GROWTH';
				brand = 'DEEP SPACE';
			} else if(/delta\s*9/i.test(brand)) {
				producer = 'Delta 9';
				brand = '';
			} else if(brand.includes('wildlife')) {
				producer = 'AGRO GREENS';
				brand = 'WILDLIFE';
			} else if(brand.includes('journey')) {
				producer = 'AGRO GREENS';
				brand = 'JOURNEY';
			} else if(/benchmark\s*botanics/i.test(brand)) {
				producer = 'BENCHMARK BOTANICS';
				brand = '';
			} else if(/jc\s*green/i.test(brand)) {
				producer = 'JC GREEN';
				brand = '';
			} else if(brand.includes('highland')) {
				producer = 'BIOME';
				brand = 'HIGHLAND';
			} else if(/citizen\s*stash/i.test(brand)) {
				producer = 'experion';
				brand = 'citizen stash';
			} else if(brand.includes('doja')) {
				producer = 'CANOPY GROWTH';
				brand = 'DOJA';
			} else if(/abba\s*medix/i.test(brand)) {
				producer = 'abba medix';
				brand = '';
			} else if(brand.includes('abcann')) {
				producer = 'VIVO';
				brand = 'ABcann MEDICINALS';
			} else if(brand.includes('legend')) {
				producer = 'INDIVA';
				brand = 'LEGEND';
			} else if(/simply\s*bare/i.test(brand)) {
				producer = 'RUBICON ORGANICS';
				brand = 'SIMPLY bare';
			} else if(brand.includes('ignite')) {
				brand = 'IGNITE';
				producer = pin();
			} else if(brand.includes('grasslands')) {
				producer = 'SUNDIAL';
				brand = 'Grasslands';
			} else if(brand.includes('tenzo')) {
				producer = 'GTEC';
				brand = 'Tenzo';
			} else if(brand.includes('blkmkt')) {
				producer = 'GTEC';
				brand = 'blkmkt';
			} else if(brand.includes('msiku')) {
				producer = 'ATLANTICANN';
				brand = 'MSIKU';
			} else if(brand.includes('ankr organics')) {
				producer = 'ORGANIGRAM';
				brand = 'ankr ORGANICS';
			} else if(brand.includes('ecotone')) {
				producer = 'NORTHERN GREEN CANADA';
				brand = 'ecotone';
			} else if(/weed\s*me/i.test(brand)) {
				producer = 'Weed Me';
				brand = '';
			} else if(brand.includes('18twelve')) {
				producer = 'united greeneries';
				brand = '18twelve'
			} else if(/captain('?)s choice/i.test(brand)) {
				producer = 'united greeneries';
				brand = "CAPTAIN'S CHOICE"
			} else if(brand.includes('kingsway')) {
				producer = 'Aurora';//high12brands just rebrands this shit
				//https://vapethebud.com/cannabis-review-dayshift-by-kingsway/
				//https://vapethebud.com/cannabis-review-nightshift-by-kingsway/
				brand = 'kingsway';
			} else if(brand.includes('mezzero')) {
				producer = 'Namaste Technologies';
				brand = 'MEZZERO';
			} else if(brand.includes('cannmart')) {
				producer = 'Namaste Technologies';
				brand = 'CannMart';
			} else if(brand.includes('simple stash')) {
				producer = 'CANOPY GROWTH';
				brand = 'SIMPLE STASH';
			} else if(brand.includes('tweed')) {
				producer = 'CANOPY GROWTH';
				brand = 'Tweed';
			} else if(brand.includes('canaca')) {
				producer = 'TILRAY';
				if(name.includes('select') || brand.includes('select')) {
					brand = 'Canaca Select';
					name = name.replace('select', '');
				} else if(name.includes('14')) {
					name = 'BLEND 14';
				} else if(name.includes('19')) {
					name = 'BLEND 19';
				} else {
					brand = 'Canaca';
				}
			} else if(brand.includes('tantalus')) {
				producer = 'Tantalus Labs';
				brand = '';
				name = name.replace(/bc|sungrown/g, '');
			} else if(/48\s*north/.test(brand)) {
				producer = '48North';
				brand = '';
			} else if(/7\s*acres/.test(brand)) {
				producer = 'Supreme';
				brand = '7ACRES';
			} else if(/ace\s*valley/.test(brand)) {
				producer = 'flowr';
				brand = 'Ace Valley';
			} else if(/acreage pharms\s*/.test(brand)) {
				producer = 'Acreage Pharms';
				brand = '';
			} else if(brand.includes('aurora') || brand.includes('aces')) {
				producer = 'Aurora';
				if(brand.includes('drift')) {
					brand = 'Drift';
					name = name.replace('drift', '');
				} else {
					brand = '';
				}
			} else if(brand.includes('beleave')) {
				producer = 'beleave';
				brand = '';
			} else if(brand.includes('bhang')) {
				producer = 'Bhang';
				brand = '';
			} else if(brand.includes('blazery')) {
				producer = 'Zenabis';
				brand = 'Blazery';
			} else if(brand.includes('blissed')) {
				brand = 'BLISSED';
				producer = pin();
			} else if(brand.includes('goodship')) {
				brand = 'GOODSHIP';
				producer = pin();
			} else if(/broken\s*coast/.test(brand)) {
				producer = 'aphria';
				brand = 'BROKEN COAST';
			} else if(/canna\s*farms/.test(brand)) {
				producer = 'VIVO';
				brand = 'CANNA FARMS';
			} else if(/chowie\s*wowie/.test(brand)) {
				producer = 'TILRAY';
				brand = 'ChOWie wOWie';
			} else if(/color\s*cannabis/.test(brand)) {
				producer = 'weedmd';
				brand = 'Color';
			} else if(brand.includes('cove')) {
				producer = 'CRONOS';
				brand = 'COVE';
			} else if(/daily\s*special/.test(brand)) {
				producer = 'Aurora';
				brand = 'Daily SPECIAL';
			} else if(/dna\s*genetics/.test(brand)) {
				producer = 'CANOPY GROWTH';
				brand = 'DNA GENETICS';
			} else if(brand.includes('dosecann')) {
				producer = 'Auxly';
				brand = 'Dosecann';
			} else if(brand.includes('dosist')) {
				brand = 'dosist';
				producer = pin();
			} else if(brand.includes('emerald')) {
				producer = 'emerald';
				brand = '';
			} else if(brand.includes('everie')) {
				producer = 'TILRAY';
				brand = 'Everie';
			} else if(brand.startsWith('eve')) {
				producer = 'Natural MedCo';
				brand = 'eVe';
			} else if(brand.includes('figr')) {
				producer = "Canada's Island Garden";
				brand = 'FIGR';
			} else if(brand.includes('fireside')) {
				producer = 'VIVO';
				brand = 'FIRESIDE';
			} else if(brand.includes('flowr')) {
				producer = 'flowr';
				brand = '';
			} else if(brand.includes('foray')) {
				producer = 'Auxly';
				brand = 'Foray';
			} else if(brand.includes('gage')) {
				producer = 'RADICAL';
				brand = 'GAGE';
			} else if(/good\s*supply/.test(brand)) {
				producer = 'aphria';
				brand = 'GOOD SUPPLY';
			} else if(brand.includes('grail')) {
				producer = 'TILRAY';
				brand = 'GRAIL';
			} else if(brand.includes('haven')) {
				producer = 'TERRASCEND';
				brand = 'Haven Street';
			} else if(brand.includes('hexo')) {
				producer = 'HEXO';
				brand = '';
			} else if(/high\s*tide/.test(brand)) {
				producer = 'WAYLAND';
				brand = 'HIGH TIDE';
			} else if(brand.includes('houseplant')) {
				producer = 'CANOPY GROWTH';
				brand = 'Houseplant';
			} else if(brand.includes('indiva')) {
				producer = 'INDIVA';
				brand = 'INDIVA';
			} else if(brand.includes('irisa')) {
				producer = 'TILRAY';
				brand = 'IRISA';
			} else if(brand.includes('kiwi')) {
				producer = 'WAYLAND';
				brand = 'KIWI';
			} else if(brand.includes('kke')) {
				producer = 'Supreme';
				brand = "KKE";
			} else if(/kolab\s*project/.test(brand)) {
				producer = 'Auxly';
				brand = 'KOLAB PROJECT';
			} else if(brand.includes('lbs')) {
				producer = 'CANOPY GROWTH';
				brand = 'LBS';
			} else if(brand.includes('liiv')) {
				producer = 'CannTrust';
				brand = 'liiv';
			} else if(brand.includes('lumina')) {
				producer = 'VIVO';
				brand = 'LUMINA';
			} else if(/marley\s*natural/.test(brand)) {
				producer = 'TILRAY';
				brand = 'MARLEY NATURAL';
			} else if(brand.includes('namaste')) {
				producer = 'Zenabis';
				brand = 'NAMASTE';
			} else if(/northern\s*green\s*canada/.test(brand)) {
				producer = 'NORTHERN GREEN CANADA';
				brand = '';
			} else if(/northern\s*harvest/.test(brand)) {
				producer = 'WAYLAND';
				brand = 'NORTHERN HARVEST';
			} else if(/original\s*stash/.test(brand)) {
				producer = 'HEXO';
				brand = 'ORIGINAL STASH';
			} else if(/plain\s*packaging/.test(brand) || brand.includes('twd')) {
				producer = 'CANOPY GROWTH';
				brand = 'Twd.';
			} else if(/pure\s*sunfarms/.test(brand)) {
				producer = 'PURE SUNFARMS';
				brand = '';
			} else if(brand.includes('qwest')) {//technically wrong, but I don't want to duplicate the brand in the name. fucktards
				producer = 'dECIBEL';
				brand = 'QWEST';
				if(name.includes('ice')) {
					brand += ' RESERVE';
					name = 'ice';
				} else if(name.includes('spirit')) {
					brand += ' RESERVE';
					name = 'spirit';
				} else if(name.includes('wonder')) {
					name = 'wonder';
				} else if(name.includes('zest')) {
					name = 'zest';
				} else if(name.includes('vision')) {
					name = 'vision';
				} else {
					console.log('unrecognized quest product:', name)
				}
			} else if(brand.includes('redecan') || brand.includes('redees')) {
				producer = 'REDECAN';
				brand = '';
			} else if(brand.includes('reef')) {
				producer = 'aqualitas';
				brand = 'reef';
			} else if(brand.includes('riff')) {
				producer = 'aphria';
				brand = 'RIFF';
			} else if(/royal\s*high/.test(brand)) {
				producer = 'united greeneries';
				brand = 'ROYAL HIGH';
			} else if(/san\s*rafael/.test(brand)) {
				producer = 'Aurora';
				brand = "San Rafael '71";
			} else if(brand.includes('saturday')) {
				producer = 'weedmd';
				brand = 'SATURDAY';
			} else if(/(seven|7)\s*oak/.test(brand)) {
				producer = 'beleave';
				brand = 'SEVEN OAKS';
			} else if(brand.includes('solei')) {
				producer = 'aphria';
				brand = 'Solei';
			} else if(brand.includes('spinach')) {
				producer = 'CRONOS';
				brand = 'Spinach';
			} else if(/sugar\s*leaf/.test(brand)) {
				producer = 'Supreme';
				brand = 'sugar leaf';
			} else if(brand.includes('symbl')) {
				producer = 'Aleafia';
				brand = 'symbl';
			} else if(brand.includes('synr')) {
				producer = 'CannTrust';
				brand = 'SYNR.G';
			} else if(/thc\s*biomed/.test(brand)) {
				producer = 'THC BioMed';
				brand = '';
			} else if(/the\s*batch/.test(brand)) {
				producer = 'TILRAY';
				brand = 'THE BATCH';
				name = '';
			} else if(/the\s*green\s*organic\s*dutchman/.test(brand) || brand.includes('tgod')) {
				producer = 'THE GREEN ORGANIC DUTCHMAN';
				brand = '';
			} else if(/tokyo\s*smoke/.test(brand)) {
				producer = 'CANOPY GROWTH';
				brand = 'TOKYO SMOKE';
			} else if(/top\s*leaf/.test(brand)) {
				producer = 'SUNDIAL';
				brand = 'TOPLEAF';
			} else if(brand.includes('trailblazer')) {
				producer = 'ORGANIGRAM';
				brand = 'TRAILBLAZER';
			} else if(brand.includes('up')) {
				producer = 'HEXO';
				brand = 'UP';
			} else if(/van\s*der\s*pop/.test(brand)) {
				producer = 'CANOPY GROWTH';
				brand = 'Van der Pop';
			} else if(brand.includes('vertical')) {
				producer = 'AgMEDICA';
				brand = 'VERTICAL';
			} else if(brand.includes('wayfarer')) {
				producer = 'MediPharm Labs';
				brand = 'Wayfarer';
			} else if(brand.includes('wdbx')) {
				producer = 	'CANADA HOUSE';
				brand = 'WDBX';
			} else if(brand.includes('whistler')) {
				producer = 'Aurora';
				brand = 'Whistler';
			} else if(brand.includes('wink')) {
				brand = 'WINK';
				producer = pin();
			} else if(brand.includes('woodstock')) {
				producer = 'Aurora';
				brand = 'WOODSTOCK';
			} else if(brand.includes('xscape')) {
				producer = 'CannTrust';
				brand = 'XSCAPE';
			} else {
				console.log(`unknown brand: ${brand}`);
				brand = brand.replace('.', '').replace(/&|(\b(and|health|therapeutics|co|company|inc|cannabis)\b)/g, '').trim();
			}
			if(typeof brand === 'string') {
				name = name.replace(brand.toLowerCase(), '');
			}
			if(typeof producer === 'string') {
				name = name.replace(producer.toLowerCase(), '');
			}
			return [producer, brand, name.split(/\s+/).reduce((accumulator, modified_word) => {
				for(let j = originalName.length - 1; j >= 0; --j) {
					if(modified_word === originalName[j].replace(/"|-||'/g, '').toLowerCase()) {
						accumulator.push(originalName[j]);
						originalName.splice(j, 1);
						break;
					}
				}
				return accumulator;
			}, []).join(' '), name.split(/\s+/).sort().join(''), q];
		}
	},
	async images(pics/*type set*/, productID) {
		for(let pic of pics) {
			let product_images = (await postgres.query(`select product_image_id, case when now() - system_time_start > '30 days' then destination else null end as destination from product_image where product_id = $1 and source = $2`, [productID, pic])).rows;
			if(product_images.length === 0) {
				let destination, x, y;
				if(process.env.CLOUDINARY_URL === 'cloudinary://api_key:api_secret@cloud_name') {
					//https://stackoverflow.com/a/13108449/
					let name = pic.split('/');
					name = name.pop() || name.pop();
					name += name.substring(name.lastIndexOf('.'));
					destination = "https://res.cloudinary.com/the-eternal-tao/image/upload/" + name;
				} else {
					let response = (await exec('./image.sh ' + pic)).stdout.trim();
					if(response.startsWith('/tmp/')) {
						response = response.split('\n');
						[x, y] = response[1].split('x');
						try {
							destination = (await cloudinary.uploader.upload(response[0], {public_id: response[0].substring(5), unique_filename: false})).secure_url;
						} catch(e) {
							console.error(e);
							console.error('skipping image upload due to cloudinary outage')
							return;
						}
					} else {
						continue;
					}
				}
				let img_insert = (await postgres.query(
					`insert into product_image (
						product_id,
						source,
						destination,
						x,
						y
					) values (
						$1,
						$2,
						$3,
						$4,
						$5
					) on conflict do nothing
					returning product_id`,
					[productID, pic, destination, x, y]
				)).rows;
				if(img_insert.length === 0) {
					console.error("how the FUCK", productID, pic, destination);
				} else if(img_insert.length > 1) {
					console.error('kys');
				}
			} else if(product_images.length === 1) {
				if(product_images[0].destination !== null && process.env.CLOUDINARY_URL !== 'cloudinary://api_key:api_secret@cloud_name') {
					let response = (await exec(`./image.sh ${pic} ${product_images[0].destination}`)).stdout.trim();
					if(response.startsWith('/tmp/')) {
						response = response.split('\n');
						try {
							await postgres.query('update product_image set destination = $1, x = $3, y = $4 where product_image_id = $2', [
								(await cloudinary.uploader.upload(response[0], {public_id: response[0].substring(5), unique_filename: false})).secure_url,
								product_images[0].product_image_id, ...response[1].split('x')
							]);
						} catch(e) {
							console.error(e);
							console.error('skipping image upload due to cloudinary outage?')
							return;
						}
					}
				}
			} else {
				console.error('data integrity issue', product_images);
				//product match, source match, but destinations are different, which should be unpossible
			}
		}
	}
}