'use strict';
'require view';
'require form';
'require uci';
'require rpc';
'require ui';
'require poll';
'require request';
'require dom';

var callPackagelist = rpc.declare({
	object: 'rpc-sys',
	method: 'packagelist',
});

var callSystemBoard = rpc.declare({
	object: 'system',
	method: 'board',
});

var callUpgradeStart = rpc.declare({
	object: 'rpc-sys',
	method: 'upgrade_start',
	params: [ 'keep' ],
});

function get_branch(version) {
	// determine branch of a version
	// SNAPSHOT -> SNAPSHOT
	// 21.02-SNAPSHOT -> 21.02
	// 21.02.0-rc1 -> 21.02
	// 19.07.8 -> 19.07
	return version.replace('-SNAPSHOT', '').split('.').slice(0, 2).join('.');
}

function get_revision_count(revision) {
	return parseInt(revision.substring(1).split('-')[0]);
}

function error_api_connect(response) {
	ui.showModal(_('Error connecting to upgrade server'), [
		E('p', {},
			_('Could not reach API at "%s". Please try again later.')
		.format(response.url)),
		E('pre', {}, response.responseText),
		E('div', {
			class: 'right',
		},
			[
	E('div', {
		class: 'btn',
		click: ui.hideModal,
	},
		_('Close')),
			]),
	]);
}

function install_sysupgrade(url, keep, sha256) {
	displayStatus('notice spinning',
		E('p', _('Downloading firmware from server to browser')));
	request
			.get(url, {
	headers: {
		'Content-Type': 'application/x-www-form-urlencoded',
	},
	responseType: 'blob',
			})
			.then((response) => {
	var form_data = new FormData();
	form_data.append('sessionid', rpc.getSessionID());
	form_data.append('filename', '/tmp/firmware.bin');
	form_data.append('filemode', 600);
	form_data.append('filedata', response.blob());

	displayStatus('notice spinning',
					E('p', _('Uploading firmware from browser to device')));
	request
			.get(`${L.env.cgi_base}/cgi-upload`, {
				method: 'PUT',
				content: form_data,
			})
			.then((response) => response.json())
			.then((response) => {
				if (response.sha256sum != sha256) {
		displayStatus('warning', [
			E('b', _('Wrong checksum')),
			E('p',
				_('Error during download of firmware. Please try again')),
			E('div', {
				class: 'btn',
				click: ui.hideModal,
			},
				_('Close')),
		]);
				} else {
		displayStatus(
				'warning spinning',
				E('p',
					_('Installing the sysupgrade. Do not unpower device!')));
		L.resolveDefault(callUpgradeStart(keep), {})
				.then((response) => {
					if (keep) {
			ui.awaitReconnect(window.location.host);
					} else {
			ui.awaitReconnect('192.168.1.1', 'openwrt.lan');
					}
				});
				}
			});
			});
}

function request_sysupgrade(server_url, data) {
	var res, req;

	if (data.request_hash) {
		req = request.get(`${server_url}/api/v1/build/${data.request_hash}`);
	} else {
		req = request.post(`${server_url}/api/v1/build`, {
			profile: data.board_name,
			target: data.target,
			version: data.version,
			packages: data.packages,
			diff_packages: true,
		});
	}

	req.then((response) => {
		switch (response.status) {
		case 200:
			res = response.json();
			var image;
			for (image of res.images) {
	if (image.type == 'sysupgrade') {
		break;
	}
			}
			if (image.name != undefined) {
	var sysupgrade_url = `${server_url}/store/${res.bin_dir}/${image.name}`;

	var keep = E('input', {
		type: 'checkbox',
	});
	keep.checked = true;

	var fields = [
		_('Version'),
		`${res.version_number} ${res.version_code}`,
		_('File'),
		E('a', {
			href: sysupgrade_url,
		},
			image.name),
		_('SHA256'),
		image.sha256,
		_('Build Date'),
		res.build_at,
		_('Target'),
		res.target,
	];

	var table = E('div', {
		class: 'table',
	});

	for (var i = 0; i < fields.length; i += 2) {
		table.appendChild(E('tr', {
			class: 'tr',
		},
						[
				E('td', {
					class: 'td left',
					width: '33%',
				},
					[ fields[i] ]),
				E('td', {
					class: 'td left',
				},
					[ fields[i + 1] ]),
						]));
	}

	var modal_body = [
		table,
		E('p', {class: 'mt-2'},
			E('label', {
				class: 'btn',
			},
				[
		keep, ' ',
		_('Keep settings and retain the current configuration')
				])),
		E('div', {
			class: 'right',
		},
			[
				E('div', {
		class: 'btn',
		click: ui.hideModal,
				},
		_('Cancel')),
				' ',
				E('div', {
		class: 'btn cbi-button-action',
		click: function() {
			install_sysupgrade(sysupgrade_url, keep.checked,
						 image.sha256);
		},
				},
		_('Install Sysupgrade')),
			]),
	];

	ui.showModal(_('Successfully created sysupgrade image'), modal_body);
			}

			break;
		case 202:
			res = response.json();
			data.request_hash = res.request_hash;

			if ('queue_position' in res)
	displayStatus('notice spinning',
					E('p', _('Request in build queue position %s')
				 .format(res.queue_position)));
			else
	displayStatus('notice spinning',
					E('p', _('Building firmware sysupgrade image')));

			setTimeout(function() { request_sysupgrade(server_url, data); }, 5000);
			break;
		case 400: // bad request
		case 422: // bad package
		case 500: // build failed
			res = response.json();
			var body = [
	E('p', {}, res.detail),
	E('p', {}, _('Please report the error message and request')),
	E('b', {}, _('Request to server:')),
	E('pre', {}, JSON.stringify(data, null, 4)),
			];

			if (res.stdout) {
	body.push(E('b', {}, 'STDOUT:'));
	body.push(E('pre', {}, res.stdout));
			}

			if (res.stderr) {
	body.push(E('b', {}, 'STDERR:'));
	body.push(E('pre', {}, res.stderr));
			}

			body = body.concat([
	E('div', {
		class: 'right',
	},
		[
			E('div', {
				class: 'btn',
				click: ui.hideModal,
			},
				_('Close')),
		]),
			]);
			ui.showModal(_('Error building the sysupgrade'), body);
			break;
		}
	});
}

