import { Response } from "express";
import log from "../config/log";
import CustomRequest from "../types/requestType";
import CONSTANTS from "../config/constants";
import { isUserPartner } from "../utils/isUserPartner";
import staffDB from "../models/staff.model";
import autopayDB from "../models/autopay.model";
import recurringExpenseDB from "../models/recurringExpense.model";
import payoutBeneficiaryDB from "../models/payoutBeneficiary.model";
import expenseDB from "../models/expense.model";
import moment from "moment";

const autopayController: any = {};

autopayController.Create = async (req: CustomRequest, res: Response) => {
  const C = "Autopay Controller";
  const F = "Create";
  try {
    const {
      beneficiaryId,
      expenseType,
      totalTransactions,
      amount,
      totalAmount,
      frequencyType,
      nextDueDate,
    } = req.body;

    const type = CONSTANTS.AUTOPAY_TYPES.FIXED_TRANSACTIONS;
    const frequencyValue = 1;

    log.info(`[${C}], [${F}], Beneficiary Id [${beneficiaryId}], Type [${type}], Expense Type [${expenseType}], Total Transactions [${totalTransactions}], Total Amount [${totalAmount}], Amount [${amount}], Frequency Type [${frequencyType}], Frequency Value [${frequencyValue}], Next Due Date [${nextDueDate}]`);

    const userType = req.userType;

    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}]`}, ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Unauthorized Access By Staff`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin Requesting....`
      );
    }

    const beneficiary = await payoutBeneficiaryDB.getById({
      id: beneficiaryId,
    });

    if (!beneficiary) {
      log.info(`[${C}], [${F}], Beneficiary Id [${beneficiaryId}], No Beneficiary Found`);
      return res.status(400).json({ 
        msg: "Invalid Beneficiary", 
        isSuccess: false 
      });
    }

    const autopay = await autopayDB.createAutopay({
      clientId,
      beneficiaryId,
      type: type,
      expenseType,
      totalTransactions: totalTransactions || null,
      transactionsRemaining: totalTransactions || null,
      totalAmount,
      amount,
      status: CONSTANTS.AUTOPAY_STATUS.ACTIVE,
      frequencyType,
      frequencyValue,
      nextDueDate,
      addedBy: req.id,
      addedByUserType: userType,
    });
    
    const expenseId = await expenseDB.create({
      type: expenseType,
      amount: totalAmount,
      clientId,
      paidDate: moment().format("YYYY-MM-DD"),
      paidByUserType: CONSTANTS.USER_TYPE.CLIENT,
      paidBy: clientId,
      paidTo: beneficiary.userId,
      paidToUserType: beneficiary.userType,
      description: null,
      paymentMethod: CONSTANTS.TRANSACTION_MODES.UPI,
      repetitionType: CONSTANTS.REPETITION_TYPE.ONE_TIME,
      noOfMonths: null,
      dueDate: moment().format("YYYY-MM-DD"),
      isPaid: 1,
      paymentAccountNo: null,
      paymentAccountName: null,
      assetId: null,
      expenseNature: null,
      expenseTitle: null,
    });

    log.info(`[${C}], [${F}], Client Id [${clientId}], Beneficiary Id [${beneficiaryId}], Type [${type}], Expense Type [${expenseType}], Total Transactions [${totalTransactions}], Total Amount [${totalAmount}], Amount [${amount}], Frequency Type [${frequencyType}], Frequency Value [${frequencyValue}], Next Due Date [${nextDueDate}], Autopay Created Successfully`)

    return res.status(200).send({
      message: "Autopay created 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,
    });
  }
};

autopayController.List = async (req: CustomRequest, res: Response) => {
  const C = "Autopay Controller";
  const F = "List";
  try {
    const { pageNum } = req.query;

    log.info(`[${C}], [${F}], Page Number [${pageNum}]`);

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

    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}]`}, ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Unauthorized Access By Staff`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin Requesting....`
      );
    }

    const autopays = await autopayDB.getByClientId({
      clientId,
      pageNum,
      limit,
    });

    // let autopaySummary = await autopayDB.getSummaryByClientIdAndDateRange({
    //   clientId,
    //   startDate: moment().startOf('month').format('YYYY-MM-DD'),
    //   endDate: moment().endOf('month').format('YYYY-MM-DD'),
    // });

    const beneficiaries = await payoutBeneficiaryDB.getByClientIdForDropDown({ clientId });

    let expenseCategories = await expenseDB.getExpenseCategories();

    for (let category of expenseCategories) {
      let items = await expenseDB.getExpenseTypes({
        categoryId: category.id,
      });
      // if (items) items = items.filter((item: { id: number }) => item.id !== 54);
      category.items = items || [];
    }

    log.info(`[${C}], [${F}], Client Id [${clientId}], Autopays Sent Successfully`);

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

