import { Response } from "express";
import log from "../config/log";
import CONSTANTS from "../config/constants";
import CustomRequest from "../types/requestType";
import staffDB from "../models/staff.model";
import { isUserPartner } from "../utils/isUserPartner";
import wifiCredentialsDB from "../models/wifiCredential.model";
import walletDB from "../models/wallet.model";

const wallet: any = {};

wallet.CreateStaffWallet = async (req: CustomRequest, res: Response) => {
  //Will also be use for creating staff wallet
  const C = "Wallet Controller";
  const F = "CreateStaffWallet";

  try {
    let { amount=0, maxTransactionLimit=0, dailyLimit=0, staffId } = req.body;

    log.info(
      `[${C}], [${F}], Amount [${amount}], Staff Id [${staffId}], Max Trans Limit [${maxTransactionLimit}], Daily Limit [${dailyLimit}]`,
    );

    const userType = req.userType;

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

    let payeeWallet = null;

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${
          isPartner
            ? `Partner Id [${req.id}], Partner`
            : `Client Id [${clientId}], Client`
        }, Requesting....`,
      );

      if (isPartner) {
        payeeWallet = await walletDB.getByClientIdAndStaffId({
          clientId,
          staffId: req.id,
        });
      } else {
        payeeWallet = await walletDB.getByClientId({ clientId });
      }
    } 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}], Staff Requesting....`,
      );

      payeeWallet = await walletDB.getByClientIdAndStaffId({
        clientId,
        staffId: req.id,
      });
    }

    if (!payeeWallet) {
      log.info(
        `[${C}], [${F}], User Id [${req.id}], User Type [${userType}], Payee Wallet Not Found`,
      );
      return res.status(400).json({
        msg: "Unable to find your wallet",
        isSuccess: false,
      });
    }

    if (Number(payeeWallet.balance) < Number(amount)) {
      log.info(
        `[${C}], [${F}], User Id [${req.id}], User Type [${userType}], Insufficient Balance`,
      );
      return res.status(400).json({
        msg: "Insufficient Balance",
        isSuccess: false,
      });
    }

    await walletDB.create({
      clientId,
      staffId,
      amount: amount,
      balance: amount,
      subWalletId: null,
      maxTransLimit: maxTransactionLimit,
      dailyPayoutLimit: dailyLimit,
    });

    
    if (Number(amount) > 0) {
      if (Number(userType) === CONSTANTS.USER_TYPE.STAFF) {
        await walletDB.updateAmount({
          clientId,
          staffId: Number(userType) === CONSTANTS.USER_TYPE.STAFF ? req.id : null,
          amount: Number(payeeWallet.amount) - Number(amount),
        });
      }
  
      await walletDB.updateBalance({
        clientId,
        staffId: Number(userType) === CONSTANTS.USER_TYPE.STAFF ? req.id : null,
        balance: Number(payeeWallet.balance) - Number(amount),
      });

      await walletDB.recordTransfer({
        clientId,
        transferBy: req.id,
        transferByUserType: userType,
        transferTo: staffId,
        transferToUserType: CONSTANTS.USER_TYPE.STAFF,
        amount,
        transferDate: new Date(),
      });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Amount [${amount}], Paid To Staff Id [${staffId}], Funds Transfered Successfully`,
    );

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

wallet.EditStaffWallet = async (req: CustomRequest, res: Response) => {
  //Will also be use for creating staff wallet
  const C = "Wallet Controller";
  const F = "EditStaffWallet";

  try {
    let { maxTransactionLimit=0, dailyLimit=0, staffId } = req.body;

    log.info(
      `[${C}], [${F}], Staff Id [${staffId}], Max Trans Limit [${maxTransactionLimit}], Daily Limit [${dailyLimit}]`,
    );

    const userType = req.userType;

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

    let payeeWallet = null;

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${
          isPartner
            ? `Partner Id [${req.id}], Partner`
            : `Client Id [${clientId}], Client`
        }, Requesting....`,
      );

      if (isPartner) {
        payeeWallet = await walletDB.getByClientIdAndStaffId({
          clientId,
          staffId
        });
      } else {
        payeeWallet = await walletDB.getByClientId({ clientId });
      }
    } 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}], Staff Requesting....`,
      );

      payeeWallet = await walletDB.getByClientIdAndStaffId({
        clientId,
        staffId
      });
    }

    if (!payeeWallet) {
      log.info(
        `[${C}], [${F}], User Id [${req.id}], User Type [${userType}], Payee Wallet Not Found`,
      );
      return res.status(400).json({
        msg: "Unable to find your wallet",
        isSuccess: false,
      });
    }


    await walletDB.updateWalletLimits({
      clientId,
      staffId: staffId || null,
      maxTransLimit: maxTransactionLimit,
      dailyPayoutLimit: dailyLimit,
    });


    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Wallet edited successfully`,
    );

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

