import * as bcrypt from "bcrypt";
import { FastifyReply, FastifyRequest } from "fastify";
import { N_ITEMS_PER_PAGE, SALT_ROUNDS } from "../../utils/consts";
import prisma from "../../utils/prisma";
import {
  NotificationQueryStringSchema,
  OtherInformationSchema,
  PasswordSchema,
  ProfileSchema,
} from "./schema";
import { stringToInt } from "../../utils/string";
import { IdParamsSchema } from "../../utils/schema";

// ------------------------------------------------------------------------------------

export async function currentUser(req: FastifyRequest, reply: FastifyReply) {
  const userId = req.user.id;

  const fullUser = await prisma.user.findUnique({
    where: { id: userId, active: true },
    include: {
      company: {
        include: {
          city: true,
          contactPosition: true,
          nEmployees: true,
          turnover: true,
        },
      },
      position: true,
      city: true,
    },
  });

  if (!fullUser) {
    return reply.code(401).send({
      message: "Cet utilisateur n'existe pas",
    });
  }

  if (fullUser.company && !fullUser.company.active) {
    return reply.code(401).send({
      message: "Votre entreprise a été desactivé par la CCIS",
    });
  }

  await prisma.user.update({
    where: { id: userId },
    data: { loggedInAt: new Date() },
  });

  const nNotifications = await prisma.notification.count({
    where: { isRead: false, userId },
  });

  const { password, ...user } = { ...fullUser, nNotifications };

  return reply.code(200).send(user);
}

// ------------------------------------------------------------------------------------

export async function getNotifications(
  req: FastifyRequest<{ Querystring: NotificationQueryStringSchema }>,
  reply: FastifyReply
) {
  const userId = req.user.id;
  const page = stringToInt(req.query.page) ?? 0;
  const skipedRows = N_ITEMS_PER_PAGE * page;

  const nNotifications = await prisma.notification.count({
    where: { userId },
  });
  const nPages = Math.ceil(nNotifications / N_ITEMS_PER_PAGE);

  const notifications = await prisma.notification.findMany({
    where: { userId },
    take: N_ITEMS_PER_PAGE,
    skip: skipedRows,
    orderBy: {
      createdAt: "desc",
    },
  });

  return reply.code(200).send({ page, nPages, notifications });
}

// ------------------------------------------------------------------------------------

export async function readNotification(
  req: FastifyRequest<{ Params: IdParamsSchema }>,
  reply: FastifyReply
) {
  const userId = req.user.id;
  const id = req.params.id;

  try {
    await prisma.notification.update({
      where: {
        userId: userId,
        id: id,
      },
      data: {
        isRead: true,
      },
    });
  } catch {
    return reply.code(404).send({
      message: "La notification n'existe pas",
    });
  }

  return reply.code(200).send({});
}

// ------------------------------------------------------------------------------------

export async function editPersonalInformation(
  req: FastifyRequest<{ Body: ProfileSchema }>,
  reply: FastifyReply
) {
  const userId = req.user.id;
  const user = await prisma.user.findUnique({
    where: { id: userId, active: true },
  });

  if (!user) {
    return reply.code(404).send({
      message: "Cet utilisateur n'existe pas",
    });
  }

  const data = req.body;

  const city = await prisma.city.findUnique({
    where: {
      id: data.cityId,
    },
  });

  if (!city) {
    return reply.code(400).send({
      message: "La ville n'existe pas",
    });
  }

  const position = await prisma.position.findUnique({
    where: {
      id: data.positionId,
    },
  });

  if (!position) {
    return reply.code(400).send({
      message: "La fonction n'existe pas",
    });
  }

  const newUserFull = await prisma.user.update({
    where: { id: userId },
    data: {
      civility: data.civility,
      firstName: data.firstName,
      lastName: data.lastName,
      cityId: data.cityId,
      positionId: data.positionId,
      phoneNumber: data.phoneNumber,
    },
  });

  const { password, ...newUser } = newUserFull;
  return reply.code(200).send(newUser);
}

// ------------------------------------------------------------------------------------

export async function editOtherInformation(
  req: FastifyRequest<{ Body: OtherInformationSchema }>,
  reply: FastifyReply
) {
  const userId = req.user.id;
  const user = await prisma.user.findUnique({
    where: { id: userId, active: true },
  });

  if (!user) {
    return reply.code(404).send({
      message: "Cet utilisateur n'existe pas",
    });
  }

  const data = req.body;

  const newUserFull = await prisma.user.update({
    where: { id: userId },
    data: {
      bio: data.bio,
      expertiseAreas: data.expertiseAreas,
      interestAreas: data.interestAreas,
    },
  });

  const { password, ...newUser } = newUserFull;
  return reply.code(200).send(newUser);
}

// ------------------------------------------------------------------------------------

export async function editPassword(
  req: FastifyRequest<{ Body: PasswordSchema }>,
  reply: FastifyReply
) {
  const userId = req.user.id;
  const user = await prisma.user.findUnique({
    where: { id: userId, active: true },
  });

  if (!user) {
    return reply.code(404).send({
      message: "Cet utilisateur n'existe pas",
    });
  }

  const data = req.body;

  const isMatch = await bcrypt.compare(data.old_password, user.password);

  if (!isMatch) {
    return reply.code(400).send({
      message: "L'ancien mot de passe est incorrect",
    });
  }

  const hash = await bcrypt.hash(data.new_password, SALT_ROUNDS);

  await prisma.user.update({
    where: { id: userId },
    data: {
      password: hash,
    },
  });

  return reply.code(200).send({});
}
