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 { isUserPartner } from "../utils/isUserPartner";
import propertyDB from "../models/property.model";
import clientDB from "../models/client.model";
import parcelDB from "../models/parcel.model";
import fsPromises from "fs/promises";
import fs from "fs";
import occupancyDB from "../models/occupancy.model";
import moment from "moment";
import propertiesTypes from "../schemas/property.schema";
import { sendWhatsappParcel, sendWhatsappParcelCollection, sendWhatsappParcelReturned } from "../utils/sendWhatsappWithConfig";
import tenantDB from "../models/tenant.model";
import settingsDB from "../models/settings.model";
import getDeliveryPartnerName from "../utils/getDeliveryPartnerName";

const parcels: any = {};

// const alternateImg = 'https://interaktprodmediastorage.blob.core.windows.net/mediaprodstoragecontainer/2ad9ac4e-e818-498d-b715-585e10597c3a/message_template_media/5TpKcUwszuBj/New%20Courier%20has%20b.png?se=2030-05-30T10%3A44%3A53Z&sp=rt&sv=2019-12-12&sr=b&sig=wTwCp69Y8PR3ix7GmCRmWtw4ozQtPaE5wke0IIaUjII%3D';
const alternateImg = 'https://apis.kipinn.com/uploads/documents/defaults/alternantParcel.png';

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

  const file = req.file as Express.Multer.File;
  const removeTmpImages = async () => {
    try {
      await fsPromises.unlink(file.path);
    } catch (err) {
      log.info(
        `[${C}], [${F}], Error in Removing Temp Image, Error [${JSON.stringify(
          err
        )}]`
      );
    }
  };

  try {
    const { tenantId, partner, description } = req.body;
    const userType = req.userType;

    log.info(
      `[${C}], [${F}], Tenant Id [${tenantId}], Delivery Partner [${partner}], Description [${description}], Is File Uploaded [${
        file ? `Yes], File ${JSON.stringify(file)}` : "No]"
      }]`
    );

    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`);

        if (file) {
          await removeTmpImages();
        }

        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      if (
        staff.role !== CONSTANTS.STAFF_ROLES.ADMIN &&
        staff.role !== CONSTANTS.STAFF_ROLES.WARDEN &&
        staff.role !== CONSTANTS.STAFF_ROLES.SECURITY_GUARD &&
        staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE
      ) {
        log.info(
          `[${C}], [${F}], Staff Id [${staff.id}], Staff Role [${staff.role}], Unauthorised Access, Only Admin, Warden And Guard Allowed`
        );

        if (file) {
          await removeTmpImages();
        }

        return res.status(400).json({
          msg: "Unauthorized access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Staff Id [${staff.id}], Staff Role [${staff.role}], ${
          staff.role === CONSTANTS.STAFF_ROLES.ADMIN
            ? "Admin"
            : "Security Guard"
        } Reqauesting....`
      );
    }

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

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

      if (file) {
        await removeTmpImages();
      }

      return res.status(400).json({ 
        msg: "Tenant has already moved out", 
        isSuccess: false, 
      });
    }

    let url = null;
    if (file) {
      const folderName = `parcel_${tenantId}`;
      const folderPath = `uploads/documents/client_${clientId}/${folderName}`;
      const fileExtension = file.mimetype.split("/")[1];
      const filename = `Parcel${moment().format(
        "DDMMYYHHmmss"
      )}.${fileExtension}`;

      if (!fs.existsSync(folderPath)) {
        await fsPromises.mkdir(folderPath, { recursive: true });
      }

      const oldPath = `uploads/tmp/${file.filename}`;
      const newPath = `${folderPath}/${filename}`;
      url = `${process.env.UPLOAD_PATH}/documents/client_${clientId}/${folderName}/${filename}`;

      await fsPromises.copyFile(oldPath, newPath);

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Old Path [${oldPath}], New Path [${newPath}], Url [${url}], Uploaded Document Processed.`
      );
    }

    await parcelDB.add({
      clientId: clientId,
      tenantId: tenantId,
      propId: occupancy.propId,
      roomId: occupancy.roomId,
      status: 1,
      description: description,
      deliveryPartner: partner,
      document: url,
    });

    const tenant = await tenantDB.getById({
      id: tenantId,
    });

    const propSettings = await settingsDB.getByClientIdAndPropId({
      clientId: clientId,
      propId: occupancy.propId,
    });

    let footerText = propSettings.footer || "The Kipinn Team";

    sendWhatsappParcel(
      tenant.mobile,
      tenant.name,
      getDeliveryPartnerName(partner),
      url || alternateImg,
      footerText,
      clientId
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Tenant Id [${tenantId}], Delivery Partner [${partner}], Description [${description}], Is File Uploaded [${
        file ? `[Yes], File ${JSON.stringify(file)}` : "[No]"
      }], Parcel Recorded Successfully`
    );

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

    if (file) {
      await removeTmpImages();
    }

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

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

  try {
    const { pageNum=1, s, filter } = req.query;
    const userType = req.userType;
    const limit = 10;

    log.info(`[${C}], [${F}], SearchVal [${s}], Page Number [${pageNum}], Limit [${limit}], User Type [${userType}], Filter [${filter}]`);

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

    let parcels: any = [];
    let parcelStats: any = [];

    if (userType === CONSTANTS.USER_TYPE.CLIENT || isPartner) {
      log.info(
        `[${C}], [${F}], ${
          isPartner
            ? `Partner Id [${req.id}], Partner`
            : `Client Id [${clientId}], Client`
        } Requesting....`
      );
      if (s && s !== " ") {
        parcels = await parcelDB.getByClientIdAndSearchVal({
          clientId: clientId,
          searchVal: s,
        });
      } else if (filter === "P") {
        parcels = await parcelDB.getByClientIdAndStatus({
          clientId: clientId,
          status: CONSTANTS.PARCEL_STATUS.PENDING,
          pageNum: pageNum || 1,
          limit,
        });
      } else if (filter === "D") {
        parcels = await parcelDB.getByClientIdAndStatus({
          clientId: clientId,
          status: CONSTANTS.PARCEL_STATUS.DELIVERED,
          pageNum: pageNum || 1,
          limit,
        });
      } else if (filter === "R") {
        parcels = await parcelDB.getByClientIdAndStatus({
          clientId: clientId,
          status: CONSTANTS.PARCEL_STATUS.RETURNED,
          pageNum: pageNum || 1,
          limit,
        });
      } else {
        parcels = await parcelDB.getByClientId({
          clientId: clientId,
          pageNum: pageNum || 1,
          limit,
        });
      }

      parcelStats = await parcelDB.getStatsByClientId({
        clientId: 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 });
      }
      if (
        staff.role !== CONSTANTS.STAFF_ROLES.ADMIN &&
        staff.role !== CONSTANTS.STAFF_ROLES.WARDEN &&
        staff.role !== CONSTANTS.STAFF_ROLES.SECURITY_GUARD &&
        staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE
      ) {
        log.info(
          `[${C}], [${F}], Staff Id [${staff.id}], Staff Role [${staff.role}], Unauthorised Access, Only Admin, Warden And Guard Allowed`
        );

        return res.status(400).json({
          msg: "Unauthorized access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Staff Id [${staff.id}], Staff Role [${staff.role}], ${
          staff.role === CONSTANTS.STAFF_ROLES.ADMIN
            ? "Admin"
            : "Security Guard"
        } Reqauesting....`
      );

      const staffLinkedProps = await propertyDB.getPropsByStaffId({
        staffId: staff.id,
      });

      if (staffLinkedProps) {
        const propertiesIds = staffLinkedProps
          .map((prop: propertiesTypes) => prop.id)
          .join(",");

        if (s && s !== " ") {
          parcels = await parcelDB.getForStaffBySearchVal({
            clientId: clientId,
            propertiesIds: propertiesIds,
            searchVal: s,
          });
        } else if (filter === "P") {
          parcels = await parcelDB.getForStaffByStatus({
            clientId: clientId,
            propertiesIds: propertiesIds,
            status: CONSTANTS.PARCEL_STATUS.PENDING,
            pageNum: pageNum || 1,
            limit,
          });
        } else if (filter === "D") {
          parcels = await parcelDB.getForStaffByStatus({
            clientId: clientId,
            propertiesIds: propertiesIds,
            status: CONSTANTS.PARCEL_STATUS.DELIVERED,
            pageNum: pageNum || 1,
            limit,
          });
        } else if (filter === "R") {
          parcels = await parcelDB.getForStaffByStatus({
            clientId: clientId,
            propertiesIds: propertiesIds,
            status: CONSTANTS.PARCEL_STATUS.RETURNED,
            pageNum: pageNum || 1,
            limit,
          });
        } else {
          parcels = await parcelDB.getForStaff({
            clientId: clientId,
            propertiesIds: propertiesIds,
            pageNum: pageNum || 1,
            limit,
          });
        }
        parcelStats = await parcelDB.getStatsForStaff({
          clientId: clientId,
          propertiesIds: propertiesIds,
        });
      }
    }

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

    return res.status(200).json({
      msg: "Parcel List fetched successfully",
      isSuccess: true,
      data: parcels || [],
      stats: parcelStats,
    });
  } catch (error: any) {
    log.info(`[${C}], [${F}], Error: ${error?.message || error}`);

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

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

  try {
    const { status, parcelId } = req.body;
    const userType = req.userType;

    log.info(
      `[${C}], [${F}], Parcel Id [${parcelId}], Status [${status}], User Type [${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 });
      }
      if (
        staff.role !== CONSTANTS.STAFF_ROLES.ADMIN &&
        staff.role !== CONSTANTS.STAFF_ROLES.WARDEN &&
        staff.role !== CONSTANTS.STAFF_ROLES.SECURITY_GUARD &&
        staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE
      ) {
        log.info(
          `[${C}], [${F}], Staff Id [${staff.id}], Staff Role [${staff.role}], Unauthorised Access, Only Admin And Guard Allowed`
        );

        return res.status(400).json({
          msg: "Unauthorized access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Staff Id [${staff.id}], Staff Role [${staff.role}], ${
          staff.role === CONSTANTS.STAFF_ROLES.ADMIN
            ? "Admin"
            : staff.role === CONSTANTS.STAFF_ROLES.WARDEN
            ? "Warden"
            : "Security Guard"
        } Reqauesting....`
      );
    }

    const parcel = await parcelDB.getById({
      id: parcelId,
    });
    if (!parcel) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Parcel Id [${parcelId}], No Parcel Found`);

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

    await parcelDB.updateStatus({
      id: parcelId,
      status: status,
    });

    const tenant = await tenantDB.getById({
      id: parcel.tenantId,
    });

    const propSettings = await settingsDB.getByClientIdAndPropId({
      clientId: clientId,
      propId: parcel.propId,
    });

    let footerText = propSettings.footer || "The Kipinn Team";

    if (status === CONSTANTS.PARCEL_STATUS.DELIVERED) {
      sendWhatsappParcelCollection(
        tenant.mobile,
        tenant.name,
        getDeliveryPartnerName(parcel.deliveryPartner),
        footerText,
        Number(clientId)
      );
    } else if (status === CONSTANTS.PARCEL_STATUS.RETURNED) {
      sendWhatsappParcelReturned(
        tenant.mobile,
        tenant.name,
        getDeliveryPartnerName(parcel.deliveryPartner),
        footerText,
        Number(clientId)
      );

    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Parcel Id [${parcelId}], Status [${status}], Status Updated Successfully`
    );

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

parcels.SendReminder = async (req: CustomRequest, res: Response) => {
  const C = "Parcel Controller";
  const F = "SendReminder";

  try {
    const { parcelId } = req.body;

    log.info(`[${C}], [${F}] Parcel Id [${parcelId}]`);

    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 });
      }
      if (
        staff.role !== CONSTANTS.STAFF_ROLES.ADMIN &&
        staff.role !== CONSTANTS.STAFF_ROLES.WARDEN &&
        staff.role !== CONSTANTS.STAFF_ROLES.SECURITY_GUARD &&
        staff.role !== CONSTANTS.STAFF_ROLES.BACK_OFFICE
      ) {
        log.info(
          `[${C}], [${F}], Staff Id [${staff.id}], Staff Role [${staff.role}], Unauthorised Access, Only Admin And Guard Allowed`
        );

        return res.status(400).json({
          msg: "Unauthorized access",
          isSuccess: false,
        });
      }
      log.info(
        `[${C}], [${F}], Staff Id [${staff.id}], Staff Role [${staff.role}], ${
          staff.role === CONSTANTS.STAFF_ROLES.ADMIN
            ? "Admin"
            : staff.role === CONSTANTS.STAFF_ROLES.WARDEN
            ? "Warden"
            : "Security Guard"
        } Reqauesting....`
      );
    }

    const parcel = await parcelDB.getById({
      id: parcelId,
    });
    
    if (!parcel) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Parcel Id [${parcelId}], No Parcel Found`);

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

    if (parcel.status !== CONSTANTS.PARCEL_STATUS.PENDING) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Parcel Id [${parcelId}], Parcel Status [${parcel.status}], Parcel Is No Longer In Pending State`
      );

      return res.status(400).json({
        msg: "Parcel is no longer in pending state",
        isSuccess: false
      });
    }

    const tenant = await tenantDB.getById({
      id: parcel.tenantId,
    });
    if (!tenant) {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Parcel Id [${parcelId}], Tenant Id [${parcel.tenantId}], No Tenant Found`);

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

    const propSettings = await settingsDB.getByClientIdAndPropId({
      clientId: clientId,
      propId: parcel.propId,
    });

    let footerText = propSettings.footer || "The Kipinn Team";

    const isSent = await sendWhatsappParcel(
      tenant.mobile,
      tenant.name,
      getDeliveryPartnerName(Number(parcel.deliveryPartner)),
      parcel.document || alternateImg,
      footerText,
      Number(clientId)
    );

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Parcel Id [${parcelId}], Reminder Sent [${isSent}], Reminder sent successfully`
    );

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