var OAuth = require("../src/authDataManager/OAuth1Client");
var request = require('request');

describe('OAuth', function() {

  it("Nonce should have right length", (done) => {
    jequal(OAuth.nonce().length, 30);
    done();
  });

  it("Should properly build parameter string", (done) => {
    var string = OAuth.buildParameterString({c:1, a:2, b:3})
    jequal(string, "a=2&b=3&c=1");
    done();
  });

  it("Should properly build empty parameter string", (done) => {
    var string = OAuth.buildParameterString()
    jequal(string, "");
    done();
  });

  it("Should properly build signature string", (done) => {
    var string = OAuth.buildSignatureString("get", "http://dummy.com", "");
    jequal(string, "GET&http%3A%2F%2Fdummy.com&");
    done();
  });

  it("Should properly generate request signature", (done) => {
    var request = {
      host: "dummy.com", 
      path: "path"
    };

    var oauth_params = {
      oauth_timestamp: 123450000,
      oauth_nonce: "AAAAAAAAAAAAAAAAA",
      oauth_consumer_key: "hello",
      oauth_token: "token"
    };

    var consumer_secret = "world";
    var auth_token_secret = "secret";
    request = OAuth.signRequest(request, oauth_params, consumer_secret, auth_token_secret);
    jequal(request.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="8K95bpQcDi9Nd2GkhumTVcw4%2BXw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"');
    done();
  });

  it("Should properly build request", (done) => {
    var options = {
      host: "dummy.com", 
      consumer_key: "hello",
      consumer_secret: "world",
      auth_token: "token",
      auth_token_secret: "secret",
      // Custom oauth params for tests
      oauth_params: {
        oauth_timestamp: 123450000,
        oauth_nonce: "AAAAAAAAAAAAAAAAA"
      }
    };
    var path = "path";
    var method = "get";

    var oauthClient = new OAuth(options);
    var req = oauthClient.buildRequest(method, path, {"query": "param"});

    jequal(req.host, options.host);
    jequal(req.path, "/"+path+"?query=param");
    jequal(req.method, "GET");
    jequal(req.headers['Content-Type'], 'application/x-www-form-urlencoded');
    jequal(req.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="wNkyEkDE%2F0JZ2idmqyrgHdvC0rs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"')
    done();
  });


  function validateCannotAuthenticateError(data, done) {
    jequal(typeof data, "object");
    jequal(typeof data.errors, "object");
    var errors = data.errors;
    jequal(typeof errors[0], "object");
    // Cannot authenticate error
    jequal(errors[0].code, 32);
    done();
  }

  it("Should fail a GET request", (done) => {
    var options = {
      host: "api.twitter.com", 
      consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
      consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    };
    var path = "/1.1/help/configuration.json";
    var params = {"lang": "en"};
    var oauthClient = new OAuth(options);
    oauthClient.get(path, params).then(function(data){
      validateCannotAuthenticateError(data, done);
    })
  });

  it("Should fail a POST request", (done) => {
    var options = {
      host: "api.twitter.com", 
      consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
      consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    };
    var body = {
      lang: "en"
    };
    var path = "/1.1/account/settings.json";

    var oauthClient = new OAuth(options);
    oauthClient.post(path, null, body).then(function(data){
      validateCannotAuthenticateError(data, done);
    })
  });

  it("Should fail a request", (done) => {
    var options = {
      host: "localhost", 
      consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
      consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
    };
    var body = {
      lang: "en"
    };
    var path = "/";

    var oauthClient = new OAuth(options);
    oauthClient.post(path, null, body).then(function(data){
      jequal(false, true);
      done();
    }).catch(function(){
      jequal(true, true);
      done();
    })
  });

  ["facebook", "github", "instagram", "google", "linkedin", "meetup", "twitter"].map(function(providerName){
    it("Should validate structure of "+providerName, (done) => {
      var provider = require("../src/authDataManager/"+providerName);
      jequal(typeof provider.validateAuthData, "function");
      jequal(typeof provider.validateAppId, "function");
      jequal(provider.validateAuthData({}, {}).constructor, Promise.prototype.constructor); 
      jequal(provider.validateAppId("app", "key", {}).constructor, Promise.prototype.constructor); 
      done();
    });
  });

  var getMockMyOauthProvider = function() {
    return {
      authData: {
        id: "12345",
        access_token: "12345",
        expiration_date: new Date().toJSON(),
      },
      shouldError: false,
      loggedOut: false,
      synchronizedUserId: null,
      synchronizedAuthToken: null,
      synchronizedExpiration: null,

      authenticate: function(options) {
        if (this.shouldError) {
          options.error(this, "An error occurred");
        } else if (this.shouldCancel) {
          options.error(this, null);
        } else {
          options.success(this, this.authData);
        }
      },
      restoreAuthentication: function(authData) {
        if (!authData) {
          this.synchronizedUserId = null;
          this.synchronizedAuthToken = null;
          this.synchronizedExpiration = null;
          return true;
        }
        this.synchronizedUserId = authData.id;
        this.synchronizedAuthToken = authData.access_token;
        this.synchronizedExpiration = authData.expiration_date;
        return true;
      },
      getAuthType: function() {
        return "myoauth";
      },
      deauthenticate: function() {
        this.loggedOut = true;
        this.restoreAuthentication(null);
      }
    };
  };

  var ExtendedUser = Parse.User.extend({
    extended: function() {
      return true;
    }
  });

  var createOAuthUser = function(callback) {
    var jsonBody = {
      authData: {
        myoauth: getMockMyOauthProvider().authData
      }
    };
    var headers = {'X-Parse-Application-Id': 'test',
          'X-Parse-REST-API-Key': 'rest', 
          'Content-Type': 'application/json' }

    var options = {
        headers: {'X-Parse-Application-Id': 'test',
          'X-Parse-REST-API-Key': 'rest', 
          'Content-Type': 'application/json' },
        url: 'http://localhost:8378/1/users',
        body: JSON.stringify(jsonBody)
      };

    return request.post(options, callback);
  }

  it("should create user with REST API", (done) => {

    createOAuthUser((error, response, body) => {
        expect(error).toBe(null);
        var b = JSON.parse(body);
        expect(b.objectId).not.toBeNull();
        expect(b.objectId).not.toBeUndefined();
        done();
      });

  });

  it("should only create a single user with REST API", (done) => {
    var objectId;
    createOAuthUser((error, response, body) => {
        expect(error).toBe(null);
        var b = JSON.parse(body);
        expect(b.objectId).not.toBeNull();
        expect(b.objectId).not.toBeUndefined();
        objectId = b.objectId;

        createOAuthUser((error, response, body) => {
          expect(error).toBe(null);
          var b = JSON.parse(body);
          expect(b.objectId).not.toBeNull();
          expect(b.objectId).not.toBeUndefined();
          expect(b.objectId).toBe(objectId);
          done();
        });
      });
    
  });

  it("unlink and link with custom provider", (done) => {
    var provider = getMockMyOauthProvider();
    Parse.User._registerAuthenticationProvider(provider);
    Parse.User._logInWith("myoauth", {
      success: function(model) {
        ok(model instanceof Parse.User, "Model should be a Parse.User");
        strictEqual(Parse.User.current(), model);
        ok(model.extended(), "Should have used the subclass.");
        strictEqual(provider.authData.id, provider.synchronizedUserId);
        strictEqual(provider.authData.access_token, provider.synchronizedAuthToken);
        strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration);
        ok(model._isLinked("myoauth"), "User should be linked to myoauth");

        model._unlinkFrom("myoauth", {
          success: function(model) {
            ok(!model._isLinked("myoauth"),
               "User should not be linked to myoauth");
            ok(!provider.synchronizedUserId, "User id should be cleared");
            ok(!provider.synchronizedAuthToken, "Auth token should be cleared");
            ok(!provider.synchronizedExpiration,
               "Expiration should be cleared");

            model._linkWith("myoauth", {
              success: function(model) {
                ok(provider.synchronizedUserId, "User id should have a value");
                ok(provider.synchronizedAuthToken,
                   "Auth token should have a value");
                ok(provider.synchronizedExpiration,
                   "Expiration should have a value");
                ok(model._isLinked("myoauth"),
                   "User should be linked to myoauth");
                done();
              },
              error: function(model, error) {
                ok(false, "linking again should succeed");
                done();
              }
            });
          },
          error: function(model, error) {
            ok(false, "unlinking should succeed");
            done();
          }
        });
      },
      error: function(model, error) {
        ok(false, "linking should have worked");
        done();
      }
    });
  });


})