import {banner} from './banner';

import {config as config_} from 'dotenv';
import path from 'path';

config_({path: path.resolve(__dirname, '../.env')});

console.info(banner.render(envOrBoolean(process.env.ASCII_BANNER, false), envOrString(process.env.BANNER_COLOR, '#808080')));

/**
 * Returns environment variable, given array, or default array.
 *
 * @param environment Interested environment variable.
 * @param array Default array. If not set, is `[]`.
 */
function envOrArray(environment: string | undefined, array?: string[]): string[] {
	return (environment ? (
		environment.includes('\n') ? environment.split('\n') : environment.split(',')
	) : (array ?? [])).map(s => s.trim());
}

/**
 * Returns environment variable, given boolean, or default boolean.
 *
 * @param environment Interested environment variable.
 * @param boolean Default boolean. If not set, is `true`.
 */
function envOrBoolean(environment: string | undefined, boolean?: boolean): boolean {
	return environment ? environment === 'true' : (boolean ?? true);
}

/**
 * Returns environment variable, given string, or default string.
 *
 * @param environment Interested environment variable.
 * @param string Default string. If not set, is `''`.
 */
function envOrString(environment: string | undefined, string?: string): string {
	return environment ? environment : (string ?? '');
}

/**
 * Returns environment variable, given number, or default number.
 *
 * @param environment Interested environment variable.
 * @param number Default number. If not set, is `0`.
 */
function envOrNumber(environment: string | undefined, number?: number): number {
	return environment ? Number(environment) : (number ?? 0);
}

/**
 * Returns environment variable, given number, or default number,
 * while handling .env input errors for a Min/Max pair.
 * .env errors handled:
 * - Min/Max swapped (Min larger than Max, Max smaller than Min)
 * - Min larger than default Max when no Max defined
 * - Max smaller than default Min when no Min defined
 *
 * @param environmentMin Min environment variable of Min/Max pair.
 * @param environmentMax Max environment variable of Min/Max pair.
 * @param number Default number. If not set, is `0`.
 */
function envOrNumberMin(environmentMin: string | undefined, environmentMax: string | undefined, number?: number) {
	if (environmentMin || environmentMax) {
		if (environmentMin && environmentMax) {
			return Number(Number(environmentMin) < Number(environmentMax) ? environmentMin : environmentMax);
		}

		if (environmentMax) {
			return Number(environmentMax) < (number ?? 0) ? Number(environmentMax) : (number ?? 0);
		}

		if (environmentMin) {
			return Number(environmentMin);
		}
	}

	return number ?? 0;
}

/**
 * Returns environment variable, given number, or default number,
 * while handling .env input errors for a Min/Max pair.
 * .env errors handled:
 * - Min/Max swapped (Min larger than Max, Max smaller than Min)
 * - Min larger than default Max when no Max defined
 * - Max smaller than default Min when no Min defined
 *
 * @param environmentMin Min environment variable of Min/Max pair.
 * @param environmentMax Max environment variable of Min/Max pair.
 * @param number Default number. If not set, is `0`.
 */
function envOrNumberMax(environmentMin: string | undefined, environmentMax: string | undefined, number?: number) {
	if (environmentMin || environmentMax) {
		if (environmentMin && environmentMax) {
			return Number(Number(environmentMin) < Number(environmentMax) ? environmentMax : environmentMax);
		}

		if (environmentMin) {
			return Number(environmentMin) > (number ?? 0) ? Number(environmentMin) : (number ?? 0);
		}

		if (environmentMax) {
			return Number(environmentMax);
		}
	}

	return number ?? 0;
}

const browser = {
	isHeadless: envOrBoolean(process.env.HEADLESS),
	isIncognito: envOrBoolean(process.env.INCOGNITO, false),
	isTrusted: envOrBoolean(process.env.BROWSER_TRUSTED, false),
	lowBandwidth: envOrBoolean(process.env.LOW_BANDWIDTH, false),
	maxBackoff: envOrNumberMax(process.env.PAGE_BACKOFF_MIN, process.env.PAGE_BACKOFF_MAX, 3600000),
	maxSleep: envOrNumberMax(process.env.PAGE_SLEEP_MIN, process.env.PAGE_SLEEP_MAX, 10000),
	minBackoff: envOrNumberMin(process.env.PAGE_BACKOFF_MIN, process.env.PAGE_BACKOFF_MAX, 10000),
	minSleep: envOrNumberMin(process.env.PAGE_SLEEP_MIN, process.env.PAGE_SLEEP_MAX, 5000),
	open: envOrBoolean(process.env.OPEN_BROWSER)
};

