/*
  User.ino

  This is a new module handling user systems on ESP8266

*/

//Check if a user login is valid by username and password
bool UserCheckAuth(String username, String password) {
  username.trim();
  password.trim();
  //Load user info from db
  if (!DBKeyExists("user", username)) {
    return false;
  }

  String userHashedPassword = DBRead("user", username);  //User hashed password from kvdb
  String enteredHashedPassword = sha1(password);         //Entered hashed password
  return userHashedPassword.equals(enteredHashedPassword);
}

//Get the username from the request, return empty string if unable to resolve
String GetUsernameFromRequest(AsyncWebServerRequest *r) {
  if (r->hasHeader("Cookie")) {
    //User cookie from browser
    String authCookie = GetCookieValueByKey(r, "web-auth");
    if (authCookie == "") {
      return "";
    }

    //Check if this is admin login
    if (authCookie.equals(authSession)) {
      return adminUsername;
    }

    //Check if user login
    if (DBKeyExists("sess", authCookie)) {
      //Return the username of this session
      return DBRead("sess", authCookie);
    }

    //Not found
    return "";
  }

  //This user have no cookie in header
  return "";
}

//Create new user, creator must be admin
void HandleNewUser(AsyncWebServerRequest *r) {
  if (!IsAdmin(r)) {
    SendErrorResp(r, "this function require admin permission");
    return;
  }
  String username = GetPara(r, "username");
  String password = GetPara(r, "password");
  username.trim();
  password.trim();

  //Check if the inputs are valid
  if (username == "" || password == "") {
    SendErrorResp(r, "username or password is an empty string");
    return;
  } else if (password.length() < 8) {
    SendErrorResp(r, "password must contain at least 8 characters");
    return;
  }

  //Check if the user already exists
  if (DBKeyExists("user", username)) {
    SendErrorResp(r, "user with name: " + username + " already exists");
    return;
  }

  //OK create the user
  bool succ = DBWrite("user", username, sha1(password));
  if (!succ) {
    SendErrorResp(r, "write new user to database failed");
    return;
  }
  r->send(200, "application/json", "\"OK\"");
}

//Remove the given username from the system
void HandleRemoveUser(AsyncWebServerRequest *r) {
  if (!IsAdmin(r)) {
    SendErrorResp(r, "this function require admin permission");
    return;
  }

  String username = GetPara(r, "username");
  username.trim();

  //Check if the user exists
  if (!DBKeyExists("user", username)) {
    SendErrorResp(r, "user with name: " + username + " not exists");
    return;
  }

  //Okey, remove the user
  bool succ = DBRemove("user", username);
  if (!succ) {
    SendErrorResp(r, "remove user from system failed");
    return;
  }
  r->send(200, "application/json", "\"OK\"");
}

//Admin or the user themselve change password for the account
void HandleUserChangePassword(AsyncWebServerRequest *r) {
  //Get requesting username
  if (!IsUserAuthed(r)) {
    SendErrorResp(r, "user not logged in");
    return;
  }

  String currentUser = GetUsernameFromRequest(r);
  if (currentUser == "") {
    SendErrorResp(r, "unable to load user from system");
    return;
  }

  //Check if the user can change password
  //note that admin password cannot be changed on-the-fly
  //admin password can only be changed in SD card config file
  String modifyingUsername = GetPara(r, "username");
  String newPassword = GetPara(r, "newpw");
  modifyingUsername.trim();
  newPassword.trim();

  if (modifyingUsername == adminUsername) {
    SendErrorResp(r, "admin username can only be changed in the config file");
    return;
  }
  if (currentUser == adminUsername || modifyingUsername == currentUser) {
    //Allow modify
    if (newPassword.length() < 8) {
      SendErrorResp(r, "password must contain at least 8 characters");
      return;
    }

    //Write to database
    bool succ = DBWrite("user", modifyingUsername, sha1(newPassword));
    if (!succ) {
      SendErrorResp(r, "write new user to database failed");
      return;
    }
    SendOK(r);

  } else {
    SendErrorResp(r, "permission denied");
    return;
  }

  SendOK(r);
}

//Get the current username
void HandleGetUserinfo(AsyncWebServerRequest *r){
  if (!HandleAuth(r)) {
    return;
  }

  String isAdmin = "false";
  if (IsAdmin(r)){
    isAdmin = "true";
  }

  String username = GetUsernameFromRequest(r);
  r->send(200, "application/json", "{\"username\":\"" + username + "\", \"admin\":" + isAdmin + "}");
}

//List all users registered in this WebStick
void HandleUserList(AsyncWebServerRequest *r) {
  if (!HandleAuth(r)) {
    return;
  }

  //Build the json with brute force
  String jsonString = "[";
  //As the DB do not support list, it directly access the root of the folder where the kvdb stores the entries
  File root = SD.open(DB_root + "user/");
  bool firstObject = true;
  if (root) {
    while (true) {
      File entry = root.openNextFile();
      if (!entry) {
        // No more files
        break;
      } else {
        //There are more lines. Add a , to the end of the previous json object
        if (!firstObject) {
          jsonString = jsonString + ",";
        } else {
          firstObject = false;
        }

        //Filter out all the directory if any
        if (entry.isDirectory()) {
          continue;
        }

        //Append to the JSON line
        jsonString = jsonString + "{\"Username\":\"" + entry.name() + "\"}";
      }
    }
  }
  jsonString += "]";

  r->send(200, "application/json", jsonString);
  
}
