import { Response } from "express";
import moment from "moment";
import CONSTANTS from "../config/constants";
import log from "../config/log";
import CustomRequest from "../types/requestType";
import { isUserPartner } from "../utils/isUserPartner";
import staffCommissionDB from "../models/staffCommission.model";
import occupancyDB from "../models/occupancy.model";
import expenseDB from "../models/expense.model";
import staffLedgerDB from "../models/staffLedger.model";
import staffDB from "../models/staff.model";
import { setExpenseJournalTallyStatus, setExpensePaymentTallyStatus } from "../utils/setTallyStatus";

const staffCommission: any = {};

staffCommission.Add = async (req: CustomRequest, res: Response) => {
  const C = "Staff Commission Controller";
  const F = "Add";

  try {
    const { staffs, tenantId, propId } = req.body;
    const userType = req.userType;
    const platform = req.platform;

    log.info(
      `[${C}], [${F}], Staffs [${JSON.stringify(staffs)}], Tenant Id [${tenantId}], Prop Id [${propId}], User Type [${userType}], Platform [${platform}]`,
    );

    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}]` : `Client Id [${clientId}]`
        }, Platform [${req.platform}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`,
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Requested User Id [${req.id}], Unauthorized Access`,
      );

      return res.status(400).json({
        msg: "Unauthorized Access",
        isSuccess: false,
      });
    }

    const occupancies = await occupancyDB.getByClientIdAndTenantId({
      clientId,
      tenantId,
    });

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

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

    if (staffs && staffs.length > 0) {
      for (let staff of staffs) {
        const commissionId = await staffCommissionDB.create({
          clientId,
          propId,
          staffId: staff.staffId,
          tenantId,
          occupancyId: occupancies[0].id,
          amount: staff.amount,
          balance: staff.amount,
          paidDate: null,
          isPaid: 0,
        });

        await staffLedgerDB.addExpense({
          staffId: staff.staffId,
          amount: -staff.amount,
          type: CONSTANTS.STAFF_LEDGER_TYPES.COMMISSION,
          mode: CONSTANTS.TRANSACTION_MODES.OFFLINE,
          description: `Commission`,
          expenseId: null,
          paidDate: null,
          dueDate: moment().format("YYYY-MM-DD HH:mm:ss"),
          commissionId: commissionId,
        });
      }
    }

    log.info(`[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Staff Commission Added Successfully`);

    return res.status(200).json({
      msg: "Staff commission added 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,
    });
  }
};

staffCommission.List = async (req: CustomRequest, res: Response) => {
  const C = "Staff Commission Controller";
  const F = "List";

  try {
    let { pageNum, startDate, endDate, s, staffFilters, propFilters, isPaid } = req.query;
    const userType = req.userType;
    const platform = req.platform;
    let limit = 10;

    if (platform === CONSTANTS.TENANT_DEVICE_TYPE.WEB) {
      limit = 10e9;
    }

    log.info(
      `[${C}], [${F}], Search Value [${s}], Page Num [${pageNum}], Staff Filter [${staffFilters}], Property Filter [${propFilters}], Start Date [${startDate}], End Date [${endDate}], Is Paid [${isPaid}], User Type [${userType}], Platform [${platform}]`,
    );

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

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

    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}]` : `Client Id [${clientId}]`
        }, Platform [${req.platform}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`,
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Requested User Id [${req.id}], Unauthorized Access`,
      );

      // return res.status(400).json({
      //   msg: "Unauthorized Access",
      //   isSuccess: false,
      // });
    }

    const staffCommissions = await staffCommissionDB.list({
      clientId,
      staffFilters,
      propFilters,
      isPaid: Number(isPaid),
      startDate,
      endDate,
      pageNum: Number(pageNum),
      limit: Number(limit),
      searchValue: s,
    });

    const UnpaidStaffCommissionsAmount = await staffCommissionDB.getAmountByClientIdAndFilters({
      clientId,
      staffFilters,
      propFilters,
      isPaid: 0,
      startDate,
      endDate,
      searchValue: s,
    });

    const PaidStaffCommissionsAmount = await staffCommissionDB.getAmountByClientIdAndFilters({
      clientId,
      staffFilters,
      propFilters,
      isPaid: 1,
      startDate,
      endDate,
      searchValue: s,
    });

    if (Number(isPaid) === 1 && staffCommissions) {
      for (let commission of staffCommissions) {
        commission.propertyList = [{ id: commission.propId, name: commission.propName }];
      }
    }

    const staffs = await staffDB.getActiveByClientId({ clientId });

    log.info(`[${C}], [${F}], Client Id [${clientId}], Page Num [${pageNum}], Staff Filter [${staffFilters}], Property Filter [${propFilters}], Start Date [${startDate}], End Date [${endDate}], Is Paid [${isPaid}], Staff Commission List Sent Successfully`);

    return res.status(200).json({
      msg: "Commission list fetched successfully",
      isSuccess: true,
      data: staffCommissions || [],
      staffs: staffs || [],
      paidTotal: PaidStaffCommissionsAmount || 0,
      unpaidTotal: UnpaidStaffCommissionsAmount || 0,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

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

staffCommission.Pay = async (req: CustomRequest, res: Response) => {
  const C = "Staff Commission Controller";
  const F = "Pay";

  try {
    const { id, amount, paidDate, mode } = req.body;
    const userType = req.userType;
    const platform = req.platform;

    log.info(
      `[${C}], [${F}], Id [${id}], Amount [${amount}], Paid Date [${paidDate}], Mode [${mode}], User Type [${userType}], Platform [${platform}]`,
    );

    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}]` : `Client Id [${clientId}]`
        }, Platform [${req.platform}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`,
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Requested User Id [${req.id}], Unauthorized Access`,
      );

      return res.status(400).json({
        msg: "Unauthorized Access",
        isSuccess: false,
      });
    }

    const staffCommission = await staffCommissionDB.getById({
      id,
    });

    if (!staffCommission) {
      log.info(`[${C}], [${F}], Id [${id}], No Staff Commission Found`);

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

    if (Number(staffCommission.balance) !== Number(amount)) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Staff Commission Id [${id}], Amount [${amount}], Balance [${staffCommission.balance}], Partial Payment`);

    } else {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Staff Commission Id [${id}], Amount [${amount}], Balance [${staffCommission.balance}], Full Payment`);

      await staffCommissionDB.updateIsPaid({
        id,
        isPaid: 1,
        paidDate: moment(paidDate).format("YYYY-MM-DD HH:mm:ss"),
      });
    }

    const expenseId = await expenseDB.create({
      type: 54,
      amount: Number(amount),
      clientId,
      paidDate: moment(paidDate).format("YYYY-MM-DD hh:mm:ss"),
      paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
      paidBy: clientId,
      paidTo: staffCommission.staffId,
      paidToUserType: CONSTANTS.USER_TYPE.STAFF,
      description: "Commission",
      paymentMethod: mode,
      repetitionType: 1,
      noOfMonths: 0,
      dueDate: moment(staffCommission.createdAt).format("YYYY-MM-DD HH:mm:ss"),
      isPaid: 1,
    });
    await expenseDB.addProperty({
      expenseId,
      propId: staffCommission.propId,
      clientId,
    });

    await setExpenseJournalTallyStatus(
      Number(clientId),
      Number(expenseId),
      CONSTANTS.TALLY_STATUS.PENDING,
    );

    await setExpensePaymentTallyStatus(
      Number(clientId),
      Number(expenseId),
      CONSTANTS.TALLY_STATUS.PENDING,
    );

    await expenseDB.updateRecurringExpenseId({
      id: expenseId,
      recurringExpenseId: staffCommission.id,
    })

    await staffLedgerDB.addExpense({
      staffId: staffCommission.staffId,
      amount: amount,
      type: CONSTANTS.STAFF_LEDGER_TYPES.COMMISSION,
      mode: mode,
      description: `Commission`,
      expenseId: expenseId || null,
      paidDate: moment(paidDate).format("YYYY-MM-DD HH:mm:ss"),
      dueDate: moment(staffCommission.dueDate).format("YYYY-MM-DD"),
      commissionId: id,
    });

    await staffCommissionDB.updateBalance({
      id,
      balance: Number(staffCommission.balance) - Number(amount),
    });

    log.info(`[${C}], [${F}], Client Id [${clientId}], Staff Commission Id [${id}], Amount [${amount}], Balance [${staffCommission.balance}], Paid Date [${paidDate}], Mode [${mode}], Staff Commission Paid Successfully`);

    return res.status(200).json({
      msg: "Commission paid 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,
    });
  }
};

staffCommission.Delete = async (req: CustomRequest, res: Response) => {
  const C = "Staff Commission Controller";
  const F = "Delete";

  try {
    const { id, expenseId } = req.body;
    const userType = req.userType;
    const platform = req.platform;

    log.info(
      `[${C}], [${F}], Staff Commission Id [${id}], Expense Id [${expenseId}], User Type [${userType}], Platform [${platform}]`,
    );

    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}]` : `Client Id [${clientId}]`
        }, Platform [${req.platform}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`,
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Requested User Id [${req.id}], Unauthorized Access`,
      );

      return res.status(400).json({
        msg: "Unauthorized Access",
        isSuccess: false,
      });
    }

    const staffCommission = await staffCommissionDB.getById({
      id,
    });

    if (!staffCommission) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Staff Commission Id [${id}], No Staff Commission Found`);

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


    if (Number(expenseId) && Number(expenseId) !== 0) {
      const expense = await expenseDB.getById({
        id: expenseId,
      });

      if (!expense) {
        log.info(`[${C}], [${F}], Client Id [${clientId}], Staff Commission Id [${id}], Expense Id [${expenseId}], No Expense Found`);

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

      await expenseDB.removeProperty({
        clientId: clientId,
        propId: staffCommission.propId,
        expenseId: expenseId,
      });

      await staffLedgerDB.deleteByCommissionIdAndExpenseId({
        commissionId: id,
        expenseId: expenseId,
      });

      await staffLedgerDB.updateInitalAmount({
        commissionId: id,
        amount: -(Number(staffCommission.amount) - Number(expense.amount)),
      });

      await expenseDB.delete({
        clientId: clientId,
        expenseId: expenseId,
      });

      await staffCommissionDB.updateAmount({
        id,
        amount: Number(staffCommission.amount) - Number(expense.amount),
      });
    } else {
      const isPartiallyPaid = await expenseDB.isCommissionPaid({
        recurringExpenseId: id,
        type: 54, //commission type
      });

      if (isPartiallyPaid) {
        await staffLedgerDB.updateInitalAmount({
          commissionId: id,
          amount: -(Number(staffCommission.amount) - Number(staffCommission.balance)),
        });

        await staffCommissionDB.updateAmount({
          id,
          amount: Number(staffCommission.amount) - Number(staffCommission.balance),
        });

        await staffCommissionDB.updateBalance({
          id,
          balance: 0,
        });

        await staffCommissionDB.updateIsPaid({
          id,
          isPaid: 1,
          paidDate: moment(isPartiallyPaid.paidDate).format("YYYY-MM-DD HH:mm:ss"),
        });
      } else {
        await staffLedgerDB.deleteByCommissionId({
          commissionId: id,
        });

        await staffCommissionDB.delete({
          id,
        });
      }

    }

    log.info(`[${C}], [${F}], Client Id [${clientId}], Staff Commission Id [${id}], Staff Commission Deleted Successfully`);

    return res.status(200).json({
      msg: "Staff commission deleted 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,
    });
  }
};

staffCommission.Edit = async (req: CustomRequest, res: Response) => {
  const C = "Staff Commission Controller";
  const F = "Edit";

  try {
    //Only for unpaid commissions, paid commission update handle in expense edit
    const { id, amount, staffId } = req.body;
    const userType = req.userType;
    const platform = req.platform;

    log.info(
      `[${C}], [${F}], Id [${id}], Amount [${amount}], Staff Id [${staffId}], User Type [${userType}], Platform [${platform}]`,
    );

    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}]` : `Client Id [${clientId}]`
        }, Platform [${req.platform}], ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`,
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Requested User Id [${req.id}], Unauthorized Access`,
      );

      return res.status(400).json({
        msg: "Unauthorized Access",
        isSuccess: false,
      });
    }

    const staffCommission = await staffCommissionDB.getById({
      id,
    });

    if (!staffCommission) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Staff Commission Id [${id}], No Staff Commission Found`);

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

    const staffLedgerEntry = await staffLedgerDB.getByStaffIdAndCommissionId({
      staffId: staffCommission.staffId,
      commissionId: id,
    });

    if (!staffLedgerEntry) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Staff Commission Id [${id}], No Staff Ledger Entry Found`);

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

    if (staffCommission.balance !== amount) {
      const diff = staffCommission.balance - amount;
      staffCommissionDB.updateBalance({
        id,
        balance: amount,
      });

      staffCommissionDB.updateAmount({
        id,
        amount: staffCommission.amount - diff,
      });

      await staffLedgerDB.updateAmount({
        id: staffLedgerEntry.id,
        staffId: staffCommission.staffId,
        amount: -(staffCommission.amount - diff),
      });
    }

    if (Number(staffCommission.staffId) !== Number(staffId)) {
      await staffLedgerDB.updateStaffByCommissionId({
        commissionId: id,
        staffId: staffId,
      });

      await staffCommissionDB.updateStaffId({
        id: id,
        staffId: staffId,
      });

      await expenseDB.updatePaidToForCommission({
        recurringExpenseId: id,
        paidTo: staffId,
        type: 54, //commission
      });
    }

    log.info(`[${C}], [${F}], Client Id [${clientId}], Staff Commission Id [${id}], Staff Commission Updated Successfully`);

    return res.status(200).json({
      msg: "Staff commission updated 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,
    });
  }
};

export default staffCommission;
