/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

const {utils: Cu} = Components;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://services-common/utils.js");
Cu.import("resource://testing-common/httpd.js");

XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsMgmtService",
  "resource://gre/modules/FxAccountsMgmtService.jsm",
  "FxAccountsMgmtService");

XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsManager",
  "resource://gre/modules/FxAccountsManager.jsm");

// At end of test, restore original state
const ORIGINAL_AUTH_URI = Services.prefs.getCharPref("identity.fxaccounts.auth.uri");
var { SystemAppProxy } = Cu.import("resource://gre/modules/FxAccountsMgmtService.jsm");
const ORIGINAL_SENDCUSTOM = SystemAppProxy._sendCustomEvent;
do_register_cleanup(function() {
  Services.prefs.setCharPref("identity.fxaccounts.auth.uri", ORIGINAL_AUTH_URI);
  SystemAppProxy._sendCustomEvent = ORIGINAL_SENDCUSTOM;
});

// Make profile available so that fxaccounts can store user data
do_get_profile();

// Mock the system app proxy; make message passing possible
var mockSendCustomEvent = function(aEventName, aMsg) {
  Services.obs.notifyObservers({wrappedJSObject: aMsg}, aEventName, null);
};

function run_test() {
  run_next_test();
}

add_task(function test_overall() {
  do_check_neq(FxAccountsMgmtService, null);
});

// Check that invalid email capitalization is corrected on signIn.
// https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#post-v1accountlogin
add_test(function test_invalidEmailCase_signIn() {
  do_test_pending();
  let clientEmail = "greta.garbo@gmail.com";
  let canonicalEmail = "Greta.Garbo@gmail.COM";
  let attempts = 0;

  function writeResp(response, msg) {
    if (typeof msg === "object") {
      msg = JSON.stringify(msg);
    }
    response.bodyOutputStream.write(msg, msg.length);
  }

  // Mock of the fxa accounts auth server, reproducing the behavior of
  // /account/login when email capitalization is incorrect on signIn.
  let server = httpd_setup({
    "/account/login": function(request, response) {
      response.setHeader("Content-Type", "application/json");
      attempts += 1;

      // Ensure we don't get in an endless loop
      if (attempts > 2) {
        response.setStatusLine(request.httpVersion, 429, "Sorry, you had your chance");
        writeResp(response, {});
        return;
      }

      let body = CommonUtils.readBytesFromInputStream(request.bodyInputStream);
      let jsonBody = JSON.parse(body);
      let email = jsonBody.email;

      // The second time through, the accounts client will call the api with
      // the correct email capitalization.
      if (email == canonicalEmail) {
        response.setStatusLine(request.httpVersion, 200, "Yay");
        writeResp(response, {
          uid: "your-uid",
          sessionToken: "your-sessionToken",
          keyFetchToken: "your-keyFetchToken",
          verified: true,
          authAt: 1392144866,
        });
        return;
      }

      // If the client has the wrong case on the email, we return a 400, with
      // the capitalization of the email as saved in the accounts database.
      response.setStatusLine(request.httpVersion, 400, "Incorrect email case");
      writeResp(response, {
        code: 400,
        errno: 120,
        error: "Incorrect email case",
        email: canonicalEmail,
      });
      return;
    },
  });

  // Point the FxAccountsClient's hawk rest request client to the mock server
  Services.prefs.setCharPref("identity.fxaccounts.auth.uri", server.baseURI);

  // Receive a mozFxAccountsChromeEvent message
  function onMessage(subject, topic, data) {
    let message = subject.wrappedJSObject;

    switch (message.id) {
      // When we signed in as "Greta.Garbo", the server should have told us
      // that the proper capitalization is really "greta.garbo".  Call
      // getAccounts to get the signed-in user and ensure that the
      // capitalization is correct.
      case "signIn":
        FxAccountsMgmtService.handleEvent({
          detail: {
            id: "getAccounts",
            data: {
              method: "getAccounts",
            }
          }
        });
        break;

      // Having initially signed in as "Greta.Garbo", getAccounts should show
      // us that the signed-in user has the properly-capitalized email,
      // "greta.garbo".
      case "getAccounts":
        Services.obs.removeObserver(onMessage, "mozFxAccountsChromeEvent");

        do_check_eq(message.data.email, canonicalEmail);

        do_test_finished();
        server.stop(run_next_test);
        break;

      // We should not receive any other mozFxAccountsChromeEvent messages
      default:
        do_throw("wat!");
        break;
    }
  }

  Services.obs.addObserver(onMessage, "mozFxAccountsChromeEvent", false);

  SystemAppProxy._sendCustomEvent = mockSendCustomEvent;

  // Trigger signIn using an email with incorrect capitalization
  FxAccountsMgmtService.handleEvent({
    detail: {
      id: "signIn",
      data: {
        method: "signIn",
        email: clientEmail,
        password: "123456",
      },
    },
  });
});

add_test(function testHandleGetAssertionError_defaultCase() {
  do_test_pending();

  FxAccountsManager.getAssertion(null).then(
    success => {
      // getAssertion should throw with invalid audience
      ok(false);
    },
    reason => {
      equal("INVALID_AUDIENCE", reason.error);
      do_test_finished();
      run_next_test();
    }
  )
});

// End of tests
// Utility functions follow

function httpd_setup (handlers, port=-1) {
  let server = new HttpServer();
  for (let path in handlers) {
    server.registerPathHandler(path, handlers[path]);
  }
  try {
    server.start(port);
  } catch (ex) {
    dump("ERROR starting server on port " + port + ".  Already a process listening?");
    do_throw(ex);
  }

  // Set the base URI for convenience.
  let i = server.identity;
  server.baseURI = i.primaryScheme + "://" + i.primaryHost + ":" + i.primaryPort;

  return server;
}


