import { Response } from "express";
import CONSTANTS from "../config/constants";
import log from "../config/log";
import staffDB from "../models/staff.model";
import CustomRequest from "../types/requestType";
import laundryRequestsDB from "../models/laundryRequests.model";
import { isUserPartner } from "../utils/isUserPartner";
import clientDB from "../models/client.model";
import moment from "moment";
import clientConfigDB from "../models/clientConfig.model";
import occupancyDB from "../models/occupancy.model";

const laundry: any = {};

laundry.Add = async (req: CustomRequest, res: Response) => {
  const C = "Laundry Controller";
  const F = "Add";
  try {
    const { tenantId, weight, noOfClothes, laundryDate } = req.body;

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Weight [${weight}], Number of Clothes [${noOfClothes}], Laundry Date [${laundryDate}]`,
    );

    const userType = req.userType;
    let recordedById = req.id;
    let recordedByName = "";

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id),
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}], Partner` : `Client Id [${req.id}], Client`} Requested....`,
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff not found, Id [${req.id}]`);
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }

      recordedByName = staff.name;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requested....`,
      );
    }

    const client = await clientDB.getById({ id: clientId });

    if (!client) {
      log.info(`[${C}], [${F}], Client not found, Id [${clientId}]`);
      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    if (userType === CONSTANTS.USER_TYPE.CLIENT) {
      recordedByName = client.name;
    } else if (isPartner) {
      const partner = await staffDB.getById({ id: req.id });
      recordedByName = partner.name;
    }

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });

    if (!occupancy) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found`
      );

      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    const propId = occupancy.propId;

    const thisMonthLaundry = await laundryRequestsDB.getByTenantIdAndClientIdAndDate({
      tenantId,
      clientId,
      startDate: moment().startOf("month").format("YYYY-MM-DD"),
      endDate: moment().endOf("month").format("YYYY-MM-DD"),
    });

    let alreadyUsedWeight = 0.0;

    if(thisMonthLaundry && thisMonthLaundry.length > 0){
      for(const laundry of thisMonthLaundry){
        if(laundry.weight){
          alreadyUsedWeight += laundry.weight;
        }
      }
    }

    const weightLimit = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.LAUNDRY_LIMIT_PER_TENANT,
    });

    if (weightLimit) {
      if (Number(weightLimit.value) < Number(alreadyUsedWeight) + Number(weight)) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Property Id [${propId}], Weight [${weight}], Already Used Weight [${alreadyUsedWeight}], Weight Limit [${weightLimit.value}], Number of Clothes [${noOfClothes}], Laundry Date [${laundryDate}], Weight Limit Exceeded`
        );

        return res.status(400).json({
          msg: "Weight limit exceeded for tenant",
          isSuccess: false,
        });
      }
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Property Id [${propId}], Weight [${weight}], Number of Clothes [${noOfClothes}], Laundry Date [${laundryDate}], Weight Limit Not Set Skipping Check`
      );
    }

    const laundryId = await laundryRequestsDB.add({
      clientId,
      tenantId,
      propId,
      weight,
      noOfClothes,
      laundryDate,
      recordedById,
      recordedByName,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Property Id [${propId}], Weight [${weight}], Number of Clothes [${noOfClothes}], Laundry Date [${laundryDate}], Laundry Record Added Successfully`,
    );

    return res.status(200).json({
      msg: "Laundry recorded successfully",
      isSuccess: true,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

//Tenant App API
laundry.GetForTenant = async (req: CustomRequest, res: Response) => {
  const C = "Laundry Controller";
  const F = "GetForTenant";

  try {
    const { startDate, endDate, pageNum=1 } = req.query;
    const tenantId = req.id;
    const clientId = req.clientId;
    const limit = 10;

    log.info(
      `[${C}], [${F}], Client Id [${clientId}] Tenant Id [${tenantId}], Start Date [${startDate}], End Date [${endDate}], Page Number [${pageNum}], Tenant Requesting....`
    );

    const occupancy = await occupancyDB.getByTenantIdAndClientId({
      tenantId,
      clientId,
    });

    if (!occupancy) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], No Occupancy Found`);

      return res.status(400).json({
        msg: CONSTANTS.MSG.INVALID_REQUEST,
        isSuccess: false,
      });
    }

    const laundryRequests = await laundryRequestsDB.getByTenantIdAndClientIdAndDateForTenant({
      tenantId,
      clientId,
      pageNum: pageNum,
      limit: limit,
    });

    const weightLimit = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.LAUNDRY_LIMIT_PER_TENANT,
    });

    let laundryWeightUsed = 0;

    laundryWeightUsed = await laundryRequestsDB.getWeightUsedByClientIdAndTenantId({
      clientId,
      tenantId,
    });

    let thisMonthLaundryWeightUsed = await laundryRequestsDB.getWeightUsedByClientIdAndTenantIdAndDate({
      clientId,
      tenantId,
      startDate: moment().startOf("month").format("YYYY-MM-DD"),
      endDate: moment().endOf("month").format("YYYY-MM-DD"),
    });

    // for (const laundry of laundryRequests) {
    //   if (laundry.weight) {
    //     laundryWeightUsed += Number(laundry.weight);
    //   }
    // }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Start Date [${startDate}], End Date [${endDate}], Laundry Requests Found [${laundryRequests.length}]`
    );

    return res.status(200).json({
      msg: "Laundry requests found successfully",
      isSuccess: true,
      data: laundryRequests,
      weightLimit: weightLimit || null,
      laundryWeightUsed: laundryWeightUsed,
      thisMonthLaundryWeightUsed: thisMonthLaundryWeightUsed || 0,
    });

  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};

