import { Response } from "express";
import moment from "moment";
import CONSTANTS from "../config/constants";
import log from "../config/log";
import bedDB from "../models/beds.model";
import clientDB from "../models/client.model";
import duesDB from "../models/dues.model";
import occupancyDB from "../models/occupancy.model";
import propertyDB from "../models/property.model";
import requestDB from "../models/request.model";
import roomDB from "../models/room.model";
import roomOptionDB from "../models/roomOption.model";
import tenantDB from "../models/tenant.model";
import bedTypes from "../schemas/bed.schema";
import roomsTypes from "../schemas/room.schema";
import CustomRequest from "../types/requestType";
import generateRoomNum from "../utils/generateRoomNum";
import sendSMS from "../utils/sendSMS";
import occupanciesTypes from "../schemas/occupancy.schema";
import staffDB from "../models/staff.model";
import extraChargeDB from "../models/extraCharges.model";
import convertTypes from "../utils/convertTypes";
import switchRoomRecordDB from "../models/switchRoomRecord.model";
import flatDB from "../models/flat.model";
import { isUserPartner } from "../utils/isUserPartner";

const flats: any = {};

flats.EditName = async (req: CustomRequest, res: Response) => {
  const C = "Flat Controller";
  const F = "EditName";

  try {
    const { flatId, newName } = req.body;
    let clientId = req.id;
    const userType = req.userType;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Flat Id [${flatId}], New Name [${newName}], Staff Not Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], New Name [${newName}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], New Name [${newName}], Client Requested....`
      );
    }

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], New Name [${newName}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const flat = await flatDB.getById({ id: flatId });

    if (!flat) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Flat Id [${flatId}], New Name [${newName}], No Flat Found`
      );

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

    const isSameNameExists = await flatDB.getByFlatNameAndPropId({
      name: newName,
      propId: flat.propId,
    });

    if (isSameNameExists) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], New Flat Num [${newName}], Flat No Already Exists`
      );
      return res.status(400).json({
        msg: "This flat number is already exists",
        isSuccess: false,
      });
    }

    await flatDB.updateName({ id: flatId, name: newName });

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Flat Id [${flatId}], New Name [${newName}], Flat name has been updated successfully`
    );

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

flats.MakeDeactive = async (req: CustomRequest, res: Response) => {
  const C = "Flat Controller";
  const F = "MakeDeactive";

  try {
    let { flatId } = req.body;

    let clientId = req.id;
    const userType = req.userType;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Flat Id [${flatId}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], Client Requested....`
      );
    }

    const client = await clientDB.getById({ id: clientId });
    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const flat = await flatDB.getById({ id: flatId });

    if (!flat) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Flat Id [${flatId}], No Flat Found`
      );

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

    if (flat.status == CONSTANTS.ROOM_STATUS.INACTIVE) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Flat Id [${flatId}], Flat is already inactive`
      );
      return res.status(400).json({
        msg: "You can deactivate active flat only",
        isSuccess: false,
      });
    }

    const occupancies = await occupancyDB.getByFlatId({
      flatId: flat.id,
    });

    if (occupancies) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Flat Id [${flatId}], Flat is not vacant`
      );
      return res.status(400).json({
        msg: "You can deactivate vacant flat only",
        isSuccess: false,
      });
    }

    await flatDB.updateStatus({
      id: flatId,
      status: CONSTANTS.FLAT_STATUS.INACTIVE,
    });

    const flatRooms = await roomDB.getByFlatId ({ flatId: flatId });
  
    if (flatRooms) {
      for (let room of flatRooms){
        await roomDB.updateStatus ({
          id: room.id,
          status: CONSTANTS.ROOM_STATUS.INACTIVE,
        })
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Flat Id [${flatId}], Flat deactivated successfully`
    );

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

flats.MakeActive = async (req: CustomRequest, res: Response) => {
  const C = "Flat Controller";
  const F = "MakeActive";

  try {
    let { flatId } = req.body;

    let clientId = req.id;
    const userType = req.userType;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Flat Id [${flatId}], Staff Id [${req.id}], No Staff Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], Client Requested....`
      );
    }

    const client = await clientDB.getById({ id: clientId });
    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const flat = await flatDB.getById({ id: flatId });

    if (!flat) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Flat Id [${flatId}], No Flat Found`
      );

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

    if (flat.status == CONSTANTS.FLAT_STATUS.ACTIVE) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Flat Id [${flatId}], Flat is already active`
      );
      return res.status(400).json({
        msg: "This Flat is already active",
        isSuccess: false,
      });
    }

    await flatDB.updateStatus({
      id: flatId,
      status: CONSTANTS.FLAT_STATUS.ACTIVE,
    });

    const flatRooms = await roomDB.getByFlatId ({ flatId: flatId });
  
    if (flatRooms) {
      for (let room of flatRooms){
        if(client?.allowTenantOnInactiveProperty === 1) {
          let occupancies = await occupancyDB.getOccupancyByRoomId({roomId: room?.id});
          let roomBeds = await bedDB.getByRoomId ({roomId: room?.id}); 
          const occupiedBeds = occupancies?.length || 0;
          const totalBeds = roomBeds.length || 0;
          let roomStatus;
          if (totalBeds === 0  || occupiedBeds === 0) {
            roomStatus = CONSTANTS.ROOM_STATUS.VACANT;
          } else if (occupiedBeds >= totalBeds) {
            roomStatus = CONSTANTS.ROOM_STATUS.OCCUPIED;
          } else {
            roomStatus = CONSTANTS.ROOM_STATUS.SEMI_OCCUPIED;
          }

          await roomDB.updateStatus({
            id: room.id,
            status: roomStatus,
          });
        } else {
          await roomDB.updateStatus ({
            id: room.id,
            status: CONSTANTS.ROOM_STATUS.VACANT,
          })
        }
      }
    }

    log.info(
      `[${C}], [${F}], Client Id [${client?.id}], Flat Id [${flatId}], Flat activated successfully`
    );

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

