'use strict';

/* global $, tld, angular, Clipboard */

// create main application module
var app = angular.module('Application', ['pascalprecht.translate', 'ngCookies', 'angular-md5', 'ui-notification', 'ui.bootstrap']);

app.filter('zoneName', function () {
    return function (domain) {
        return tld.getDomain(domain);
    };
});

app.controller('SetupDNSController', ['$scope', '$http', '$timeout', 'Client', function ($scope, $http, $timeout, Client) {
    var search = decodeURIComponent(window.location.search).slice(1).split('&').map(function (item) { return item.split('='); }).reduce(function (o, k) { o[k[0]] = k[1]; return o; }, {});

    $scope.state = null; // 'initialized', 'waitingForDnsSetup', 'waitingForBox'
    $scope.error = {};
    $scope.provider = '';
    $scope.showDNSSetup = false;
    $scope.instanceId = '';
    $scope.isDomain = false;
    $scope.isSubdomain = false;
    $scope.advancedVisible = false;
    $scope.webServerOrigin = '';
    $scope.clipboardDone = false;
    $scope.search = window.location.search;
    $scope.setupToken = '';

    $scope.tlsProvider = [
        { name: 'Let\'s Encrypt Prod', value: 'letsencrypt-prod' },
        { name: 'Let\'s Encrypt Prod - Wildcard', value: 'letsencrypt-prod-wildcard' },
        { name: 'Let\'s Encrypt Staging', value: 'letsencrypt-staging' },
        { name: 'Let\'s Encrypt Staging - Wildcard', value: 'letsencrypt-staging-wildcard' },
        { name: 'Self-Signed', value: 'fallback' }, // this is not 'Custom' because we don't allow user to upload certs during setup phase
    ];

    $scope.sysinfo = {
        provider: 'generic',
        ip: '',
        ifname: ''
    };

    $scope.sysinfoProvider = [
        { name: 'Public IP', value: 'generic' },
        { name: 'Static IP Address', value: 'fixed' },
        { name: 'Network Interface', value: 'network-interface' }
    ];

    $scope.prettySysinfoProviderName = function (provider) {
        switch (provider) {
        case 'generic': return 'Public IP';
        case 'fixed': return 'Static IP Address';
        case 'network-interface': return 'Network Interface';
        default: return 'Unknown';
        }
    };

    $scope.needsPort80 = function (dnsProvider, tlsProvider) {
        return ((dnsProvider === 'manual' || dnsProvider === 'noop' || dnsProvider === 'wildcard') &&
            (tlsProvider === 'letsencrypt-prod' || tlsProvider === 'letsencrypt-staging'));
    };

    // If we migrate the api origin we have to poll the new location
    if (search.admin_fqdn) Client.apiOrigin = 'https://' + search.admin_fqdn;

    $scope.$watch('dnsCredentials.domain', function (newVal) {
        if (!newVal) {
            $scope.isDomain = false;
            $scope.isSubdomain = false;
        } else if (!tld.getDomain(newVal) || newVal[newVal.length-1] === '.') {
            $scope.isDomain = false;
            $scope.isSubdomain = false;
        } else {
            $scope.isDomain = true;
            $scope.isSubdomain = tld.getDomain(newVal) !== newVal;
        }
    });

    // keep in sync with domains.js
    $scope.dnsProvider = [
        { name: 'AWS Route53', value: 'route53' },
        { name: 'Cloudflare', value: 'cloudflare' },
        { name: 'DigitalOcean', value: 'digitalocean' },
        { name: 'Gandi LiveDNS', value: 'gandi' },
        { name: 'GoDaddy', value: 'godaddy' },
        { name: 'Google Cloud DNS', value: 'gcdns' },
        { name: 'Linode', value: 'linode' },
        { name: 'Name.com', value: 'namecom' },
        { name: 'Namecheap', value: 'namecheap' },
        { name: 'Netcup', value: 'netcup' },
        { name: 'Wildcard', value: 'wildcard' },
        { name: 'Manual (not recommended)', value: 'manual' },
        { name: 'No-op (only for development)', value: 'noop' }
    ];
    $scope.dnsCredentials = {
        busy: false,
        domain: '',
        accessKeyId: '',
        secretAccessKey: '',
        gcdnsKey: { keyFileName: '', content: '' },
        digitalOceanToken: '',
        gandiApiKey: '',
        cloudflareEmail: '',
        cloudflareToken: '',
        cloudflareTokenType: 'GlobalApiKey',
        godaddyApiKey: '',
        godaddyApiSecret: '',
        linodeToken: '',
        nameComUsername: '',
        nameComToken: '',
        namecheapUsername: '',
        namecheapApiKey: '',
        netcupCustomerNumber: '',
        netcupApiKey: '',
        netcupApiPassword: '',
        provider: 'route53',
        zoneName: '',
        tlsConfig: {
            provider: 'letsencrypt-prod-wildcard'
        }
    };

    $scope.setDefaultTlsProvider = function () {
        var dnsProvider = $scope.dnsCredentials.provider;
        // wildcard LE won't work without automated DNS
        if (dnsProvider === 'manual' || dnsProvider === 'noop' || dnsProvider === 'wildcard') {
            $scope.dnsCredentials.tlsConfig.provider = 'letsencrypt-prod';
        } else {
            $scope.dnsCredentials.tlsConfig.provider = 'letsencrypt-prod-wildcard';
        }
    };


    function readFileLocally(obj, file, fileName) {
        return function (event) {
            $scope.$apply(function () {
                obj[file] = null;
                obj[fileName] = event.target.files[0].name;

                var reader = new FileReader();
                reader.onload = function (result) {
                    if (!result.target || !result.target.result) return console.error('Unable to read local file');
                    obj[file] = result.target.result;
                };
                reader.readAsText(event.target.files[0]);
            });
        };
    }

    document.getElementById('gcdnsKeyFileInput').onchange = readFileLocally($scope.dnsCredentials.gcdnsKey, 'content', 'keyFileName');

    $scope.setDnsCredentials = function () {
        $scope.dnsCredentials.busy = true;
        $scope.error = {};

        var provider = $scope.dnsCredentials.provider;

        var config = {};

        if (provider === 'route53') {
            config.accessKeyId = $scope.dnsCredentials.accessKeyId;
            config.secretAccessKey = $scope.dnsCredentials.secretAccessKey;
        } else if (provider === 'gcdns') {
            try {
                var serviceAccountKey = JSON.parse($scope.dnsCredentials.gcdnsKey.content);
                config.projectId = serviceAccountKey.project_id;
                config.credentials = {
                    client_email: serviceAccountKey.client_email,
                    private_key: serviceAccountKey.private_key
                };

                if (!config.projectId || !config.credentials || !config.credentials.client_email || !config.credentials.private_key) {
                    throw new Error('One or more fields are missing in the JSON');
                }
            } catch (e) {
                $scope.error.dnsCredentials = 'Cannot parse Google Service Account Key: ' + e.message;
                $scope.dnsCredentials.busy = false;
                return;
            }
        } else if (provider === 'digitalocean') {
            config.token = $scope.dnsCredentials.digitalOceanToken;
        } else if (provider === 'gandi') {
            config.token = $scope.dnsCredentials.gandiApiKey;
        } else if (provider === 'godaddy') {
            config.apiKey = $scope.dnsCredentials.godaddyApiKey;
            config.apiSecret = $scope.dnsCredentials.godaddyApiSecret;
        } else if (provider === 'cloudflare') {
            config.email = $scope.dnsCredentials.cloudflareEmail;
            config.token = $scope.dnsCredentials.cloudflareToken;
            config.tokenType = $scope.dnsCredentials.cloudflareTokenType;
        } else if (provider === 'linode') {
            config.token = $scope.dnsCredentials.linodeToken;
        } else if (provider === 'namecom') {
            config.username = $scope.dnsCredentials.nameComUsername;
            config.token = $scope.dnsCredentials.nameComToken;
        } else if (provider === 'namecheap') {
            config.token = $scope.dnsCredentials.namecheapApiKey;
            config.username = $scope.dnsCredentials.namecheapUsername;
        } else if (provider === 'netcup') {
            config.customerNumber = $scope.dnsCredentials.netcupCustomerNumber;
            config.apiKey = $scope.dnsCredentials.netcupApiKey;
            config.apiPassword = $scope.dnsCredentials.netcupApiPassword;
        }

        var tlsConfig = {
            provider: $scope.dnsCredentials.tlsConfig.provider,
            wildcard: false
        };
        if ($scope.dnsCredentials.tlsConfig.provider.indexOf('-wildcard') !== -1) {
            tlsConfig.provider = tlsConfig.provider.replace('-wildcard', '');
            tlsConfig.wildcard = true;
        }

        var sysinfoConfig = {
            provider: $scope.sysinfo.provider
        };
        if ($scope.sysinfo.provider === 'fixed') {
            sysinfoConfig.ip = $scope.sysinfo.ip;
        } else if ($scope.sysinfo.provider === 'network-interface') {
            sysinfoConfig.ifname = $scope.sysinfo.ifname;
        }

        var data = {
            dnsConfig: {
                domain: $scope.dnsCredentials.domain,
                zoneName: $scope.dnsCredentials.zoneName,
                provider: provider,
                config: config,
                tlsConfig: tlsConfig
            },
            sysinfoConfig: sysinfoConfig,
            providerToken: $scope.instanceId,
            setupToken: $scope.setupToken
        };

        Client.setup(data, function (error) {
            if (error) {
                $scope.dnsCredentials.busy = false;
                if (error.statusCode === 422) {
                    if (provider === 'ami') {
                        $scope.error.ami = error.message;
                    } else {
                        $scope.error.setup = error.message;
                    }
                } else {
                    $scope.error.dnsCredentials = error.message;
                }
                return;
            }

            waitForDnsSetup();
        });
    };

    function waitForDnsSetup() {
        $scope.state = 'waitingForDnsSetup';

        Client.getStatus(function (error, status) {
            if (!error && !status.setup.active) {
                if (!status.adminFqdn || status.setup.errorMessage) { // setup reset or errored. start over
                    $scope.error.setup = status.setup.errorMessage;
                    $scope.state = 'initialized';
                    $scope.dnsCredentials.busy = false;
                } else { // proceed to activation
                    window.location.href = 'https://' + status.adminFqdn + '/setup.html' + (window.location.search);
                }
                return;
            }

            $scope.message = status.setup.message;

            setTimeout(waitForDnsSetup, 5000);
        });
    }

    function initialize() {
        Client.getStatus(function (error, status) {
            if (error) {
                // During domain migration, the box code restarts and can result in getStatus() failing temporarily
                console.error(error);
                $scope.state = 'waitingForBox';
                return $timeout(initialize, 3000);
            }

            // domain is currently like a lock flag
            if (status.adminFqdn) return waitForDnsSetup();

            if (status.provider === 'digitalocean' || status.provider === 'digitalocean-mp') {
                $scope.dnsCredentials.provider = 'digitalocean';
            // don't suggest linode by default since it takes a while for DNS to propagate
            } else if (status.provider === 'linode' || status.provider === 'linode-oneclick' || status.provider === 'linode-stackscript') {
                $scope.dnsCredentials.provider = 'linode';
            } else if (status.provider === 'gce') {
                $scope.dnsCredentials.provider = 'gcdns';
            } else if (status.provider === 'ami') {
                $scope.dnsCredentials.provider = 'route53';
            }

            $scope.instanceId = search.instanceId;
            $scope.setupToken = search.setupToken;
            $scope.provider = status.provider;
            $scope.webServerOrigin = status.webServerOrigin;
            $scope.state = 'initialized';

            setTimeout(function () { $("[autofocus]:first").focus(); }, 100);
        });
    }

    var clipboard = new Clipboard('.clipboard');
    clipboard.on('success', function () {
        $scope.$apply(function () { $scope.clipboardDone = true; });
        $timeout(function () { $scope.clipboardDone = false; }, 5000);
    });

    initialize();
}]);