const docker = envOrBoolean(process.env.DOCKER);

const logLevel = envOrString(process.env.LOG_LEVEL, 'info');

const notifications = {
	desktop: process.env.DESKTOP_NOTIFICATIONS === 'true',
	discord: {
		notifyGroup: envOrArray(process.env.DISCORD_NOTIFY_GROUP),
		webHookUrl: envOrArray(process.env.DISCORD_WEB_HOOK)
	},
	email: {
		password: envOrString(process.env.EMAIL_PASSWORD),
		smtpAddress: envOrString(process.env.SMTP_ADDRESS),
		smtpPort: envOrNumber(process.env.SMTP_PORT, 25),
		to: envOrString(process.env.EMAIL_TO, envOrString(process.env.EMAIL_USERNAME)),
		username: envOrString(process.env.EMAIL_USERNAME)
	},
	mqtt: {
		broker: envOrString(process.env.MQTT_BROKER_ADDRESS),
		clientId: envOrString(process.env.MQTT_CLIENT_ID),
		password: envOrString(process.env.MQTT_PASSWORD),
		port: envOrNumber(process.env.MQTT_BROKER_PORT, 1883),
		qos: envOrNumber(process.env.MQTT_QOS, 0),
		topic: envOrString(process.env.MQTT_TOPIC, 'streetmerchant/alert'),
		username: envOrString(process.env.MQTT_USERNAME)
	},
	pagerduty: {
		integrationKey: envOrString(process.env.PAGERDUTY_INTEGRATION_KEY),
		severity: envOrString(process.env.PAGERDUTY_SEVERITY, 'info')
	},
	philips_hue: {
		accessToken: envOrString(process.env.PHILIPS_HUE_CLOUD_ACCESS_TOKEN),
		apiKey: envOrString(process.env.PHILIPS_HUE_API_KEY),
		bridgeIp: envOrString(process.env.PHILIPS_HUE_LAN_BRIDGE_IP),
		clientId: envOrString(process.env.PHILIPS_HUE_CLOUD_CLIENT_ID),
		clientSecret: envOrString(process.env.PHILIPS_HUE_CLOUD_CLIENT_SECRET),
		lightColor: envOrString(process.env.PHILIPS_HUE_LIGHT_COLOR),
		lightIds: envOrString(process.env.PHILIPS_HUE_LIGHT_IDS),
		lightPattern: envOrString(process.env.PHILIPS_HUE_LIGHT_PATTERN),
		refreshToken: envOrString(process.env.PHILIPS_HUE_CLOUD_REFRESH_TOKEN),
		remoteApiUsername: envOrString(process.env.PHILIPS_HUE_API_KEY)
	},
	phone: {
		availableCarriers: new Map([
			['att', 'txt.att.net'],
			['attgo', 'mms.att.net'],
			['bell', 'txt.bell.ca'],
			['fido', 'fido.ca'],
			['google', 'msg.fi.google.com'],
			['koodo', 'msg.koodomobile.com'],
			['mint', 'mailmymobile.net'],
			['rogers', 'pcs.rogers.com'],
			['sprint', 'messaging.sprintpcs.com'],
			['telus', 'msg.telus.com'],
			['tmobile', 'tmomail.net'],
			['verizon', 'vtext.com'],
			['virgin', 'vmobl.com'],
			['virgin-ca', 'vmobile.ca']
		]),
		carrier: envOrString(process.env.PHONE_CARRIER),
		number: envOrString(process.env.PHONE_NUMBER)
	},
	playSound: envOrString(process.env.PLAY_SOUND),
	pushbullet: envOrString(process.env.PUSHBULLET),
	pushover: {
		priority: envOrNumber(process.env.PUSHOVER_PRIORITY),
		token: envOrString(process.env.PUSHOVER_TOKEN),
		username: envOrString(process.env.PUSHOVER_USER)
	},
	slack: {
		channel: envOrString(process.env.SLACK_CHANNEL),
		token: envOrString(process.env.SLACK_TOKEN)
	},
	telegram: {
		accessToken: envOrString(process.env.TELEGRAM_ACCESS_TOKEN),
		chatId: envOrArray(process.env.TELEGRAM_CHAT_ID)
	},
	twilio: {
		accountSid: envOrString(process.env.TWILIO_ACCOUNT_SID),
		authToken: envOrString(process.env.TWILIO_AUTH_TOKEN),
		from: envOrString(process.env.TWILIO_FROM_NUMBER),
		to: envOrString(process.env.TWILIO_TO_NUMBER)
	},
	twitch: {
		accessToken: envOrString(process.env.TWITCH_ACCESS_TOKEN),
		channel: envOrString(process.env.TWITCH_CHANNEL),
		clientId: envOrString(process.env.TWITCH_CLIENT_ID),
		clientSecret: envOrString(process.env.TWITCH_CLIENT_SECRET),
		refreshToken: envOrString(process.env.TWITCH_REFRESH_TOKEN)
	},
	twitter: {
		accessTokenKey: envOrString(process.env.TWITTER_ACCESS_TOKEN_KEY),
		accessTokenSecret: envOrString(process.env.TWITTER_ACCESS_TOKEN_SECRET),
		consumerKey: envOrString(process.env.TWITTER_CONSUMER_KEY),
		consumerSecret: envOrString(process.env.TWITTER_CONSUMER_SECRET),
		tweetTags: envOrString(process.env.TWITTER_TWEET_TAGS)
	}
};

