"use strict";

const httpStatus = require("http-status");
const generateControllers = require("../../core/controllers");
const eventInscriptionService = require("./events_inscriptions.service");
const eventReservationService = require("./events_reservations.service");
const eventService = require("./event.service");
const mailService = require("./mail.service");
const marketingListService = require("./marketing_list.service");
const QRHelper = require("../../helpers/qr.helper");
const {
  extractParamsFromRequest,
  handleResultResponse,
  handleErrorResponse,
} = require("../../helpers/controller.helper");
const { data } = require("../../core/logger");
const lodash = require("lodash");
const userService = require("../auth/user.service");

// Module Name
const MODULE_NAME = "[eventInscription.controller]";
const controllerOptions = { MODULE_NAME };

async function refreshConfirmed(inscription) {
  if (!inscription) {
    throw new Error("Error al eliminar inscripción, no puedo cambiar confirmados a la reserva asociada");
  }

  if (inscription.type === "online") return true;

  //En caso de inscripciones
  const EventOrReservationChangeId = inscription.reservationId
    ? inscription.reservationId
    : inscription.overflowEventId
    ? inscription.overflowEventId
    : inscription.eventId;

  let NewConfirmed = 0;

  //Si la inscripción viene por una reserva modificamos los confirmados de la reserva
  if (inscription.reservationId != null) {
    console.log("Tengo reservation>>>>>>>>>>>>>>>>>>", inscription.reservationId);
    NewConfirmed = await eventInscriptionService._getCountInscriptionsWithReservation(EventOrReservationChangeId);
    //No se tienen en cuenta los marketinglist de las otras estructuras si lo tuviera seria esto
    // marketingListId = (await eventReservationService._getReservaById(EventOrReservationChangeId))
    //    .marketing_list;

    //Inscripcion de lista de espera bien de reserva o de evento de lista de espera
  } else if (inscription.overflowEventId != null) {
    console.log("Tengo overflow>>>>>>>>>>>>>>>>>>", inscription.overflowEventId);
    NewConfirmed = await eventInscriptionService._getCountInscriptionsWithOverflowEventId(EventOrReservationChangeId);
    //No se tienen en cuenta los marketinglist de las otras estructuras si lo tuviera seria esto
    ///  marketingListId = (await eventService._getEvent(EventOrReservationChangeId)).marketing_list;

    //Inscripción al evento (ni reserva ni lista de espera)
  } else if (inscription.eventId != null) {
    NewConfirmed = await eventInscriptionService._getCountInscriptionsWithoutReservationAndOverflow(
      EventOrReservationChangeId
    );

    //No se tienen en cuenta los marketinglist de las otras estructuras si lo tuviera seria esto
    //marketingListId = (await eventService._getEvent(EventOrReservationChangeId)).marketing_list;
  }

  //Actualizamos aforo del evento o de la reserva
  if (inscription.reservationId != null) {
    console.log(">>>>>>>>>>>>>>Voy a actualizar aforo reserva", EventOrReservationChangeId);
    console.log(">>>>>>>>>>>>>> ", NewConfirmed);
    if (!(await eventReservationService._updateConfirmedReservation(EventOrReservationChangeId, NewConfirmed))) {
      throw new Error("Error al eliminar inscripción, no puedo cambiar confirmados a la reserva asociada");
    }
  } else {
    console.log(">>>>>>>>>>>>>>Voy a actualizar aforo evento", EventOrReservationChangeId);
    console.log(">>>>>>>>>>>>>> ", NewConfirmed);
    if (!(await eventService._updateConfirmedEvent(EventOrReservationChangeId, NewConfirmed))) {
      throw new Error("Error al eliminar inscripción, no puedo cambiar confirmados a la inscripcion");
    }
  }
}

