const _find = require("./find");
const httpStatus = require("http-status");
const {
  extractParamsFromRequest,
  handleErrorResponse,
  handleResultResponse,
} = require("../../helpers/controller.helper");


function buildContext(req, config) {
  return {
    user: req.user,
    scopes: [],
    ...config,
  };
}

const defaultOptions = {
  MODULE_NAME: "default",
  params: {
    find: { includeAll: false },
    findOne: { includeAll: false, paginate: { limit: 1, page: 1 } },
    count: {},
    create: {},
    update: {},
    delete: {},
  },
};

const generateControllers = (service, extraControllers = {}, options = {}) => {
  const _options = {
    ...defaultOptions,
    ...options,
  };

  const defaultControllers = {
    find: (config) => {
      return async (req, res, next) => {
        config = config || {
          scopes: [],
        };

        const params = extractParamsFromRequest(req, res, _options.params.find);
        try {
          const result = await service.fetchAll(params, buildContext(req, config));

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

          return handleResultResponse(result.rows, result.count, params, res);
        } catch (error) {
          return handleErrorResponse(_options.MODULE_NAME, "find", error, res);
        }
      };
    },

    findOne: (config) => {
      return async (req, res, next) => {
        const params = extractParamsFromRequest(req, res, _options.params.findOne);
        try {
          let result = await service.fetchOne(params, buildContext(req, config));

          //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 result) {
                  delete result[campo];
              }
            });
          };

          result = _options.findOneCallback !== undefined ? _options.findOneCallback(result) : result;
          return handleResultResponse(
            result,
            null,
            params,
            res,
            result === null ? httpStatus.NOT_FOUND : httpStatus.OK
          );
        } catch (error) {
          console.log(error);
          return handleErrorResponse(_options.MODULE_NAME, "findOne", error, res);
        }
      };
    },

    count: (config) => {
      return async (req, res, next) => {
        const params = extractParamsFromRequest(req, res, _options.params.count);
        try {
          const result = await service.count(params, buildContext(req, config));
          return handleResultResponse(result, null, params, res);
        } catch (error) {
          return handleErrorResponse(_options.MODULE_NAME, "count", error, res);
        }
      };
    },

    create: (config) => {
      return async (req, res, next) => {
        const params = extractParamsFromRequest(req, res, _options.params.count);
        try {
          //Asignamos el usuario que crea el elemento si viene
          req.body.userId = req.user ? req.user.id : null;
          //Añadimos los parametros que vienen por url y en el body
          let values = Object.assign({}, params.params, req.body);
          //Quitamos el campo id si viniera, para que no de un conflicto con el id autoinc
          if (values.id) delete values.id;
          console.log(">> SERVICE CREATE ->", values);
          const result = await service.create(values, buildContext(req, config));
          return handleResultResponse(result, null, null, res, httpStatus.CREATED);
        } catch (error) {
          return handleErrorResponse(_options.MODULE_NAME, "create", error, res);
        }
      };
    },

    update: (config) => {
      return async (req, res, next) => {
        try {
          const params = extractParamsFromRequest(req, res, _options.params.update);
          console.log("SERVICE UPDATE ->", params, req.body);

          const response = await service.update(params, req.body, buildContext(req, config));
          // https://sequelize.org/master/class/lib/model.js~Model.html#static-method-update
          // Update devuelve un array de un elemento con el número de filas afectadas
          // por la operación. Por comodidad, devuelvo el valor, no el array.

          let result = response[0];
          let status = httpStatus.OK;
          if (result < 1) {
            status = httpStatus.NOT_FOUND;
          }

          // Express no puede devolver números porque los confunde con el 'status'.
          // Convertimos a cadena.
          result = result.toString();

          return handleResultResponse(result, null, req.params, res, status);
        } catch (error) {
          return handleErrorResponse(_options.MODULE_NAME, "update", error, res);
        }
      };
    },

    delete: (config) => {
      return async (req, res, next) => {
        try {
          const params = extractParamsFromRequest(req, res, _options.params.delete);
          console.log("SERVICE DELETE ->", params);
          const result = await service.delete(params, buildContext(req, config));
          return handleResultResponse(result, null, req.params, res, httpStatus.NO_CONTENT);
        } catch (error) {
          return handleErrorResponse(_options.MODULE_NAME, "delete", error, res);
        }
      };
    },
  };

  //const associationControllers = generateAssociationControllers(model, options);

  //console.log(associationControllers);

  return {
    ...defaultControllers,
    //...associationControllers
    ...extraControllers,
  };
};

module.exports = generateControllers;
module.exports.buildContext = buildContext;
module.exports.defaultOptions = defaultOptions;