const nvidia = {
	addToCardAttempts: envOrNumber(process.env.NVIDIA_ADD_TO_CART_ATTEMPTS, 10),
	sessionTtl: envOrNumber(process.env.NVIDIA_SESSION_TTL, 60000)
};

const page = {
	height: 1080,
	inStockWaitTime: envOrNumber(process.env.IN_STOCK_WAIT_TIME),
	screenshot: envOrBoolean(process.env.SCREENSHOT),
	timeout: envOrNumber(process.env.PAGE_TIMEOUT, 30000),
	userAgents: envOrArray(process.env.USER_AGENT, ['Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36']),
	width: 1920
};

const proxy = {
	address: envOrString(process.env.PROXY_ADDRESS),
	port: envOrNumber(process.env.PROXY_PORT, 80)
};

// Check for deprecated configuration values
if (process.env.MAX_PRICE) {
	console.warn('ℹ MAX_PRICE is deprecated, please use MAX_PRICE_SERIES_{{series}}');
}

const store = {
	country: envOrString(process.env.COUNTRY, 'usa'),
	maxPrice: {
		series: {
			3070: envOrNumber(process.env.MAX_PRICE_SERIES_3070),
			3080: envOrNumber(process.env.MAX_PRICE_SERIES_3080),
			3090: envOrNumber(process.env.MAX_PRICE_SERIES_3090),
			ryzen5600: envOrNumber(process.env.MAX_PRICE_SERIES_RYZEN5600),
			ryzen5800: envOrNumber(process.env.MAX_PRICE_SERIES_RYZEN5800),
			ryzen5900: envOrNumber(process.env.MAX_PRICE_SERIES_RYZEN5900),
			ryzen5950: envOrNumber(process.env.MAX_PRICE_SERIES_RYZEN5950),
			sonyps5c: -1,
			sonyps5de: -1,
			'test:series': -1
		}
	},
	microCenterLocation: envOrArray(process.env.MICROCENTER_LOCATION, ['web']),
	showOnlyBrands: envOrArray(process.env.SHOW_ONLY_BRANDS),
	showOnlyModels: envOrArray(process.env.SHOW_ONLY_MODELS).map(entry => {
		const [name, series] = entry.match(/[^:]+/g) ?? [];
		return {
			name: envOrString(name),
			series: envOrString(series)
		};
	}),
	showOnlySeries: envOrArray(process.env.SHOW_ONLY_SERIES, ['3070', '3080', '3090', 'ryzen5600', 'ryzen5800', 'ryzen5900', 'ryzen5950', 'sonyps5c', 'sonyps5de']),
	stores: envOrArray(process.env.STORES, ['nvidia']).map(entry => {
		const [name, minPageSleep, maxPageSleep] = entry.match(/[^:]+/g) ?? [];
		return {
			maxPageSleep: envOrNumberMax(minPageSleep, maxPageSleep, browser.maxSleep),
			minPageSleep: envOrNumberMin(minPageSleep, maxPageSleep, browser.minSleep),
			name: envOrString(name)
		};
	})
};

export const defaultStoreData = {
	maxPageSleep: browser.maxSleep,
	minPageSleep: browser.minSleep
};

export const config = {
	browser,
	docker,
	logLevel,
	notifications,
	nvidia,
	page,
	proxy,
	store
};

export function setConfig(newConfig: any) {
	const writeConfig = config as any;
	for (const key of Object.keys(newConfig)) {
		writeConfig[key] = newConfig[key];
	}
}
