const bcrypt = require("bcrypt");
const AppError = require("../middleware/AppError.js");
const { USER_ALREADY_EXISTS } = require("../constants/errorCodes");

class Users {
  
  constructor(db, foldersModel) {
      this.db = db;
      this.folders = foldersModel;
  }

  async hashPassword(password) {
    return await bcrypt.hash(password, 10);
  }

  generatePassword() {
    return Math.random().toString(36).slice(-8);
  }

  async verify(password, hash) {
    return await bcrypt.compare(password, hash);
  }

  async register(userInfos) {
    await this.db.connect();
    const conn = this.db.getConnection();

    const userCollection = conn.collection("users");

    const existingUser = await userCollection.findOne({ email: userInfos.email });

    if (existingUser) {
      throw new AppError(USER_ALREADY_EXISTS);
    }

    let newUser = {
      name: userInfos.name ?? userInfos.email,
      email: userInfos.email,
      password: await this.hashPassword(userInfos.password),
      created_at: new Date(),
      roles: userInfos.roles
    };

    let created_user = await userCollection.insertOne(newUser);
    let user = await this.getById(created_user.insertedId)

    const folderTitle = "Dossier par Défaut";
    
    const userId = newUser._id ? newUser._id.toString() : 'x';
    await this.folders.create(folderTitle, userId);

    // TODO: verif if inserted properly...
    return user;
  }

  async login(userid) {
    await this.db.connect();
    const conn = this.db.getConnection();

    const userCollection = conn.collection("users");
    const user = await userCollection.findOne({ _id: userid });

    if (!user) {
      return false;
    }

    return user;
  }
/*
  async login(email, password) {
    try {
      await this.db.connect();
      const conn = this.db.getConnection();
      const userCollection = conn.collection("users");

      const user = await userCollection.findOne({ email: email });

      if (!user) {
        const error = new Error("User not found");
        error.statusCode = 404; 
        throw error;
      }

      const passwordMatch = await this.verify(password, user.password);

      if (!passwordMatch) {
        const error = new Error("Password does not match");
        error.statusCode = 401; 
        throw error;
      }

      return user;
    } catch (error) {
      console.error(error);
      throw error; 
    }
  }
*/
  async resetPassword(email) {
    const newPassword = this.generatePassword();

    return await this.changePassword(email, newPassword);
  }

  async changePassword(email, newPassword) {
    await this.db.connect();
    const conn = this.db.getConnection();

    const userCollection = conn.collection("users");

    const hashedPassword = await this.hashPassword(newPassword);

    const result = await userCollection.updateOne(
      { email },
      { $set: { password: hashedPassword } }
    );

    if (result.modifiedCount != 1) return null;

    return newPassword;
  }

  async delete(email) {
    await this.db.connect();
    const conn = this.db.getConnection();

    const userCollection = conn.collection("users");

    const result = await userCollection.deleteOne({ email });

    if (result.deletedCount != 1) return false;

    return true;
  }

  async getId(email) {
    await this.db.connect();
    const conn = this.db.getConnection();

    const userCollection = conn.collection("users");

    const user = await userCollection.findOne({ email: email });

    if (!user) {
      return false;
    }

    return user._id;
  }

  async getById(id) {
    await this.db.connect();
    const conn = this.db.getConnection();

    const userCollection = conn.collection("users");

    const user = await userCollection.findOne({ _id: id });

    if (!user) {
      return false;
    }

    return user;
  }

  async editUser(userInfo) {
    await this.db.connect();
    const conn = this.db.getConnection();

    const userCollection = conn.collection("users");

    const user = await userCollection.findOne({ _id: userInfo.id });

    if (!user) {
      return false;
    }

    const updatedFields = { ...userInfo };
    delete updatedFields.id;

    const result = await userCollection.updateOne(
      { _id: userInfo.id },
      { $set: updatedFields }
    );

    if (result.modifiedCount === 1) {
      return true;
    }

    return false;
  }
}

module.exports = Users;