wallet.AddFunds = async (req: CustomRequest, res: Response) => {
  //Will also be use for creating staff wallet
  const C = "Wallet Controller";
  const F = "AddFunds";

  try {
    let { amount=0, staffId } = req.body;

    log.info(
      `[${C}], [${F}], Amount [${amount}], Paid To Staff Id [${staffId}]`,
    );

    const userType = req.userType;

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

    let payeeWallet = null;

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${
          isPartner
            ? `Partner Id [${req.id}], Partner`
            : `Client Id [${clientId}], Client`
        }, Requesting....`,
      );

      if (isPartner) {
        payeeWallet = await walletDB.getByClientIdAndStaffId({
          clientId,
          staffId: req.id,
        });
      } else {
        payeeWallet = await walletDB.getByClientId({ clientId });
      }
    } 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}], Staff Requesting....`,
      );

      payeeWallet = await walletDB.getByClientIdAndStaffId({
        clientId,
        staffId: req.id,
      });
    }

    if (!payeeWallet) {
      log.info(
        `[${C}], [${F}], User Id [${req.id}], User Type [${userType}], Payee Wallet Not Found`,
      );
      return res.status(400).json({
        msg: "Unable to find your wallet",
        isSuccess: false,
      });
    }

    if (Number(payeeWallet.balance) < Number(amount)) {
      log.info(
        `[${C}], [${F}], User Id [${req.id}], User Type [${userType}], Insufficient Balance`,
      );
      return res.status(400).json({
        msg: "Insufficient Balance",
        isSuccess: false,
      });
    }

    const paidToWallet = await walletDB.getByClientIdAndStaffId({
      clientId,
      staffId: staffId,
    });

    if (!paidToWallet) {
      log.info(
        `[${C}], [${F}], User Id [${req.id}], User Type [${userType}], PaidTo Wallet Not Found, Creating New`,
      );

      await walletDB.create({
        clientId,
        staffId,
        amount: amount,
        balance: amount,
        subWalletId: null,
        maxTransLimit: null,
        dailyPayoutLimit: null,
      });
    } else {
      await walletDB.updateAmount({
        clientId,
        staffId,
        amount: Number((Number(paidToWallet.amount) + Number(amount)).toFixed(2)),
      });

      await walletDB.updateBalance({
        clientId,
        staffId,
        balance: Number((Number(paidToWallet.balance) + Number(amount)).toFixed(2)),
      });
    }

    if (Number(userType) === CONSTANTS.USER_TYPE.STAFF) {
      await walletDB.updateAmount({
        clientId,
        staffId: Number(userType) === CONSTANTS.USER_TYPE.STAFF ? req.id : null,
        amount: Number((Number(payeeWallet.amount) - Number(amount)).toFixed(2)),
      });
    }

    await walletDB.updateBalance({
      clientId,
      staffId: Number(userType) === CONSTANTS.USER_TYPE.STAFF ? req.id : null,
      balance: Number((Number(payeeWallet.balance) - Number(amount)).toFixed(2)),
    });

    if (Number(amount) > 0) {
      await walletDB.recordTransfer({
        clientId,
        transferBy: req.id,
        transferByUserType: userType,
        transferTo: staffId,
        transferToUserType: CONSTANTS.USER_TYPE.STAFF,
        amount,
        transferDate: new Date(),
      });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Amount [${amount}], Paid To Staff Id [${staffId}], Funds Transfered Successfully`,
    );

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

wallet.WithdrawFunds = async (req: CustomRequest, res: Response) => {
  const C = "Wallet Controller";
  const F = "WithdrawFunds";

  try {
    let { amount, staffId } = req.body;

    log.info(
      `[${C}], [${F}], Amount [${amount}], Paid To Staff Id [${staffId}]`,
    );

    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}], Partner`
            : `Client Id [${clientId}], 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}], Staff Requesting....`,
      );

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

    const staffWallet = await walletDB.getByClientIdAndStaffId({
      clientId,
      staffId: staffId,
    });
    if (!staffWallet) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Staff Wallet Not Found`,
      );
      return res.status(400).json({
        msg: "Unable to find staff wallet",
        isSuccess: false,
      });
    }

    if (Number(staffWallet.balance) < Number(amount)) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Insufficient Balance In Staff Wallet`,
      );
      return res.status(400).json({
        msg: "Insufficient balance in staff wallet",
        isSuccess: false,
      });
    }
    
    await walletDB.updateAmount({
      clientId,
      staffId,
      amount: Number((Number(staffWallet.amount) - Number(amount)).toFixed(2)),
    });

    await walletDB.updateBalance({
      clientId,
      staffId,
      balance: Number((Number(staffWallet.balance) - Number(amount)).toFixed(2)),
    });

    const clientWallet = await walletDB.getByClientId({
      clientId,
    });

    await walletDB.updateBalance({
      clientId,
      staffId: null,
      balance: Number((Number(clientWallet.balance) + Number(amount)).toFixed(2)),
    });

    await walletDB.recordTransfer({
      clientId,
      transferBy: staffId,
      transferByUserType: CONSTANTS.USER_TYPE.STAFF,
      transferTo: clientId,
      transferToUserType: CONSTANTS.USER_TYPE.CLIENT,
      amount,
      transferDate: new Date(),
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Amount [${amount}], Paid To Staff Id [${staffId}], Funds Withdrawn Successfully`,
    );

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

wallet.ListStaffTransfer = async (req: CustomRequest, res: Response) => {
  const C = "Wallet Controller";
  const F = "ListStaffTransfer";

  try {
    let { staffId, pageNum } = req.query;

    log.info(`[${C}], [${F}], Staff Id [${staffId}], 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}], Partner`
            : `Client Id [${clientId}], 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}], Staff Requesting....`,
      );
    }

    const transfers = await walletDB.getStaffTransfers({
      clientId,
      staffId,
      pageNum: Number(pageNum) || null,
      limit: Number(limit),
    });

    const staffWalletSummary = await walletDB.getWalletSummaryForStaff({
      clientId: Number(clientId),
      staffId: Number(staffId),
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staffId}], Staff Transfers Fetched Successfully`,
    );

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

