angular.module('com.daplie.cloud', [ 'org.oauth3' ])
  .service('oauth3', [ 'Oauth3', function (Oauth3) {
    // for security this app should not store the refresh token
    // (the localhost.* domains should never store them)
    Oauth3.hooks.session._store = {};
    Oauth3.hooks.session._get = function (providerUri) {
      return Oauth3.PromiseA.resolve(Oauth3.hooks.session._store[providerUri]);
    };
    Oauth3.hooks.session._set = function (providerUri, session) {
      Oauth3.hooks.session._store[providerUri] = session;
      return Oauth3.PromiseA.resolve(session);
    };
    var auth = Oauth3.create();
    auth.setProvider('oauth3.org').then(function () {
      auth.checkSession().then(function (session) {
        console.log('hasSession?', session);
      });
    });

    window.oauth3 = auth; // debug
    return auth;
  } ])
  .controller('LoginController', [ '$scope', '$timeout', 'oauth3', function ($scope, $timeout, oauth3) {
    var vm = this;
    var OAUTH3 = window.OAUTH3;

    vm.admin = { network: { iface: null, ipv4: null, ipv6: null } };
    vm.admin.ipify = 'https://api.ipify.org?format=json';

    vm.clientUri = OAUTH3.clientUri(window.location);

    vm.setSimple = function () {
      vm.advanced = false;
      vm.providerUri = vm.providerUri || 'oauth3.org';
    };

    vm.setAdvanced = function () {
      vm.advanced = true;
      vm.myProviderUri = vm.providerUri;
    };

    vm.checkProviderUri = function (myProviderUri) {
      $timeout.cancel(vm.checkProviderTimeout);

      vm.providerUri = null;
      vm.checkProviderTimeout = $timeout(function () {
        //var providerUri = vm.providerUri;

        return oauth3.setProvider(myProviderUri).then(function (directives) {
          console.log('directives', directives);
          vm.providerUri = myProviderUri;
        }, function (err) {
          console.error('failed provider lookup', err);
          vm.checkProviderTimeout = null;
        });
      }, 250);
    };

    vm.sortDnsRecords = function (a, b) {
      if (a.sld !== b.sld) {
        return a.sld > b.sld ? 1 : -1;
      }
      if (a.tld !== b.tld) {
        return a.tld > b.tld ? 1 : -1;
      }
      // TODO normalize
      a.sub = a.sub || '';
      b.sub = b.sub || '';
      if (a.sub !== b.sub) {
        if (!a.sub) {
          return -1;
        }
        if (!b.sub) {
          return 1;
        }
        return a.sub > b.sub ? 1 : -1;
      }
      if (a.domain !== b.domain) {
        if (!a.domain) {
          return 1;
        }
        if (!b.domain) {
          return -1;
        }
      }
    };

    vm.viewDomains = function (config, domains, dns) {
      vm.dns = dns.slice(0);
      vm.domains = domains.slice(0);
      vm.domains.sort(vm.sortDnsRecords);
      vm.dns = vm.dns.filter(function (record) {
        if (-1 === [ 'A', 'AAAA', 'ANAME' ].indexOf(record.type)) {
          return false;
        }
        if (record.device !== config.device.hostname) {
          return false;
        }
        return true;
      });
      vm.dns.forEach(function (r) {
        vm.domains.forEach(function (d) {
          if (r.zone === d.domain) {
            r.sub = r.name.substr(0, r.name.length - (d.domain.length + 1));
            r.tld = d.tld;
            r.sld = d.sld;
          }
        });
      });
      vm.dns = vm.dns.concat(vm.domains);
      vm.dns.sort(vm.sortDnsRecords);
      vm.dns.forEach(function (r) {
        if (r.domain) {
          return;
        }
        r.devices = r.devices || [ r.device ];

        dns.forEach(function (r2) {
          if (r.name !== r2.name) {
            return;
          }
          if (-1 !== r.devices.indexOf(r2.device)) {
            return;
          }
          r.devices.push(r2.device);
        });
      });
      console.log('vm.dns');
      console.log(vm.dns);
      /*
      vm.domains.forEach(function (d) {
        d.devices = [];
        dns.forEach(function (r) {
          // 0 === r.name.split('').reverse().join('').indexOf(d.domain.split('').reverse().join(''))
          if (r.zone === d.domain) {
            d.devices.push({
              name: r.device
            });
          }
        });
      });
      */
    };

    vm.authenticate = function () {
      // TODO authorization redirect /api/org.oauth3.consumer/authorization_redirect/:provider_uri
      var opts = {
        type: 'popup'
      , scope: 'domains,dns'
      // , debug: true
      };

      return oauth3.authenticate(opts).then(function (session) {
        console.info("Authorized Session", session);

        return oauth3.api('domains.list').then(function (domains) {
          console.info("domains owned", domains);

          return oauth3.api('dns.list').then(function (dns) {
            console.info("dns records", dns);

            return OAUTH3.request({
              method: 'POST'
            , url: 'https://' + vm.clientUri + '/api/com.daplie.goldilocks/init'
            , session: session
            , data: {
                access_token: session.access_token
              , refresh_token: session.refresh_token
              , expires_in: session.expires_in
              , scope: session.scope
              , provider_uri: OAUTH3.uri.normalize(session.provider_uri)
              , client_uri: vm.clientUri
              , domains: domains.map(function (d) {
                  return {
                    id: d.id
                  , sub: d.sub
                  , sld: d.sld
                  , tld: d.tld
                  };
                })
              , jwk: null // TODO publish public key
              }
            }).then(function (resp) {
              // TODO resp should contain a token
              console.info('Initialized Goldilocks', resp);
              return OAUTH3.request({
                method: 'GET'
              , url: 'https://' + vm.clientUri + '/api/com.daplie.goldilocks/config'
              , session: session
              }).then(function (configResp) {
                console.log('config', configResp.data);
                vm.config = configResp.data;
                vm.config.addresses.forEach(function (addr) {
                  if (/unicast/.test(addr.range) || /internet/i.test(addr.range)) {
                    // TODO
                    // there could be more than one iface (ipv4 on one and ipv6 on the other)
                    // there could also be more than one valid public ip

                    if ("IPv4" === addr.family) {
                      vm.admin.network.iface = addr.iface;
                      vm.admin.network.ipv4 = addr.address;
                    }
                    else if ("IPv6" === addr.family) {
                      // IPv6 gives valid internet address even when behind a firewall
                      // IPv6 requires a loopback test
                      // TODO always do the loopback / firewall test anyway
                      vm.admin.network.ipv6 = addr.address;
                    }
                  }
                });

                vm.admin.servername = vm.config.device.hostname;
                //vm.config.ifaces
                //vm.config.addresses = [];
                vm.viewDomains(vm.config, domains, dns);

                if (vm.admin.network.iface) {
                  return resp;
                }

                // TODO try UPnP IGD / NAT-PMP / DNS-SD / PCP (ipv6) here
                // TODO two requests - one to {{ipv4.ipify.org}}, the other to {{ipv6.ipify.org}}
                vm.admin.network.iface = 'gateway';
                return OAUTH3.request({
                  method: 'POST'
                , url: 'https://' + vm.clientUri + '/api/com.daplie.goldilocks/request'
                , session: session
                , data: {
                    method: 'GET'
                  , url: 'https://api.ipify.org?format=json'
                  }
                }).then(function (ipResp) {
                  console.log('ip-response', ipResp);
                  vm.admin.network.ipv4 = ipResp.data.body.ipv4 || ipResp.data.body.ip;
                  vm.admin.network.ipv6 = ipResp.data.body.ipv6;

                  return resp;
                });
              });
            }, function (err) {
              console.error(err);
              window.alert("Initialization failed:" + err.message);
            });
          });
        });
      }, function (err) {
        console.error(err);
        window.alert("Authentication failed:" + err.message);
      });
    };

    vm.enableTunnel = function (/*opts*/) {
      return oauth3.request({
        method: 'POST'
      , url: 'https://' + vm.clientUri + '/api/com.daplie.goldilocks/tunnel'
      }).then(function (result) {
        // vm.admin.network.iface = 'oauth3-tunnel';
        return result;
      });
    };

    /*
    console.log('OAUTH3.PromiseA', OAUTH3.PromiseA);
    return oauth3.setProvider('oauth3.org').then(function () {
      return oauth3.authenticate({ windowType: 'background' }).then(function () {
        console.log('HELLO!!');
        //vm.authnUpdated = Date.now();
        vm.hasSession = true;
      }, function () {
        console.log('GOODBYE!!');
        //vm.authnUpdated = Date.now();
        vm.hasSession = false;
        $timeout(function () {
          console.log('GOODBYE!!');
          vm.hello = 'Nope!';
        }, 1);
      });
    });
    //*/

  }]);
/*
$(function () {
'use strict';


var ui = {
function login() {
}
};


var auth = window.OAUTH3.create();
// TODO put explicit in dns record
// TODO CCA record
auth.setProvider('oauth3.org');

$('body').on('click', '.js-login', login);

});
*/