fcf.module({
  name: "fcf:NServer/NControllers/Login.js",
  dependencies: ["fcf:NServer/NControllers/Controller.js", "fcf:NAuthorization/authorizationHandler.js"],
  module: function(Controller, authorizationHandler){
    var Namespace = fcf.prepareObject(fcf, "packages/fcf/NServer/NControllers");

    class Login extends Controller{

      constructor(a_options) {
        super(a_options);
        this.userImportance = true;
      }

      action(a_request) {
        let self = this;

        if (fcf.empty(fcf.getContext().route.args.user)) {
          a_request.sendError(new fcf.Exception("FCF_LOGIN_NOT_SET_USER"));
          return;
        }

        if (fcf.empty(fcf.getContext().route.args.password)) {
          a_request.sendError(new fcf.Exception("FCF_LOGIN_NOT_SET_PASSWORD"));
          return;
        }

        let user     = fcf.getContext().route.args.user;
        let password = authorizationHandler.getDBPassword(fcf.getContext().route.args.password);

        fcf.actions()
        .then(()=>{
          let error = undefined;
          return fcf.application.getStorage().query({
            query: "SELECT id, password, active, initialized, number_login_attempts, last_login_attempt FROM ___fcf___users WHERE user = ${1}",
            args:  [user],
            roles: ["root"],
          })
          .then((a_records)=>{
            let config = fcf.application.getConfiguration();

            if (fcf.count(a_records[0]) < 1) {
              error = new fcf.Exception("FCF_LOGIN_INVALID_USER_OR_PASSWORD");
            } else if (a_records[0][0].password != password) {
              error = new fcf.Exception("FCF_LOGIN_INVALID_USER_OR_PASSWORD");
            } else if (!a_records[0][0].active) {
              error = new fcf.Exception("FCF_LOGIN_INVALID_USER_BLOCKED");
            } else if (!fcf.application.getConfiguration().loginUncreatedUser && !a_records[0][0].initialized) {
              error = new fcf.Exception("FCF_LOGIN_INVALID_USER_NOT_CREATED");
            }
            if (fcf.count(a_records[0]) &&
                config.numberInvalidLoginAttemptsBeforeWaiting && 
                config.numberInvalidLoginAttemptsBeforeWaiting < a_records[0][0].number_login_attempts &&
                ((new Date()).getTime() - (new Date(a_records[0][0].last_login_attempt)).getTime() ) < (config.waitingLogin*1000)
                ) {
              error = new fcf.Exception("FCF_LOGIN_TIMEOUT", {minutes: config.waitingLogin/60|0, seconds: config.waitingLogin%60 });
            }

            if (error) {
              if (fcf.count(a_records[0])) {
                let lastLoginAttempt = ((new Date()).getTime() - (new Date(a_records[0][0].last_login_attempt)).getTime() ) < (config.waitingLogin*1000)
                                          ? a_records[0][0].last_login_attempt 
                                          : fcf.dateFormat(new Date(), "Y-m-d H:i:s");
                return fcf.application.getStorage().query({
                  query: "UPDATE ___fcf___users SET number_login_attempts = ${1}, last_login_attempt = ${2} WHERE id = ${3}",
                  args:  [ a_records[0][0].number_login_attempts+1 , lastLoginAttempt , a_records[0][0].id ],
                  roles: ["root"],
                })
                .then(()=>{
                  throw error;
                });
              } else {
                throw error;
              }
            }

            return a_records[0][0];
          })
        })
        .then((a_userRecord)=>{
          return fcf.application.getStorage().query({
            query: "UPDATE ___fcf___users SET number_login_attempts = ${1}, last_login_attempt = ${2} WHERE id = ${3}",
            args:  [ 1, fcf.dateFormat(new Date(), "Y-m-d H:i:s") , a_userRecord.id ],
            roles: ["root"],
          })
          .then(()=>{
            return a_userRecord;
          })
        })
        .then((a_record)=>{
          let sessionId = fcf.randStr(64);
          let datetime  = fcf.dateFormat(new Date(), "Y-m-d H:i:s");
          return fcf.application.getStorage().query({
            query: "INSERT INTO ___fcf___sessions (session_id, user_id, start, last) VALUES(${1}, ${2}, ${3}, ${4})",
            args:  [ sessionId, a_record.id, datetime, datetime ],
            roles: ["root"],
          })
          .then(()=>{
            return sessionId;
          })
        })
        .then((a_sessionId)=>{
          fcf.getContext().session.id = a_sessionId;
          fcf.saveContext(a_request);
          a_request.send(a_sessionId);
        })
        .catch((a_error)=>{
          a_request.sendError(a_error);
        });
      }

    };

    Namespace.Login = Login;

    return Namespace.Login;
  }
});
