let dlg = null;

class ServerConfig {
    /** @type string */ Protocol;
    /** @type string */ Hostname;
    /** @type string */ Port;
    /** @type string */ Path;
    /** @type string */ Query;
    /** @type boolean */ UseSSL;

    /**
     * 
     * @param {string} protocol 
     * @param {string} hostname 
     * @param {string} port 
     * @param {string} path 
     * @param {string} query 
     * @param {boolean} useSSL 
     */
    constructor(protocol, hostname, port, path, query, useSSL)
    {
        this.Protocol = protocol;
        this.Hostname = hostname
        this.Port = port;
        this.Path = path;
        this.Query = query;
        this.UseSSL = useSSL;
    }

    url() {
        const uri = `${this.Protocol}${this.UseSSL ? 's' : ''}://${this.Hostname}${this.Port ? ':'+this.Port : ''}/${this.Path}${this.Query}`;
        return uri;
    }
}

backupApp.service('BackupConnectionService', function($location, $rootScope, $timeout, $cookies, AppService, AppUtils, BackupList, CaptchaService, DialogService, EditUriBackendConfig, gettextCatalog)
{

    class Tester {

        /** @type ServerConfig */
        ServerConfig;
        /** @type number */
        BackupID;
        /** @type boolean */
        HasTriedHostkey;


        /**
         * 
         * @param {ServerConfig} serverConfig 
         * @param {number} backupID 
         */
        constructor(serverConfig, backupID)
        {
            this.ServerConfig = serverConfig;
            this.BackupID = backupID;
            this.HasTriedHostkey = false;
        }

        async createFolder() {
            await AppService.post('/remoteoperation/create', this.ServerConfig.url());
        }

        async handleError(data, check_hostKey = true) {

            if (dlg != null)
                dlg.dismiss();
    
            var message = data.statusText;
            if (!this.HasTriedHostkey && message.indexOf('incorrect-host-key:') == 0) {
                var re = /incorrect-host-key\s*:\s*"([^"]*)"(,\s*accepted-host-key\s*:\s*"([^"]*)")?/;
                var m = re.exec(message);
                var key = null;
                var prev = null;
                if (m != null) {
                    key = m[1] || '';
                    prev = m[3] || '';
                }
    
                if ((key || '').trim().length == 0 || key == prev) {
                    if (data.data != null && data.data.Message != null)
                        message = data.data.Message;
                    DialogService.errorDialog(gettextCatalog.getString('Error'), gettextCatalog.getString('Failed to connect: ') + message);
                }
                else {
                    this.HasTriedHostkey = true;
                    let oldQueryHasSshFingerprint = false;
                    const oldQuery = this.ServerConfig.Query.split('&').map(q => {
                        const part = q.split('=');
                        const param = { key: part[0], value: part[1] }
                        if (!oldQueryHasSshFingerprint && param.key == 'ssh-fingerprint')
                        {
                            oldQueryHasSshFingerprint = true;
                            param.value = encodeURIComponent(key)
                        }
                        return param;
                    })

                    if (!oldQueryHasSshFingerprint)
                    {
                        oldQuery.push({ key: 'ssh-fingerprint', value: encodeURIComponent(key) });
                    }
                    

                    this.ServerConfig.Query = oldQuery.map(p => `${p.key}=${p.value}`).join('&');
                    return await this.testConnection();
                }
            }
            else {
                if (data.data != null && data.data.Message != null)
                    message = data.data.Message;
                DialogService.errorDialog(gettextCatalog.getString('Error'), gettextCatalog.getString('Failed to connect: ') + message);
            }

            return false;
        }

        async handleResponse(data, ensureCreateFolder) {
            dlg.dismiss();

            if (!data.data.FolderExists && this.BackupID == null && ensureCreateFolder) {
                try {
                    await this.createFolder();
                    const result = await this.testConnection();
                    if (result.success) {
                        return await this.handleResponse(result.data)
                    }
                    return await this.handleError(result.data)
                } catch (e) {
                    return await this.handleError(e)
                }
            }
            else if (!data.data.FolderExists && this.BackupID > 0) {
                DialogService.errorDialog(
                    gettextCatalog.getString('Error'),
                    gettextCatalog.getString('The backup has been wrongly setup (the targeted folder is not accessible). Try to delete it and recreate it.'),
                )
                return false;
            }
            else if (data.data.FolderExists && this.BackupID == null && ensureCreateFolder) {
                DialogService.errorDialog(
                    gettextCatalog.getString('Error'),
                    gettextCatalog.getString('The Folder already exists and used by another job.')
                )
                return false;
            }

            DialogService.dialog(
                gettextCatalog.getString('Success'),
                gettextCatalog.getString('Connection worked!'),
                [gettextCatalog.getString('Finish')]
            );
            return true;
        }

        testConnection() {

            return new Promise( (resolve, reject) => {
                /** Tester */
                const self = this;
                if (dlg != null)
                    dlg.dismiss();

                    dlg = DialogService.loadingDialog(
                        function () {
                            AppService.post('/remoteoperation/test', self.ServerConfig.url())
                                .then((data) => {
                                    // dlg.dismiss();this
                                    resolve({success:true, data})
                                }, (data) => {
                                    // dlg.dismiss();
                                    resolve({success:false, data})
                                });
                        }
                    );
            });

        }
    }

    

    this.checkFolderExist = async function(serverConfig) {
              
        const tester = new Tester(serverConfig, null);
        const result = await tester.testConnection();

        function serverSuccessResponse(data)
        {
            dlg.dismiss();
            if (data.data.FolderExists){
                DialogService.dialog(gettextCatalog.getString('Success'), gettextCatalog.getString('Connection worked!'));
                return tester.ServerConfig.url();
            }
            
            DialogService.errorDialog(gettextCatalog.getString('Error'), gettextCatalog.getString('The Folder does not exists.'));
            throw new Error('server connection failed')
        }

        if (result.success) {
            return serverSuccessResponse(result.data)
        }
        else {
            
            const res = await tester.handleError(result.data);
            if (res.success)
                return serverSuccessResponse(res.data)

            await tester.handleError(res.data);

        }

        throw new Error('server connection failed')
    }

    this.performConnectionTest = async function(serverConfig, backupid, ensureCreateFolder = false)
    {
        const tester = new Tester(serverConfig, backupid);
        const result = await tester.testConnection();

        async function serverSuccessResponse(data)
        {
            const res = await tester.handleResponse(data, ensureCreateFolder);
            if (res)
            {
                return tester.ServerConfig.url();
            }
            throw new Error('server connection failed')
        }


        if (result.success) {
            return serverSuccessResponse(result.data);
        }
        else {
            const res = await tester.handleError(result.data, ensureCreateFolder);
            if (res && res.success)
            {
                return serverSuccessResponse(res.data);
            }
            await tester.handleError(res.data);
        }

        // return null;
        throw new Error('server connection failed')
    }

    this.buildUri = function(serverConfig, callback)
    {
        function validationCompleted() {
            if (EditUriBackendConfig.builders[serverConfig.Backend.Key] == null)
                callback(EditUriBackendConfig.defaultbuilder(serverConfig));
            else
                callback(EditUriBackendConfig.builders[serverConfig.Backend.Key](serverConfig));
        }

        if (EditUriBackendConfig.validaters[serverConfig.Backend.Key] == null)
            EditUriBackendConfig.defaultvalidater(serverConfig, validationCompleted);
        else
            EditUriBackendConfig.validaters[serverConfig.Backend.Key](serverConfig, validationCompleted);
    }
});