import { Request, Response } from "express";
import { AbstractController } from "./abstract.controller";
import { RolesEndpointsModel } from "../models/roles-endpoint.model";
import { RolesEndpointsMethods } from "../methods/roles-endpoint.method";

import { EndpointsModel } from "../models/endpoint.model";
import { EndpointsMethods } from "../methods/endpoint.method";
import mongoose from "mongoose";

export class RolesEndpointsController extends AbstractController {
  methods: RolesEndpointsMethods; 
  methodsEndpoints: EndpointsMethods;
  constructor() {
    super(RolesEndpointsModel);
    this.methods = new RolesEndpointsMethods(RolesEndpointsModel);
    // this.methodsRoles = new RolesMethods(RolesModel);
    this.methodsEndpoints = new EndpointsMethods(EndpointsModel);
  }
  /**
   *
   * @param {*} req
   * @param {*} res
   */
  async getItems(req: Request, res: Response, next) {
    try {
      const { filter, sort } = this.params.aqp(req.query);
      const [, options] = this.params.optionsPaginate(req);

      let aggregate = this.model.aggregate([
        {
          $lookup: {
            from: "endpoints",
            localField: "endpoint_id",
            foreignField: "_id",
            as: "endpoint",
          },
        },
        {
          $unwind: "$endpoint",
        },
        {
          $lookup: {
            from: "roles",
            localField: "role_id",
            foreignField: "_id",
            as: "role",
          },
        },
        {
          $unwind: "$role",
        },
        {
          $match: filter,
        },
        ...this.methods.mainFilter(req),
      ]);
      const data = await this.model.aggregatePaginate(
        { ...aggregate },
        {
          ...options,
          sort,
        }
      );

      res.send(data);
    } catch (e) {
      this.params.handleHttpError(res, e);
    }
  }
  /**
   * Get detail by single row
   * @param {*} req
   * @param {*} res
   */
  async getItem(req: Request, res: Response) {
    try {
      const matchedData = this.params.matchedData(req);
      const _id = matchedData._id;
      const data = await this.model.aggregate([
        {
          $lookup: {
            from: "endpoints",
            localField: "endpoint_id",
            foreignField: "_id",
            as: "endpoint",
          },
        },
        {
          $unwind: "$endpoint",
        },
        {
          $lookup: {
            from: "roles",
            localField: "role_id",
            foreignField: "_id",
            as: "role",
          },
        },
        {
          $unwind: "$role",
        },
        { $limit: 1 },
        {
          $match: { _id: new mongoose.Types.ObjectId(_id) },
        },
        ...this.methods.mainFilter(req),
      ]);
      res.send(data.length > 0 ? data[0] : {});
    } catch (e) {
      this.params.handleHttpError(res, e);
    }
  }
  /**
   * Create record with public source
   * @param {*} req
   * @param {*} res
   */
  async createItem(req: Request, res: Response) {
    try {
      const matchedData = this.params.matchedData(req);
      const rolesEndpoints = await this.methods.existDocument({
        endpoint_id: matchedData.endpoint_id,
        role_id: matchedData.role_id,
      });
      if (rolesEndpoints) {
        this.params.handleErrorResponse(res, "ROLES_ENDPOINT_EXIST", 402);
        return;
      }
      const endpoints = await this.methodsEndpoints.existDocument({
        _id: matchedData.endpoint_id,
      });
      if (!endpoints) {
        this.params.handleErrorResponse(res, "ENDPOINT_NOT_EXIST", 402);
        return;
      }
      // const role_id = await this.methodsRoles.existDocument({
      //   _id: matchedData.role_id,
      // });
      // if (!role_id) {
      //   this.params.handleErrorResponse(res, "ROLES_NOT_EXIST", 402);
      //   return;
      // }

      const data = await this.methods.saveDocument(matchedData, req);
      res.send(data);
    } catch (e) {
      this.params.handleHttpError(res, e);
    }
  }
  /**
   * Set permissions to a role
   * @param {*} req
   * @param {*} res
   */
  async setPermissions(req: Request, res: Response) {
    try {
      const matchedData = this.params.matchedData(req);
      const _id = matchedData._id;

      // const publicRole = await RolesModel.findOne({
      //   name: "PUBLIC",
      //   _id,
      // }).select("_id");
      // if (publicRole) {
      //   this.params.handleErrorResponse(res, "IS_PUBLIC_ROLE", 402);
      //   return;
      // }

      const allEndpoints = await EndpointsModel.find();
      const permissions = allEndpoints.map((endpoint) => {
        return { endpoint: endpoint._id, role_id: _id };
      });

      const data = await Promise.all([
        ...permissions.map((permission) =>
          RolesEndpointsModel.findOneAndUpdate(
            {
              endpoint: permission.endpoint,
              role_id: permission.role_id,
            },
            permission,
            { upsert: true, new: true, setDefaultsOnInsert: true }
          )
        ),
      ]);

      res.send(data);
    } catch (e) {
      this.params.handleHttpError(res, e);
    }
  }
  /**
   * Avoid check to role
   * @param {*} req
   * @param {*} res
   */
  async avoidCheck(req: Request, res: Response) {
    try {
      const matchedData = this.params.matchedData(req);
      const _id = matchedData._id;
      const existEndpoint = await this.methodsEndpoints.existDocument({
        endpoint: _id,
      });
      if (!existEndpoint) {
        this.params.handleErrorResponse(res, "ENDPOINT_NOT_EXIST", 402);
        return;
      }

      // const publicRole = await RolesModel.findOne({ name: "PUBLIC" }).select(
      //   "_id"
      // );
      // if (!publicRole) {
      //   this.params.handleErrorResponse(res, "PUBLIC_ROLE_NOT_EXIST", 402);
      //   return;
      // }
      // const data = await RolesEndpointsModel.findOneAndUpdate(
      //   {
      //     endpoint: _id,
      //     role_id: publicRole._id,
      //   },
      //   {
      //     endpoint: _id,
      //     role_id: publicRole._id,
      //   },
      //   { upsert: true, new: true, setDefaultsOnInsert: true }
      // );
      // res.send(data);
    } catch (e) {
      this.params.handleHttpError(res, e);
    }
  }
}
