import { FastifyReply, FastifyRequest } from "fastify";
import prisma from "../../utils/prisma";
import { getPresignedUrl, minioClient } from "../../utils/minio";
import { IdParamsSchema } from "../../utils/schema";
import { commissions } from "../settings/controller";
import { MinioImageSchema } from "../minio/schema";
import { bucketName, SALT_ROUNDS } from "../../utils/consts";
import { v4 as uuid } from "uuid";
import {
  EditElectedMemberPasswordSchema,
  EditElectedMemberSchema,
  ElectedMemberSchema,
  ElectedMemebrActiveSchema,
  EmailSchema,
  NotificationSchema,
  SmsSchema,
} from "./schema";
import { mail } from "../../utils/mail";
import { $Enums } from "@prisma/client";
import * as bcrypt from "bcrypt";
import axios from "axios";
import { releansApiKey } from "../../utils/env";

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

export async function getElectedMembers(
  req: FastifyRequest,
  reply: FastifyReply
) {
  const electedMembers = await prisma.user.findMany({
    where: {
      role: $Enums.Role.ElectedMember,
    },
    orderBy: {
      lastName: "asc",
    },
    select: {
      id: true,
      civility: true,
      firstName: true,
      lastName: true,
      email: true,
      active: true,
      phoneNumber: true,
      position: true,
      createdAt: true,
    },
  });

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

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

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

  const electedMember = await prisma.user.findUnique({
    where: {
      id: id,
      role: $Enums.Role.ElectedMember,
    },
    select: {
      id: true,
      civility: true,
      firstName: true,
      lastName: true,
      email: true,
      phoneNumber: true,

      positionId: true,
      position: true,

      active: true,
      createdAt: true,

      adresse: true,

      college: true,
      collegeId: true,
      province: true,
      provinceId: true,
      mandate: true,
      mandateId: true,

      commissions: true,
      details: true,
    },
  });

  if (!electedMember) {
    return reply.code(404).send({
      message: "Ce membre élu n'existe pas",
    });
  }

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

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

// export async function uploadElectedMemberImage(
//   req: FastifyRequest<{ Body: MinioImageSchema }>,
//   reply: FastifyReply
// ) {
//   const data = req.body;
//   const buffer = await data.image.toBuffer();
//   const ext = data.image.filename.split(".").pop();
//   const mimetype = data.image.mimetype;
//   const fileName = "elected-members/" + uuid() + "." + ext;
//   await minioClient.putObject(bucketName, fileName, buffer, undefined, {
//     "Content-Type": mimetype,
//   });
//   const presignedUrl = await getPresignedUrl(fileName);
//   return reply
//     .code(200)
//     .send({ image: fileName, presignedImage: presignedUrl });
// }

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

export async function createElectedMember(
  req: FastifyRequest<{
    Body: ElectedMemberSchema;
  }>,
  reply: FastifyReply
) {
  const data = req.body;

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

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

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

  if (!college) {
    return reply.code(404).send({
      message: "Le collège n'existe pas",
    });
  }

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

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

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

  if (!mandate) {
    return reply.code(404).send({
      message: "Le mandat n'existe pas",
    });
  }

  const user = await prisma.user.findUnique({
    where: {
      email: data.email.toLowerCase(),
    },
  });

  if (user) {
    return reply.code(400).send({
      message:
        "Un autre utilisateur/membre élu/fontionnaire avec cette adresse mail existe déjà",
    });
  }

  const hash = await bcrypt.hash(data.password, SALT_ROUNDS);
  const electedMember = await prisma.user.create({
    data: {
      civility: data.civility,
      firstName: data.firstName,
      lastName: data.lastName,

      positionId: data.positionId,

      phoneNumber: data.phoneNumber,
      adresse: data.adresse,

      email: data.email.toLowerCase(),
      password: hash,

      collegeId: data.collegeId,
      provinceId: data.provinceId,
      mandateId: data.mandateId,
      commissions: data.commissions,
      details: data.details,

      role: $Enums.Role.ElectedMember,
      bio: "",
    },
    select: {
      id: true,
      civility: true,
      firstName: true,
      lastName: true,
      email: true,
      active: true,
      phoneNumber: true,
      position: true,
      createdAt: true,
    },
  });

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

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

export async function editElectedMember(
  req: FastifyRequest<{
    Params: IdParamsSchema;
    Body: EditElectedMemberSchema;
  }>,
  reply: FastifyReply
) {
  const data = req.body;
  const id = req.params.id;

  const electedMember = await prisma.user.findUnique({
    where: {
      id: id,
      role: $Enums.Role.ElectedMember,
    },
  });

  if (!electedMember) {
    return reply.code(404).send({
      message: "Ce membre élu 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 college = await prisma.college.findUnique({
    where: {
      id: data.collegeId,
    },
  });

  if (!college) {
    return reply.code(404).send({
      message: "Le collège n'existe pas",
    });
  }

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

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

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

  if (!mandate) {
    return reply.code(404).send({
      message: "Le mandat n'existe pas",
    });
  }

  const nUsers = await prisma.user.count({
    where: {
      id: { not: electedMember.id },
      email: data.email.toLowerCase(),
    },
  });

  if (nUsers > 0) {
    return reply.code(400).send({
      message:
        "Un autre utilisateur/membre élu/fontionnaire avec cette adresse mail existe déjà",
    });
  }

  const newElectedMember = await prisma.user.update({
    where: {
      id: electedMember.id,
      role: $Enums.Role.ElectedMember,
    },
    data: {
      civility: data.civility,
      firstName: data.firstName,
      lastName: data.lastName,

      positionId: data.positionId,

      phoneNumber: data.phoneNumber,
      adresse: data.adresse,

      email: data.email.toLowerCase(),

      collegeId: data.collegeId,
      provinceId: data.provinceId,
      mandateId: data.mandateId,
      commissions: data.commissions,
      details: data.details,
    },
    select: {
      id: true,
      civility: true,
      firstName: true,
      lastName: true,
      email: true,
      active: true,
      phoneNumber: true,
      position: true,
      createdAt: true,
    },
  });

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

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

export async function editElectedMemebrActive(
  req: FastifyRequest<{
    Params: IdParamsSchema;
    Body: ElectedMemebrActiveSchema;
  }>,
  reply: FastifyReply
) {
  const id = req.params.id;
  const data = req.body;

  const electedMember = await prisma.user.findUnique({
    where: {
      id: id,
      role: $Enums.Role.ElectedMember,
    },
  });

  if (!electedMember) {
    return reply.code(404).send({
      message: "Ce membre élu n'existe pas",
    });
  }

  const newElectedMember = await prisma.user.update({
    where: { id: id, role: $Enums.Role.ElectedMember },
    data: {
      active: data.active,
    },
  });

  return reply.code(200).send({ active: newElectedMember.active });
}

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

export async function editElectedMemberPassword(
  req: FastifyRequest<{
    Params: IdParamsSchema;
    Body: EditElectedMemberPasswordSchema;
  }>,
  reply: FastifyReply
) {
  const id = req.params.id;
  const data = req.body;

  const electedMember = await prisma.user.findUnique({
    where: {
      id: id,
      role: $Enums.Role.ElectedMember,
    },
  });

  if (!electedMember) {
    return reply.code(404).send({
      message: "Ce memebre élu n'existe pas",
    });
  }

  const hash = await bcrypt.hash(data.password, SALT_ROUNDS);
  await prisma.user.update({
    where: { id: id },
    data: {
      password: hash,
    },
  });
  return reply.code(200).send({});
}

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

export async function sendNotification(
  req: FastifyRequest<{ Body: NotificationSchema }>,
  reply: FastifyReply
) {
  const data = req.body;

  const electedMembers = await prisma.user.findMany({
    where: {
      id: { in: data.electedMembers },
      role: $Enums.Role.ElectedMember,
    },
    select: { id: true },
  });

  await Promise.all(
    electedMembers.map(async (electedMember) => {
      await prisma.notification.create({
        data: {
          title: "Message de l'administration",
          content: data.content,
          userId: electedMember.id,
          source: $Enums.Source.Administration,
        },
      });
    })
  );

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

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

export async function sendEmail(
  req: FastifyRequest<{ Body: EmailSchema }>,
  reply: FastifyReply
) {
  const data = req.body;
  const electedMembers = await prisma.user.findMany({
    where: {
      id: { in: data.electedMembers },
      role: $Enums.Role.ElectedMember,
    },
    select: { email: true },
  });

  electedMembers.map((electedMember) => {
    const mailOptions = {
      from: process.env.EMAIL_HOST_USER,
      to: electedMember.email,
      subject: data.subject,
      html: data.body,
    };

    try {
      mail.sendMail(mailOptions);
    } catch {}
  });
  return reply.code(200).send({});
}

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

export async function sendSms(
  req: FastifyRequest<{ Body: SmsSchema }>,
  reply: FastifyReply
) {
  const data = req.body;

  const electedMembers = await prisma.user.findMany({
    where: {
      id: { in: data.electedMembers },
      role: $Enums.Role.ElectedMember,
    },
    select: { phoneNumber: true },
  });

  try {
    await Promise.all(
      electedMembers.map(async (electedMember) => {
        const phoneNumber = `+212${electedMember.phoneNumber.slice(1)}`;
        await axios.post(
          "https://api.releans.com/v2/message",
          {
            sender: "CCIS",
            mobile: phoneNumber,
            content: data.content,
          },
          {
            headers: {
              Authorization: `Bearer ${releansApiKey}`,
            },
          }
        );
      })
    );
  } catch {
    return reply.code(400).send({ message: "Erreur lors de l'envoie du SMS" });
  }

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

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

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

  const electedMember = await prisma.user.findUnique({
    where: {
      id: id,
      role: $Enums.Role.ElectedMember,
    },
  });

  if (!electedMember) {
    return reply.code(404).send({
      message: "Ce membre élu n'existe pas",
    });
  }

  try {
    await prisma.user.delete({
      where: { id: id },
    });
  } catch {
    return reply.code(400).send({
      message: "Vouis pouvez pas supprimer un membre élu",
    });
  }

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

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