#!/usr/bin/env node

'use strict';
var path = require('path');
var os = require('os');

// https://nodejs.org/api/os.html#os_os_arch
// 'arm', 'arm64', 'ia32', 'mips', 'mipsel', 'ppc', 'ppc64', 's390', 's390x', 'x32', and 'x64'
var arch = os.arch(); // process.arch

// https://nodejs.org/api/os.html#os_os_platform
// 'aix', 'darwin', 'freebsd', 'linux', 'openbsd', 'sunos', 'win32'
var platform = os.platform(); // process.platform
var ext = /^win/i.test(platform) ? '.exe' : '';

// This is _probably_ right. It's good enough for us
// https://github.com/nodejs/node/issues/13629
if ('arm' === arch) {
	arch += 'v' + process.config.variables.arm_version;
}

var map = {
	// arches
	armv6: 'armv6',
	armv7: 'armv7',
	arm64: 'armv8',
	ia32: '386',
	x32: '386',
	x64: 'amd64',
	// platforms
	darwin: 'darwin',
	linux: 'linux',
	win32: 'windows'
};

arch = map[arch];
platform = map[platform];

if (!arch || !platform) {
	console.error(
		"'" + os.platform() + "' on '" + os.arch() + "' isn't supported yet."
	);
	console.error(
		'Please open an issue at https://git.rootprojects.org/root/serviceman/issues'
	);
	process.exit(1);
}

var newVer = require('../package.json').version;
var fs = require('fs');
var exec = require('child_process').exec;
var request = require('@root/request');
var mkdirp = require('@root/mkdirp');

function needsUpdate(oldVer, newVer) {
	// "v1.0.0-pre" is BEHIND "v1.0.0"
	newVer = newVer
		.replace(/^v/, '')
		.split(/[\.\-\+]/)
		.filter(Boolean);
	oldVer = oldVer
		.replace(/^v/, '')
		.split(/[\.\-\+]/)
		.filter(Boolean);

	if (!oldVer.length) {
		return true;
	}

	// ex: v1.0.0-pre vs v1.0.0
	if (newVer[3] && !oldVer[3]) {
		// don't install beta over stable
		return false;
	}

	// ex: old is v1.0.0-pre
	if (oldVer[3]) {
		if (oldVer[2] > 0) {
			oldVer[2] -= 1;
		} else if (oldVer[1] > 0) {
			oldVer[2] = 999;
			oldVer[1] -= 1;
		} else if (oldVer[0] > 0) {
			oldVer[2] = 999;
			oldVer[1] = 999;
			oldVer[0] -= 1;
		} else {
			// v0.0.0
			return true;
		}
	}

	// ex: v1.0.1 vs v1.0.0-pre
	if (newVer[3]) {
		if (newVer[2] > 0) {
			newVer[2] -= 1;
		} else if (newVer[1] > 0) {
			newVer[2] = 999;
			newVer[1] -= 1;
		} else if (newVer[0] > 0) {
			newVer[2] = 999;
			newVer[1] = 999;
			newVer[0] -= 1;
		} else {
			// v0.0.0
			return false;
		}
	}

	// ex: v1.0.1 vs v1.0.0
	if (oldVer[0] > newVer[0]) {
		return false;
	} else if (oldVer[0] < newVer[0]) {
		return true;
	} else if (oldVer[1] > newVer[1]) {
		return false;
	} else if (oldVer[1] < newVer[1]) {
		return true;
	} else if (oldVer[2] > newVer[2]) {
		return false;
	} else if (oldVer[2] < newVer[2]) {
		return true;
	} else if (!oldVer[3] && newVer[3]) {
		return false;
	} else if (oldVer[3] && !newVer[3]) {
		return true;
	} else {
		return false;
	}
}