autopayController.ListTransactions = async (req: CustomRequest, res: Response) => {
  const C = "Autopay Controller";
  const F = "ListTransactions";
  try {
    const { pageNum, autopayId } = req.query;

    log.info(`[${C}], [${F}], Page Number [${pageNum}], Autopay Id [${autopayId}]`);

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

    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}]`}, ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Unauthorized Access By Staff`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin Requesting....`
      );
    }

    const transactions = await autopayDB.getTransactionByClientIdAndAutopayId({
      clientId,
      autopayId,
      pageNum,
      limit,
    });

    log.info(`[${C}], [${F}], Client Id [${clientId}], Autopays Sent Successfully`);

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

autopayController.ConvertRecurringToAutopay = async (req: CustomRequest, res: Response) => {
  const C = "Autopay Controller";
  const F = "ConvertRecurringToAutopay";
  try {
    const { recurringExpenseId, beneficiaryId } = req.body;
    log.info(`[${C}], [${F}], Recurring Expense Id [${recurringExpenseId}], Beneficiary Id [${beneficiaryId}]`);

    const userType = req.userType;

    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}]`}, ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      // if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
      //   log.info(
      //     `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Unauthorized Access By Staff`
      //   );
      //   return res.status(400).json({
      //     msg: "Unauthorized Access",
      //     isSuccess: false,
      //   });
      // }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin Requesting....`
      );
    }

    const recurringExpense = await recurringExpenseDB.getById({
      clientId,
      id: recurringExpenseId,
    });
    if(!recurringExpense){
      log.info(`[${C}], [${F}], Client Id [${clientId}], Recurring Expense Id [${recurringExpenseId}], No Recurring Expense Found`);
      return res
        .status(400)
        .json({ msg: "No such recurring expense found", isSuccess: false });
    }

    // const beneficiaryRecord = await payoutBeneficiaryDB.getByUserIdAndUserType({
    //   userId: recurringExpense.paidTo,
    //   userType: recurringExpense.paidToUserType,
    //   clientId,
    // });

    const beneficiaryRecord = await payoutBeneficiaryDB.getById({
      id: beneficiaryId,
    });

    if(!beneficiaryRecord){
      log.info(`[${C}], [${F}], Client Id [${clientId}], Recurring Expense Id [${recurringExpenseId}], No Beneficiary Record Found`);
      return res.status(400).json({ 
        msg: "No beneficiary record found for the user", 
        isSuccess: false 
      });
    }

    const autopayId = await autopayDB.createAutopay({
      clientId,
      beneficiaryId: beneficiaryRecord.id,
      type: CONSTANTS.AUTOPAY_TYPES.UNLIMITED_TRANSACTIONS,
      expenseType: recurringExpense.expenseType,
      totalTransactions: null,
      transactionsRemaining: null,
      totalAmount: null,
      amount: recurringExpense.amount,
      status: CONSTANTS.AUTOPAY_STATUS.ACTIVE,
      frequencyType: CONSTANTS.AUTOPAY_FREQUENCY_TYPES.MONTHLY,
      frequencyValue: recurringExpense.noOfMonths,
      nextDueDate: recurringExpense.dueDate,
      addedBy: req.id,
      addedByUserType: userType,
    });

    await recurringExpenseDB.updateIsAutopay({
      id: recurringExpenseId,
      isAutopay: 1,
      autopayId: autopayId,
    });

    log.info(`[${C}], [${F}], Client Id [${clientId}], Recurring Expense Id [${recurringExpenseId}], Autopay Created Successfully`)

    return res.status(200).send({
      message: "Recurring expense setup for autopay 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,
    });
  }
};

autopayController.ConvertRecurringBackToManual = async (req: CustomRequest, res: Response) => {
  const C = "Autopay Controller";
  const F = "ConvertRecurringBackToManual";
  try {
    const { recurringExpenseId } = req.body;
    log.info(`[${C}], [${F}], Recurring Expense Id [${recurringExpenseId}]`);

    const userType = req.userType;

    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}]`}, ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        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}], Admin Requesting....`
      );
    }

    const recurringExpense = await recurringExpenseDB.getById({
      clientId,
      id: recurringExpenseId,
    });
    if(!recurringExpense){
      log.info(`[${C}], [${F}], Client Id [${clientId}], Recurring Expense Id [${recurringExpenseId}], No Recurring Expense Found`);
      return res
        .status(400)
        .json({ msg: "No such recurring expense found", isSuccess: false });
    }

    const autopay = await autopayDB.getById({
      id: recurringExpense.autopayId,
    });

    if (autopay) {
      await autopayDB.updateAutopayStatus({
        id: autopay?.id,
        status: CONSTANTS.AUTOPAY_STATUS.CANCELLED,
      });
    } else {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Auto Pay Id [${recurringExpense?.autopayId}], No Auto Pay Found With Id`);
    }

    await recurringExpenseDB.updateIsAutopay({
      id: recurringExpenseId,
      isAutopay: 0,
      autopayId: null,
    });

    log.info(`[${C}], [${F}], Client Id [${clientId}], Recurring Expense Id [${recurringExpenseId}], Autopay Cancelled Successfully`);

    return res.status(200).send({
      message: "Manual payment enabled successfully for recurring expense",
      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,
    });
  }
};

autopayController.UpdateStatus = async (req: CustomRequest, res: Response) => {
  const C = "Autopay Controller";
  const F = "UpdateStatus";
  try {
    const { autopayId, status } = req.body;
    log.info(`[${C}], [${F}], Auto Pay Id [${autopayId}], Status [${status}]`);

    const userType = req.userType;

    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}]`}, ${isPartner ? "Partner Requesting" : "Client Requesting"
        }....`
      );
    } else {
      const staff = await staffDB.getById({
        id: req.id,
      });
      if (!staff) {
        log.info(`[${C}], [${F}], Staff Id [${req.id}], No Staff Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      if (staff.role !== CONSTANTS.STAFF_ROLES.ADMIN && staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Unauthorized Access By Staff`
        );
        return res.status(400).json({
          msg: "Unauthorized Access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin Requesting....`
      );
    }

    await autopayDB.updateAutopayStatus({
      id: autopayId,
      status: status,
    });

    log.info(`[${C}], [${F}], Client Id [${clientId}], Auto Pay Id [${autopayId}], Status [${status}], Autopay Status Updated Successfully`)

    return res.status(200).send({
      message: "Autopay status 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 autopayController;
