'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) {
        console.log(response)
        ui.showModal(_('Error connecting to upgrade server'), [
                E('p', {}, _(`Could not reach API at "${response.url}. Please try again later.`)),
                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('div', {
                                                        'class': 'tr'
                                                }, [
                                                        E('div', {
                                                                'class': 'td left',
                                                                'width': '33%'
                                                        }, [fields[i]]),
                                                        E('div', {
                                                                'class': 'td left'
                                                        }, [(fields[i + 1] != null) ? fields[i + 1] : '?'])
                                                ]));
                                        }

                                        var modal_body = [
                                                table,
                                                E('p', {}, 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 %d'.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 ${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 ${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;
        },

});