flats.AddFlat = async (req: CustomRequest, res: Response) => {
  const C = "Flat Controller";
  const F = "AddFlat";

  try {
    const { propId, flatName } = req.body;
    let clientId = req.id;
    const userType = req.userType;

    if (userType === CONSTANTS.USER_TYPE.STAFF) {
      const staff = await staffDB.getById({ id: req.id });
      if (!staff) {
        log.info(
          `[${C}], [${F}], Staff Id [${req.id}], Prop Id [${propId}], Flat Name [${flatName}], Staff Not Found`
        );
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }

      clientId = staff.clientId;

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Flat Name [${flatName}], Staff Id [${req.id}], Staff Requested.....`
      );
    } else {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Flat Name [${flatName}], Client Requested....`
      );
    }

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

    if (!client) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], New Name [${flatName}], No Client Found`
      );
      return res
        .status(400)
        .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
    }

    const prop = await propertyDB.getById({ id: propId });

    if (!prop) {
      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], Flat Name [${flatName}], No Property Found`
      );

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

    if (flatName.length > 5){

      log.info(
        `[${C}], [${F}], Client Id [${client?.id}], Prop Id [${propId}], Flat Name [${flatName}], Flat Name is too long`
      );
      return res.status(400).json({
        msg: "Flat name is too long",
        isSuccess: false,
      });
    }

    const isSameNameExists = await flatDB.getByFlatNameAndPropId({
      name: flatName,
      propId: propId,
    });

    if (isSameNameExists) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Flat Name [${flatName}], Flat No Already Exists`
      );
      return res.status(400).json({
        msg: "This flat number is already exists",
        isSuccess: false,
      });
    }

    const flatId = await flatDB.create({ clientId, propId, name: flatName });

    await flatDB.updateGender({
      id: flatId,
      gender: prop.tenantPreference,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Prop Id [${propId}], Flat Name [${flatName}], Flat has been added successfully`
    );

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

flats.UpdateGenderPreference = async (req: CustomRequest, res: Response) => {
  const C = "Flat Controller";
  const F = "UpdateGenderPreference";

  try {
    const { flatId, gender } = req.body;

    log.info(`[${C}], [${F}], Flat Id [${flatId}], Gender [${gender}]`);

    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}], Staff Id [${req.id}], Staff Role [${staff.role}], Admin or Back Office Requested....`
      );
    }

    const flat = await flatDB.getById({ id: flatId });
    if (!flat) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], Gender [${gender}], No Flat Found`
      );

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

    await flatDB.updateGender({
      id: flatId,
      gender: gender,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Flat Id [${flatId}], Gender [${gender}], Gender Preference Updated Successfully`
    );

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