async function check_sysupgrade(server_url, system_board, packages) {
	var {board_name} = system_board;
	var {target, version, revision} = system_board.release;
	var current_branch = get_branch(version);
	var advanced_mode =
			uci.get_first('attendedsysupgrade', 'client', 'advanced_mode') || 0;
	var candidates = [];
	var response;

	displayStatus('notice spinning',
		E('p', _('Searching for an available sysupgrade of %s - %s')
				 .format(version, revision)));

	if (version.endsWith('SNAPSHOT')) {
		response =
	await request.get(`${server_url}/api/v1/revision/${version}/${target}`);
		if (!response.ok) {
			error_api_connect(response);
			return;
		}

		const remote_revision = response.json().revision;

		if (get_revision_count(revision) < get_revision_count(remote_revision)) {
			candidates.push(version);
		}
	} else {
		response = await request.get(`${server_url}/api/overview`, {
			timeout: 8000,
		});

		if (!response.ok) {
			error_api_connect(response);
			return;
		}

		const latest = response.json().latest;

		for (let remote_version of latest) {
			var remote_branch = get_branch(remote_version);

			// already latest version installed
			if (version == remote_version) {
	break;
			}

			// skip branch upgrades outside the advanced mode
			if (current_branch != remote_branch && advanced_mode == 0) {
	continue;
			}

			candidates.unshift(remote_version);

			// don't offer branches older than the current
			if (current_branch == remote_branch) {
	break;
			}
		}
	}

	if (candidates.length) {
		var m, s, o;

		var mapdata = {
			request: {
	board_name: board_name,
	target: target,
	version: candidates[0],
	packages: Object.keys(packages).sort(),
			},
		};

		m = new form.JSONMap(mapdata, '');

		s = m.section(form.NamedSection, 'request', 'example', '',
			'Use defaults for the safest update');
		o = s.option(form.ListValue, 'version', 'Select firmware version');
		for (let candidate of candidates) {
			o.value(candidate, candidate);
		}

		if (advanced_mode == 1) {
			o = s.option(form.Value, 'board_name', 'Board Name / Profile');
			o = s.option(form.DynamicList, 'packages', 'Packages');
		}

		m.render().then(function(form_rendered) {
			ui.showModal(_('New upgrade available'), [
	form_rendered,
	E('div', {
		class: 'right',
	},
		[
			E('div', {
				class: 'btn',
				click: ui.hideModal,
			},
				_('Cancel')),
			' ',
			E('div', {
				class: 'btn cbi-button-action',
				click: function() {
		m.save().then((foo) => {
			request_sysupgrade(server_url, mapdata.request);
		});
				},
			},
				_('Request Sysupgrade')),
		]),
			]);
		});
	} else {
		ui.showModal(_('No upgrade available'), [
			E('p', {},
	_('The device runs the latest firmware version %s - %s')
			.format(version, revision)),
			E('div', {
	class: 'right',
			},
	[
		E('div', {
			class: 'btn',
			click: ui.hideModal,
		},
			_('Close')),
	]),
		]);
	}
}

function displayStatus(type, content) {
	if (type) {
		var message = ui.showModal('', '');

		message.classList.add('alert-message');
		DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/));

		if (content)
			dom.content(message, content);
	} else {
		ui.hideModal();
	}
}

return view.extend({
	load: function() {
		return Promise.all([
			L.resolveDefault(callPackagelist(), {}),
			L.resolveDefault(callSystemBoard(), {}),
			uci.load('attendedsysupgrade'),
		]);
	},
	render: function(res) {
		var packages = res[0].packages;
		var system_board = res[1];
		var auto_search =
	uci.get_first('attendedsysupgrade', 'client', 'auto_search') || 1;
		var server_url = uci.get_first('attendedsysupgrade', 'server', 'url');

		var view = [
			E('h2', _('Attended Sysupgrade')),
			E('p',
	_('The attended sysupgrade service allows to easily upgrade vanilla and custom firmware images.')),
			E('p',
	_('This is done by building a new firmware on demand via an online service.')),
		];

		if (auto_search == 1) {
			check_sysupgrade(server_url, system_board, packages);
		}

		view.push(E('p', {
			class: 'btn cbi-button-positive',
			click:
		function() { check_sysupgrade(server_url, system_board, packages); },
		},
		_('Search for sysupgrade')));

		return view;
	},
});