'use strict';
const path = require('path');
const childProcess = require('child_process');
const osName = require('os-name');
const Conf = require('conf');
const chalk = require('chalk');
const debounce = require('lodash.debounce');
const inquirer = require('inquirer');
const uuid = require('uuid');
const providers = require('./providers');

const DEBOUNCE_MS = 100;

class Insight {
	constructor(options) {
		options = options || {};
		options.pkg = options.pkg || {};

		// Deprecated options
		// TODO: Remove these at some point in the future
		if (options.packageName) {
			options.pkg.name = options.packageName;
		}

		if (options.packageVersion) {
			options.pkg.version = options.packageVersion;
		}

		if (!options.trackingCode || !options.pkg.name) {
			throw new Error('trackingCode and pkg.name required');
		}

		this.trackingCode = options.trackingCode;
		this.trackingProvider = options.trackingProvider || 'google';
		this.packageName = options.pkg.name;
		this.packageVersion = options.pkg.version || 'undefined';
		this.os = osName();
		this.nodeVersion = process.version;
		this.appVersion = this.packageVersion;
		this.config = options.config || new Conf({
			configName: `insight-${this.packageName}`,
			defaults: {
				clientId: options.clientId || Math.floor(Date.now() * Math.random())
			}
		});
		this._queue = {};
		this._permissionTimeout = 30;
		this._debouncedSend = debounce(this._send, DEBOUNCE_MS, {leading: true});
	}

	get optOut() {
		return this.config.get('optOut');
	}

	set optOut(val) {
		this.config.set('optOut', val);
	}

	get clientId() {
		return this.config.get('clientId');
	}

	set clientId(val) {
		this.config.set('clientId', val);
	}

	_save() {
		setImmediate(() => {
			this._debouncedSend();
		});
	}

	_send() {
		const pending = Object.keys(this._queue).length;
		if (pending === 0) {
			return;
		}

		this._fork(this._getPayload());
		this._queue = {};
	}

	_fork(payload) {
		// Extracted to a method so it can be easily mocked
		const cp = childProcess.fork(path.join(__dirname, 'push.js'), {silent: true});
		cp.send(payload);
		cp.unref();
		cp.disconnect();
	}

	_getPayload() {
		return {
			queue: Object.assign({}, this._queue),
			packageName: this.packageName,
			packageVersion: this.packageVersion,
			trackingCode: this.trackingCode,
			trackingProvider: this.trackingProvider
		};
	}

	_getRequestObj(...args) {
		return providers[this.trackingProvider].apply(this, args);
	}

	track(...args) {
		if (this.optOut) {
			return;
		}

		const path = '/' + args.map(el => String(el).trim().replace(/ /, '-')).join('/');

		// Timestamp isn't unique enough since it can end up with duplicate entries
		this._queue[`${Date.now()} ${uuid.v4()}`] = {
			path,
			type: 'pageview'
		};
		this._save();
	}

	trackEvent(options) {
		if (this.optOut) {
			return;
		}

		if (this.trackingProvider !== 'google') {
			throw new Error('Event tracking is supported only for Google Analytics');
		}

		if (!options || !options.category || !options.action) {
			throw new Error('`category` and `action` required');
		}

		// Timestamp isn't unique enough since it can end up with duplicate entries
		this._queue[`${Date.now()} ${uuid.v4()}`] = {
			category: options.category,
			action: options.action,
			label: options.label,
			value: options.value,
			type: 'event'
		};
		this._save();
	}

	askPermission(msg, cb) {
		const defaultMsg = `May ${chalk.cyan(this.packageName)} anonymously report usage statistics to improve the tool over time?`;

		cb = cb || (() => {});

		if (!process.stdout.isTTY || process.argv.indexOf('--no-insight') !== -1 || process.env.CI) {
			setImmediate(cb, null, false);
			return;
		}

		const prompt = inquirer.prompt({
			type: 'confirm',
			name: 'optIn',
			message: msg || defaultMsg,
			default: true
		});

		// Add a 30 sec timeout before giving up on getting an answer
		const permissionTimeout = setTimeout(() => {
			// Stop listening for stdin
			prompt.ui.close();

			// Automatically opt out
			this.optOut = true;
			cb(null, false);
		}, this._permissionTimeout * 1000);

		prompt.then(result => {
			// Clear the permission timeout upon getting an answer
			clearTimeout(permissionTimeout);

			this.optOut = !result.optIn;
			cb(null, result.optIn);
		});
	}
}

module.exports = Insight;
