import clientPromise, { objectId, database } from "@/db.config";
import { normalizeMongoDoc } from "./helper";

export const getUserByUsername = async (username) => {
  const client = await clientPromise;
  const db = client.db(database);
  return await db.collection("users").findOne({ username: username });
};

export const getUserById = async (id, projection = null) => {
  const client = await clientPromise;
  const db = client.db(database);
  const result = await db
    .collection("users")
    .findOne({ _id: objectId(id) }, { projection });
  return normalizeMongoDoc(result);
};

export const getUserBridge = async () => {
  const client = await clientPromise;
  const db = client.db(database);
  return await db.collection("users").findOne({ username: "masterbridge" });
};

export const createUser = async (data) => {
  const client = await clientPromise;
  const db = client.db(database);
  const result = await db.collection("users").insertOne(data);

  // get data
  const response = await db
    .collection("users")
    .findOne({ _id: objectId(result.insertedId) });

  return normalizeMongoDoc(response);
};

export const updateUserById = async (id, data) => {
  const client = await clientPromise;
  const db = client.db(database);
  const result = await db
    .collection("users")
    .updateOne({ _id: objectId(id) }, { $set: data });

  const response = await db
    .collection("users")
    .findOne({ _id: objectId(id) }, { projection: { password: 0 } });

  return normalizeMongoDoc(response);
};

export const getAllUsers = async () => {
  const client = await clientPromise;
  const db = client.db(database);
  const result = await db
    .collection("users")
    .find({}, { projection: { password: 0 } })
    .toArray();

  return normalizeMongoDoc(result);
};

export const getAllUserExcept = async (exceptIds = []) => {
  const client = await clientPromise;
  const db = client.db(database);
  const result = await db
    .collection("users")
    .find(
      { _id: { $nin: exceptIds.map((id) => objectId(id)) } },
      { projection: { password: 0 } }
    )
    .toArray();

  return normalizeMongoDoc(result);
};

export const deleteUserById = async (id) => {
  const client = await clientPromise;
  const db = client.db(database);
  const users = db.collection("users");

  const deleted = await users.findOne({ _id: objectId(id) });
  const result = await users.deleteOne({ _id: objectId(id) });
  if (result.deletedCount === 0) return null;

  return normalizeMongoDoc(deleted);
};

export const searchUser = async (keyword) => {
  const client = await clientPromise;
  const db = client.db(database);
  const result = await db
    .collection("users")
    .aggregate([
      {
        $match: {
          $or: [
            { username: { $regex: keyword, $options: "i" } },
            { name: { $regex: keyword, $options: "i" } },
          ],
        },
      },
      {
        $project: { password: 0 },
      },
    ])
    .toArray();

  return normalizeMongoDoc(result);
};

export const updateUserStatus = async (_id, status) => {
  const client = await clientPromise;
  const db = client.db(database);

  const result = await db.collection("users").findOneAndUpdate(
    { _id: objectId(_id) },
    { $set: { status: status } },
    {
      returnDocument: "after",
    }
  );

  return normalizeMongoDoc(result);
};

export const deleteTokenExpired = async () => {
  const client = await clientPromise;
  const db = client.db(database);
  return await db
    .collection("tokens")
    .deleteMany({ expiredAt: { $lt: Date.now() } });
};

export const getAllToken = async () => {
  const client = await clientPromise;
  const db = client.db(database);

  // delete first
  await deleteTokenExpired();

  const result = await db
    .collection("tokens")
    .aggregate([
      {
        $lookup: {
          from: "users",
          localField: "userId",
          foreignField: "_id",
          pipeline: [
            {
              $project: { password: 0 },
            },
          ],
          as: "user",
        },
      },
      { $unwind: "$user" },
    ])
    .toArray();

  return normalizeMongoDoc(result);
};

export const getTokenByUserId = async (id) => {
  const client = await clientPromise;
  const db = client.db(database);
  return await db.collection("tokens").findOne({ userId: objectId(id) });
};

export const getTokenBy = async (filter) => {
  const client = await clientPromise;
  const db = client.db(database);
  return await db.collection("tokens").findOne(filter);
};

export const createToken = async (data) => {
  const client = await clientPromise;
  const db = client.db(database);
  const result = await db.collection("tokens").insertOne(data);

  // get indexes
  const indexes = await db.collection("tokens").listIndexes().toArray();
  if (!indexes.map((i) => i.name).includes("expireIn_1"))
    await db
      .collection("tokens")
      .createIndex({ expireIn: 1 }, { expireAfterSeconds: 0 });

  return normalizeMongoDoc(result);
};

export const updateTokenByUserId = async (id, data) => {
  const client = await clientPromise;
  const db = client.db(database);
  return await db
    .collection("tokens")
    .updateOne({ userId: objectId(id) }, { $set: data });
};

export const updateTokenBy = async (filter, data) => {
  const client = await clientPromise;
  const db = client.db(database);
  return await db.collection("tokens").updateOne(filter, { $set: data });
};

export const deleteTokenByUserId = async (id) => {
  const client = await clientPromise;
  const db = client.db(database);
  return await db.collection("tokens").deleteOne({ userId: objectId(id) });
};

export const deleteTokenByUID = async (uid) => {
  const client = await clientPromise;
  const db = client.db(database);
  return await db.collection("tokens").deleteOne({ uid: uid });
};

export const revokeTokenById = async (id) => {
  const client = await clientPromise;
  const db = client.db(database);
  const result = await db
    .collection("tokens")
    .updateOne({ _id: objectId(id) }, { $set: { revoke: true } });

  return normalizeMongoDoc(result);
};

export const grantTokenById = async (id) => {
  const client = await clientPromise;
  const db = client.db(database);
  const result = await db
    .collection("tokens")
    .updateOne({ _id: objectId(id) }, { $set: { revoke: false } });

  return normalizeMongoDoc(result);
};

export const searchToken = async (keyword) => {
  const client = await clientPromise;
  const db = client.db(database);
  const result = await db
    .collection("tokens")
    .aggregate([
      {
        $lookup: {
          from: "users",
          localField: "userId",
          foreignField: "_id",
          pipeline: [
            {
              $project: { password: 0 },
            },
          ],
          as: "user",
        },
      },
      { $unwind: "$user" },
      {
        $match: {
          $or: [{ "user.name": { $regex: keyword, $options: "i" } }],
        },
      },
    ])
    .toArray();

  return normalizeMongoDoc(result);
};