/*
// Same version
console.log(false === needsUpdate('0.5.0', '0.5.0'));
// No previous version
console.log(true === needsUpdate('', '0.5.1'));
// The new version is slightly newer
console.log(true === needsUpdate('0.5.0', '0.5.1'));
console.log(true === needsUpdate('0.4.999-pre1', '0.5.0-pre1'));
// The new version is slightly older
console.log(false === needsUpdate('0.5.0', '0.5.0-pre1'));
console.log(false === needsUpdate('0.5.1', '0.5.0'));
*/

function install(name, bindirs, getVersion, parseVersion, urlTpl) {
	exec(getVersion, { windowsHide: true }, function(err, stdout) {
		var oldVer = parseVersion(stdout);
		//console.log('old:', oldVer, 'new:', newVer);
		if (!needsUpdate(oldVer, newVer)) {
			console.info(
				'Current ' + name + ' version is new enough:',
				oldVer,
				newVer
			);
			return;
			//} else {
			//	console.info('Current serviceman version is older:', oldVer, newVer);
		}

		var url = urlTpl
			.replace(/{{ .Version }}/g, newVer)
			.replace(/{{ .Platform }}/g, platform)
			.replace(/{{ .Arch }}/g, arch)
			.replace(/{{ .Ext }}/g, ext);

		console.info('Installing from', url);
		return request({ uri: url, encoding: null }, function(err, resp) {
			if (err) {
				console.error(err);
				return;
			}

			//console.log(resp.body.byteLength);
			//console.log(typeof resp.body);
			var bin = name + ext;
			function next() {
				if (!bindirs.length) {
					return;
				}
				var bindir = bindirs.pop();
				return mkdirp(bindir, function(err) {
					if (err) {
						console.error(err);
						return;
					}

					var localsrv = path.join(bindir, bin);
					return fs.writeFile(localsrv, resp.body, function(err) {
						next();
						if (err) {
							console.error(err);
							return;
						}
						fs.chmodSync(localsrv, parseInt('0755', 8));
						console.info('Wrote', bin, 'to', bindir);
					});
				});
			}
			next();
		});
	});
}

function winstall(name, bindir) {
	try {
		fs.writeFileSync(
			path.join(bindir, name),
			'#!/usr/bin/env bash\n"$(dirname "$0")/serviceman.exe" "$@"\nexit $?'
		);
	} catch (e) {
		// ignore
	}

	// because bugs in npm + git bash oddities, of course
	// https://npm.community/t/globally-installed-package-does-not-execute-in-git-bash-on-windows/9394
	try {
		fs.writeFileSync(
			path.join(path.join(__dirname, '../../.bin'), name),
			[
				'#!/bin/sh',
				'# manual bugfix patch for npm on windows',
				'basedir=$(dirname "$(echo "$0" | sed -e \'s,\\\\,/,g\')")',
				'"$basedir/../' + name + '/bin/' + name + '"   "$@"',
				'exit $?'
			].join('\n')
		);
	} catch (e) {
		// ignore
	}
	try {
		fs.writeFileSync(
			path.join(path.join(__dirname, '../../..'), name),
			[
				'#!/bin/sh',
				'# manual bugfix patch for npm on windows',
				'basedir=$(dirname "$(echo "$0" | sed -e \'s,\\\\,/,g\')")',
				'"$basedir/node_modules/' + name + '/bin/' + name + '"   "$@"',
				'exit $?'
			].join('\n')
		);
	} catch (e) {
		// ignore
	}
	// end bugfix
}

function run() {
	//var homedir = require('os').homedir();
	//var bindir = path.join(homedir, '.local', 'bin');
	var bindir = path.resolve(__dirname, '..', 'bin');
	var name = 'serviceman';
	if ('.exe' === ext) {
		winstall(name, bindir);
	}

	return install(
		name,
		[bindir],
		'serviceman version',
		function parseVersion(stdout) {
			return (stdout || '').split(' ')[0];
		},
		'https://rootprojects.org/serviceman/dist/{{ .Platform }}/{{ .Arch }}/serviceman{{ .Ext }}'
	);
}

if (require.main === module) {
	run();
}