wallet.ListTransferForClient = async (req: CustomRequest, res: Response) => {
  const C = "Wallet Controller";
  const F = "ListTransferForClient";

  try {
    const { startDate, endDate } = req.query;
    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}], Partner`
            : `Client Id [${clientId}], 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}], Staff Requesting....`,
      );
    }

    const transfers = await walletDB.getTransfersByClientId({
      clientId,
      startDate,
      endDate,
    });

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

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

wallet.ListWallets = async (req: CustomRequest, res: Response) => {
  const C = "Wallet Controller";
  const F = "ListWallets";

  try {
    const { pageNum } = req.query;
    const limit = 10;

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

    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}], Partner`
            : `Client Id [${clientId}], 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}], Staff Requesting....`,
      );
    }

    const wallets = await walletDB.getAllByClientId({
      clientId,
      pageNum: Number(pageNum) || null,
      limit: Number(limit),
    });

    const clientWallet = await walletDB.getByClientId({
      clientId,
    });

    const staffWithoutWallet = await walletDB.getStaffIdWithoutWallet({
      clientId,
    });

    const summary = {
      total: Number(clientWallet?.amount) || 0,
      balance: Number(clientWallet?.balance) || 0,
      givenToStaff: Number(((Number(clientWallet?.amount) || 0) - (Number(clientWallet?.balance) || 0)).toFixed(2)),
    }

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

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

wallet.UpdateStatus = async (req: CustomRequest, res: Response) => {
  const C = "Wallet Controller";
  const F = "UpdateStatus";

  try {
    const { walletId, status } = req.body;
    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}], Partner`
            : `Client Id [${clientId}], 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}], Staff Requesting....`,
      );
    }

    const wallet = await walletDB.getById({ id: walletId });
    if (!wallet) {
      log.info(`[${C}], [${F}], Wallet Id [${walletId}], No Wallet Found`);
      return res.status(400).json({
        msg: "No wallet record found",
        isSuccess: false,
      });
    }

    if (!wallet.staffId) {
      log.info(`[${C}], [${F}], Wallet Id [${walletId}], Cannot Deactivate Client Wallet`)
      return res.status(400).json({
        msg: "You can't deactivate client wallet",
        isSuccess: false,
      });
    }

    const clientWallet = await walletDB.getByClientId({
      clientId,
    });

    if (status === CONSTANTS.WALLET_STATUS.DEACTIVATED) {
      await walletDB.updateAmount({
        clientId: wallet?.clientId,
        staffId: wallet?.staffId,
        amount: 0,
      });

      await walletDB.updateBalance({
        clientId: wallet?.clientId,
        staffId: wallet?.staffId,
        balance: 0,
      });

      await walletDB.updateBalance({
        clientId: wallet?.clientId,
        staffId: null,
        balance: Number((Number(clientWallet?.balance) + Number(wallet?.balance)).toFixed(2)),
      });
    }

    await walletDB.updateStatus({ 
      id: walletId, 
      status 
    });

    log.info(
      `[${C}], [${F}], Wallet Id [${walletId}], Status Updated Successfully`,
    );

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