//Client App API
laundry.GetForClient = async (req: CustomRequest, res: Response) => {
  const C = "Laundry Controller";
  const F = "GetForClient";

  try {
    const { startDate, endDate, pageNum, s, propIds } = req.query;

    let propFilter = null;
    if (propIds && typeof propIds === 'string') {
      propFilter = propIds.split(',').map(v => v.trim()).filter(Boolean);
    }

    const userType = req.userType;
    const limit = 10;

    log.info(
      `[${C}], [${F}], Search Value [${s}], Start Date [${startDate}], End Date [${endDate}], Page Number [${pageNum}], Limit [${limit}], Prop Ids [${propIds}]`
    );

    let { isPartner, clientId } = await isUserPartner(
      Number(userType),
      Number(req.id),
    );

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${isPartner ? `Partner Id [${req.id}], Partner` : `Client Id [${req.id}], Client`} Requested....`,
      );
    } else {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff not found, Id [${req.id}]`);
        return res.status(400).json({
          msg: CONSTANTS.MSG.INVALID_REQUEST,
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Staff Requested....`,
      );
    }

    let laundryRequests = [];
    let laundryWeightUsed = 0;

    if (String(s) && String(s).trim() !== "" && String(s).trim() !== "undefined" && String(s).trim() !== "null") {
      laundryRequests = await laundryRequestsDB.getByClientIdAndDateAndSearch({
        clientId,
        startDate,
        endDate,
        limit,
        pageNum,
        searchVal: s,
      });

      laundryWeightUsed = await laundryRequestsDB.getWeightUsedByClientIdAndDateAndSearch({
        clientId,
        startDate,
        endDate,
        searchVal: s,
      });
    } else {
      laundryRequests = await laundryRequestsDB.getByClientIdAndDate({
        clientId,
        startDate,
        endDate,
        limit,
        pageNum,
        propFilter: propFilter && Array.isArray(propFilter) && propFilter.length > 0 ? propFilter : null,
      });

      laundryWeightUsed = await laundryRequestsDB.getWeightUsedByClientIdAndDate({
        clientId,
        startDate,
        endDate,
        propFilter: propFilter && Array.isArray(propFilter) && propFilter.length > 0 ? propFilter : null,
      });
    }
    
    const weightLimitPerTenant = await clientConfigDB.getClientConfig({
      clientId,
      provider: CONSTANTS.CLIENT_CONFIG_PROVIDER.INTERNAL,
      type: CONSTANTS.CLIENT_CONFIG_TYPE.INTERNAL.LAUNDRY_LIMIT_PER_TENANT,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Start Date [${startDate}], End Date [${endDate}], Laundry Requests Found [${laundryRequests.length}]`
    );

    return res.status(200).json({
      msg: "Laundry requests fetched",
      isSuccess: true,
      data: laundryRequests,
      weightLimitPerTenant: weightLimitPerTenant?.value || null,
      laundryWeightUsed: laundryWeightUsed.toFixed(2),
    });

  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);
    return res.status(500).json({
      msg: CONSTANTS.MSG.ERROR_MESSAGE,
      isSuccess: false,
    });
  }
};



export default laundry;