const extraControllers = {
  ///////////////////////////////////////////////////////////////////
  //Prepara la estructura de datos para el registro de inscripciones
  ///////////////////////////////////////////////////////////////////
  prepareDataInscription: async (req, res, next) => {
    const params = extractParamsFromRequest(req, res, {});

    //Si no viene type es porque es una inscripción con la app antigua y el valor por defecto es onsite
    let typeInscription = "onsite";
    if (req.body.type && req.body.type === "online") typeInscription = "online";

    //Si viene code es la appa antigua o la nueva
    if ((req.body.code && req.body.code !== "") || (req.body.group_size && req.body.group_size > 1))
      typeInscription = typeInscription + " group";

    // Si viene el usuario, el code viene directamente por que lo rellena la APP.
    // Si no viene el usuario, estamos por inscripciones web por lo que hay que tratarlo.
    let reservationCode = req.body.code;
    if (!req.user && req.body.code) {
       reservationCode = Buffer.from(req.body.code, "base64").toString("ascii").trim();
    }

    let dataInscription = {
      eventId: params.params.id,
      reservationCode,
      type: typeInscription,
      groupSize: req.body.group_size ? req.body.group_size : 1, //Si no viene group_size será uno porque es una inscripcion de la APP antigua
      source: req.user ? (lodash.isEmpty(req.user) ? "web4V" : "app") : "web", //En el caso de tener ya usuario, si es vacio viene de formulario WEBV4, si no por la APP, y en caso de no tener user el formulario web actual
      validated: false, //si no esta validado la inscripción es a la lista de espera
      inscriptionsWithoutReservationAndOverflowCount: null, //nº total de inscritos sin reserva y sin overflow asignada
      inscriptionsWithReservationCount: null, //nº total de inscritos a la reserva asignada
      event: null,
      reservation: null,
      inscription: null,
    };

    try {
      dataInscription.event = await eventService._getEvent(dataInscription.eventId);
      if (dataInscription.event) {
        dataInscription.event = await dataInscription.event.toJSON();
      } else {
        // No se ha encontrado
        return handleResultResponse("Evento no encontrado", null, params, res, httpStatus.NOT_FOUND);
      }
    } catch (error) {
      return handleErrorResponse(MODULE_NAME, "encontrado", error, res);
    }

    res.locals.dataInscription = dataInscription;
    console.log(">>>>>>>>>>>>>>>>>>>>  prepareDataInscription", dataInscription.type);
    next();
  },

  ////////////////////////////////////////////////////////////////////////////////
  //Esta función comprueba si el usuario ya tiene una inscripción para el evento
  //si es así se comprueba que no quiera cambiar de codigo de reserva y se actualiza
  ////////////////////////////////////////////////////////////////////////////////
  checkInscriptionByUser: async (req, res, next) => {
    const params = extractParamsFromRequest(req, res, {});
    let dataInscription = res.locals.dataInscription;
    if (!dataInscription || !dataInscription.event)
      return handleResultResponse(
        "Error checkInscriptionByUser, prepareDataInscription requerida",
        null,
        params,
        res,
        httpStatus.NOT_FOUND
      );
    let dataUser = res.locals.dataUser;
    if (!dataUser)
      return handleResultResponse(
        "Error checkInscriptionByUser, prepareDataInscription, getOrCreateUser requerida",
        null,
        params,
        res,
        httpStatus.NOT_FOUND
      );

    try {
      //Comprobamos que el usuario no tenga ya inscripcion para ese evento
      dataInscription.inscription = await eventInscriptionService._getInscriptionByEventAndUser(
        dataInscription.event.id,
        dataUser.userResult.user.id
      );

      if (dataInscription.inscription) {
        console.log("esta es la inscripcion que ya tengo>>>>", dataInscription.inscription);

        //Si la inscripcion no tiene reserva o la tiene y es la misma de la insripcion devuelvo la inscripcion
        if (
          !dataInscription.inscription.reservationId ||
          (dataInscription.reservation && dataInscription.inscription.reservationId == dataInscription.reservation.id)
        )
          return handleResultResponse(dataInscription.inscription, null, params, res, httpStatus.OK);

        //En caso contrario devuelvo la plaza a la reserva que tenia la inscripción anterior y apunto la inscripción a la nueva reserva
        //ACTUALIZAMOS LA RESERVA DE LA INSCRIPCION CON LA NUEVA Y CAMBIAMOS COMFIRMADOS DEVOLVIENDO LA INSCRIPCIÓN CON LA NUEVA RESERVA
        let CountConfirmedOldReservation = await eventInscriptionService._getCountInscriptionsWithReservation(
          dataInscription.inscription.reservationId
        );
        console.log("actualizo confirmados de la reserva anterior");
        await eventReservationService._updateConfirmedReservation(
          dataInscription.inscription.reservationId,
          --CountConfirmedOldReservation
        );

        let CountConfirmedNewReservation = await eventInscriptionService._getCountInscriptionsWithReservation(
          dataInscription.reservation.id
        );
        console.log("actualizo confirmados de la nueva reserva");
        await eventReservationService._updateConfirmedReservation(
          dataInscription.reservation.id,
          ++CountConfirmedNewReservation
        );
        await eventInscriptionService._updateReservationOfInscription(
          dataInscription.inscription.id,
          dataInscription.reservation.id
        );

        dataInscription.inscription = await eventInscriptionService._getInscriptionById(dataInscription.inscription.id);
        return handleResultResponse(dataInscription.inscription, null, params, res, httpStatus.OK);
      }
    } catch (error) {
      return handleResultResponse("Error checkInscriptionByUser", error, params, res, httpStatus.NOT_FOUND);
    }
    next();
  },

  getInscription: async (req, res, next) => {
    const params = extractParamsFromRequest(req, res, {});
    const inscriptionId = params.params.id;
    const userId = req.user.id;

    try {
      let inscription = await eventInscriptionService._getInscriptionById(inscriptionId);
      if (!inscription) {
        return handleResultResponse("Inscripción no encontrada", null, params, res, httpStatus.NOT_FOUND);
      };
      /* Se quita esta validación porque ya no se va a cumplir que solo se elimine la entrada por parte del porpio usuario desde la APP
      // ahora el formulario web AP4 podrá eliminar entradas de cualquier petición (protegido por apikey)
      else if (inscription.userId !== userId) {
        return handleResultResponse("Inscripción no encontrada", null, params, res, httpStatus.NOT_FOUND);
      }*/

      //console.log("inscripcion encontrada>>>>>>>>>>>>>>>>>>>>>>>>>>>", inscription);
      inscription = await inscription.toJSON();
      //console.log(">>>>>>>voy a dar inscription>>><", inscription.user);
      var member = marketingListService._generateMarketingDTO(inscription);
      member.qrConfig = QRHelper.generateQRConfig(member);
      inscription.code_ticket_qr = await QRHelper.getInscriptionQRCode(member.qrConfig);

      //Si el usuario de la inscripción no es tutor limpiamos la información de la reserva
      if (inscription.user.profile !== "tutor") if (inscription.reservation) inscription.reservation.assistants = null;

      if (inscription.reservation === null) {
        console.log("asigno green");
        inscription.color = "green";

        /* parche */
        inscription.reservation = {
          color: "green",
          description: "Entrada",
        };
      }

      //MAPEO SALIDA API4
      //Tratamos resultado, si hay que remover campos para API4 web
      if (res.locals.v4){
        /*const arrayFieldsremove = res.locals.v4.removeFields;
        arrayFieldsremove.forEach(campo => {
          if (campo in inscription) {
              delete inscription[campo];
          }
        });*/
        //En lugar de estar quitando campo creamos un DTO nuevo con la información que nos interesa
        const inscriptionResume = {
          id : inscription.id, 
          stateText : inscription.stateText, 
          code_ticket : inscription.code_ticket,           
          eventName : inscription.event.name,
          userName : inscription.user.name,
          userSurname : inscription.user.surname,
        };
        inscription = inscriptionResume;
      };

      console.log(">>>>>>>voy a dar inscription", inscription);
      return handleResultResponse(inscription, null, params, res, httpStatus.OK);
    } catch (error) {
      return handleResultResponse("Error al buscar la inscripción", null, params, res, httpStatus.NOT_FOUND);
    }
  },

  ////////////////////////////////////////////////////////////////////////////////
  //Esta función comprueba si el email ya tiene una inscripción para el evento
  ////////////////////////////////////////////////////////////////////////////////
  checkInscriptionByMail: async (req, res, next) => {
    const params = extractParamsFromRequest(req, res, {});
    var eventId = params.params.id;
    var email = params.params.email;

    console.log(">checkInscriptionByMail>>>>>>>>>>>>>>>>>>>>>>>>>>>>>", eventId, email);

    try {
      const user = await userService._getUserByEmail(email);
      if (user) {
        const result = await eventInscriptionService._getInscriptionByEventAndUser(eventId, user.id);
        if (result) return handleResultResponse(result.stateText, null, params, res, httpStatus.OK);
      }

      return handleResultResponse("No hay inscripción con ese email", null, params, res, httpStatus.NOT_FOUND);
    } catch (error) {
      return handleErrorResponse(MODULE_NAME, "checkInscription", error, res);
    }
  },

  ///////////////////////////////////////////////////////////////////
  //Esta función se llama solo desde APP
  //Inscripción sin CODIGO DE RESERVA, SE MODIFICA EL CONFIRMED DEL EVENTO, YA QUE SE DESCONTARA DEL AFORO DEL EVENTO
  // en caso de online no afectará a los aforos
  ///////////////////////////////////////////////////////////////////
  createInscription: async (req, res, next) => {
    console.log(">>>>>>>>>>>>>>>>>>>>  createInscription (event_inscriptions.controller)");
    const params = extractParamsFromRequest(req, res, {});

    let dataInscription = res.locals.dataInscription;
    if (!dataInscription || !dataInscription.event)
      return handleResultResponse("Error prepareDataInscription requerida", null, params, res, httpStatus.NOT_FOUND);

    let dataUser = res.locals.dataUser;
    if (!dataUser)
      return handleResultResponse("Error getOrCreateUser requerida", null, params, res, httpStatus.NOT_FOUND);

    if (dataInscription.reservation)
      return handleResultResponse(
        "Error existe una reserva por lo que debe llamar a createInscriptionReservation",
        null,
        params,
        res,
        httpStatus.NOT_FOUND
      );

    try {
      //ONLINE
      //Si es una inscripcion online no se validan aforos se crea inscripción y ya esta
      if (dataInscription.type === "online" || dataInscription.type === "online group") {
        //creamos inscripcion
        dataInscription.inscription = await eventInscriptionService._createInscription(
          dataInscription.event.id,
          dataUser.userResult.user.id,
          dataInscription.type,
          true, //validated,
          dataInscription.source,
          null,
          null
        );
        console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>ENTRADA ONLINE", dataInscription.inscription);
      }

      //ONSITE INDIVIDUAL
      if (!dataInscription.inscription) {
        let inscriptionsWithoutReservationAndOverflowCount =
          await eventInscriptionService._getCountInscriptionsWithoutReservationAndOverflow(dataInscription.event.id);
        ++inscriptionsWithoutReservationAndOverflowCount;

        console.log("Vamos a ver si hay aforo>>>>", inscriptionsWithoutReservationAndOverflowCount);
        //COMPROBAMOS SI ES VALIDO O HAY QUE APUNTARLE A LA LISTA DE ESPERA DEL EVENTO
        if (
          dataInscription.event.sold_out == 0 &&
          dataInscription.event.assistants >= inscriptionsWithoutReservationAndOverflowCount
        ) {
          dataInscription.validated = true;
          console.log("actualizamos afoorooo>>>");
          //Actualizamos aforo del evento y creamos inscripcion
          if (
            await eventService._updateConfirmedEvent(
              dataInscription.event.id,
              inscriptionsWithoutReservationAndOverflowCount
            )
          ) {
            try {
              dataInscription.inscription = await eventInscriptionService._createInscription(
                dataInscription.event.id,
                dataUser.id,
                dataInscription.type,
                dataInscription.validated,
                dataInscription.source,
                null,
                null
              );
            } catch (error) {
              console.log("SSSSSSSSSSSSSSSSSSSSSSSSS", error);
            }
          } else
            return handleResultResponse(
              "No se ha podido actualizar el aforo del evento",
              null,
              params,
              res,
              httpStatus.NOT_FOUND
            );
          console.log("Inscripcion hecha>>>", dataInscription.inscription);

          //Ponemos el evento en SOLD_OUT
          if (dataInscription.event.assistants == inscriptionsWithoutReservationAndOverflowCount)
            await eventService._updateSoldOutEvent(dataInscription.event.id, true);
        }

        // APUNTARSE A la lista de espera si se puede
        else {
          dataInscription.validated = false;

          if (dataInscription.event.allow_overflow === false) {
            console.log("Aforo completo y no hay lista de espera");
            return handleResultResponse(
              "Aforo completo y no hay lista de espera",
              null,
              params,
              res,
              httpStatus.NOT_FOUND
            );
          }

          //recuperamos la cantidad de apuntados al evento overflow (lista de espera)
          let ConfirmedWaitList = await eventInscriptionService._getCountInscriptionsWithOverflowEventId(
            dataInscription.event.overflow_eventId
          );
          //recuperamos aforo de la lista de espera
          dataInscription.overflow_event = await eventService._getEvent(dataInscription.event.overflow_eventId);
          console.log(
            "cantidad apuntados a lista de espera asociado, aforo >>>>>>>>>>>>>>>>>>>>>",
            ConfirmedWaitList,
            dataInscription.overflow_event.assistants
          );

          //Si no hay espacio a lista de espera damos el mismo error que no hay lista de espera
          if (dataInscription.overflow_event.assistants < ++ConfirmedWaitList) {
            console.log("Aforo completo de lista de espera");
            return handleResultResponse(
              "Aforo completo y no hay lista de espera",
              null,
              params,
              res,
              httpStatus.NOT_FOUND
            );
          }

          //Creamos inscripción a lista de espera
          if (await eventService._updateConfirmedEvent(dataInscription.event.overflow_eventId, ConfirmedWaitList)) {
            dataInscription.inscription = await eventInscriptionService._createInscription(
              dataInscription.event.id,
              dataUser.userResult.user.id,
              dataInscription.type,
              dataInscription.validated,
              dataInscription.source,
              null,
              dataInscription.event.overflow_eventId
            );
          } else {
            console.log("No se ha podido actualizar el aforo de la lista de espera del evento");
            return handleResultResponse(
              "No se ha podido actualizar el aforo de la lista de espera del evento",
              null,
              params,
              res,
              httpStatus.NOT_FOUND
            );
          }
        } //FIN APUNTARSE A la lista de espera si se puede
      }

      dataInscription.inscription = await dataInscription.inscription.toJSON();
      //Incluimos correo en sendinblue
      try {
        marketingListService.addMarketingList(dataUser, dataInscription);
      } catch (error) {
        console.log("Se ha producido un error al añadir a Hubspot >>>>>>>>>>>>>>>>>", error);
      }

      //Mandamos correo con entrada o lista de espera
      try {
        mailService.sendEmailConfirm(dataUser, dataInscription);
      } catch (error) {
        console.log("Se ha producido un error al enviar mail>>>>>>>>>>>>>>>>><<", error);
      }

      //MAPEO SALIDA API4
      //Tratamos resultado, si hay que remover campos para API4 web
      if (res.locals.v4) {
        const arrayFieldsremove = res.locals.v4.removeFields;
        arrayFieldsremove.forEach((campo) => {
          if (campo in dataInscription.inscription) {
            delete dataInscription.inscription[campo];
          }
        });
      }

      return handleResultResponse(await dataInscription.inscription, null, params, res, httpStatus.CREATED);
    } catch (Error) {
      return handleResultResponse(
        "Error al crear la incripción createInscription",
        null,
        params,
        res,
        httpStatus.NOT_FOUND
      );
    }
  },

  deleteInscription: async (req, res, next) => {
    const params = extractParamsFromRequest(req, res, {});
    const user = req.user;
    const inscriptionId = params.params.id;
    let marketingListId = null;

    try {
      const inscription = await eventInscriptionService._getInscriptionById(inscriptionId);
      console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>><Inscripcion a borrar:");
      console.log(inscription.id);

      //Solo se tiene en cuenta el marketinglist del evento
      marketingListId = inscription.event.marketing_list;

      //Si no existe la inscripcion damos error
      if (!inscription)
        return handleResultResponse("Inscription no encontrada", null, params, res, httpStatus.NOT_FOUND);

      //Si user{} quiere decir que lo llaman desde API4 para eliminar desde la web
      //Si no es vacio es desde la APP y tiene que cumplirse la comprobación, de hecho esto no seria posible si es asi damos error
      if (!lodash.isEmpty(user) && inscription.userId !== user.id)
        return handleResultResponse("Inscription no encontrada", null, params, res, httpStatus.NOT_FOUND);

      //En el caso de ser una inscripción grupal y el tutor se quiere quitar, se podrá hacer solo en el caso de que en la reserva no haya ya confirmados
      if (inscription.user.profile === "tutor" && inscription.reservation && inscription.reservation.confirmed > 1)
        return handleResultResponse(
          "No se pudo eliminar inscripción por ser tutor de grupo y tener alumnos apuntados, pongase en contacto con nosotros",
          null,
          params,
          res,
          httpStatus.NOT_FOUND
        );

      //Borramos inscripción
      if ((await eventInscriptionService._deleteInscription(inscription.id)) > 0) {
        console.log(">>>>>>>>>>>>>>>>>>>>>>>>>>>>><Inscripcion borrada");

        //En todos los casos en el que se elimine una inscripción hay que refrescar los confirmados de la reserva o del evento
        //en el caso de ser un tutor que ha realizado una reserva y solo está el confirmado, se borra y punto no se actualiza nada
        if (
          inscription.user.profile === "tutor" &&
          inscription.reservation &&
          inscription.reservation.confirmed === 1
        ) {
          //Eliminamos la reserva hecha del centro aliado
          if (!((await eventReservationService._deleteReservation(inscription.reservation.id)) > 0))
            return handleResultResponse(
              "No se pudo eliminar inscripción por ser tutor de grupo online y tener alumnos apuntados, pongase en contacto con nosotros",
              null,
              params,
              res,
              httpStatus.NOT_FOUND
            );

          if (inscription.reservation.state === "publish" && lodash.words(inscription.type).includes("onsite")) {
            const eventOfReservation = await eventService._getEvent(inscription.reservation.eventId);
            //Modificamos los asistentes de evento (AFORO) para añadir las plazas de la reserva eliminada
            const newAforo = eventOfReservation.assistants + inscription.reservation.assistants;
            if (!(await eventService._updateAssistantsEvent(eventOfReservation.id, newAforo)))
              return handleResultResponse(
                "No se ha podido actualizar el aforo del evento, para reservar las plazas solicitadas",
                null,
                params,
                res,
                httpStatus.NOT_FOUND
              );
          }
        }
        //En el caso de ser cualquier usuario o un tutor inscrito de forma libre o a una reserva de la que no es titular
        //Actualizamos confirmados asistentes
        else refreshConfirmed(inscription);
      } else return handleResultResponse("No se pudo eliminar inscripción", null, params, res, httpStatus.NOT_FOUND);

      //Quitamos correo en sendinblue
      try {
        await marketingListService._deleteMember(marketingListId, inscription.user.email, inscription);
      } catch (error) {
        console.log("Se ha producido un error al eliminar de SenINBlue>>>>>>>>>>>>>>>>><<", error);
      }

      //Mandamos correo de confirmación de eliminación
      try {
        mailService.sendEmailCancelate(inscription);
      } catch (error) {
        console.log("Se ha producido un error al enviar mail>>>>>>>>>>>>>>>>><<", error);
      }

      console.log(">>>>>>>>>>>>>>Inscripcion eliminada con todos los pasos");
      return handleResultResponse("Inscripción eliminada", null, params, res, httpStatus.DELETEOK);
    } catch (error) {
      console.log("eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeerrrrrrrrrrrrrrrrrrorrrr", error);
      return handleResultResponse("Error al eliminar inscripción", null, params, res, httpStatus.NOT_FOUND);
    }
  },

  deleteInscriptionsByUser: async (req, res, next) => {
    try {
      //const context = buildContext(req, config);
      //const user = context.user ? context.user : null;

      const user = {
        id: req.body.id,
        phone: req.body.phone,
        email: req.body.email,
      };

      await eventInscriptionService._deleteInscriptionByUser(user.id);

      next();
    } catch (error) {
      console.error(error);
    }
  },
};

module.exports = generateControllers(eventInscriptionService, extraControllers, controllerOptions);
