import { Response } from "express";
import path from "path";
import CustomRequest from "../types/requestType";
import clientDocumentsDB from "../models/officialDocument.model";
//import documentHelper from "../utils/userdocument.helper";
import log from "../config/log";
import clientDB from "../models/client.model";
import CONSTANTS from "../config/constants";
import staffDB from "../models/staff.model";
import { isUserPartner } from "../utils/isUserPartner";
import fsPromises from "fs/promises";
import fs from "fs";
import moment from "moment";

const officialDocuments: any = {};

officialDocuments.CreateFolder = async (req: CustomRequest, res: Response) => {
  const C = "OfficialDoc Controller";
  const F = "CreateFolder";

  try {
    const { name, parentFolderId } = req.body;

    const userType = req.userType;
    let parentFolder = null;
    let parentFolderPath = null;

    log.info(
      `[${C}], [${F}], Name [${name}], Parent Folder Id [${parentFolderId}]`,
    );

    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}], Not Allowed`
      //   );
      //   return res.status(400).json({
      //     msg: "Unauthorized Access",
      //     isSuccess: false,
      //   });
      // }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Staff Requesting.....`,
      );
    }

    const client = await clientDB.getById({ id: clientId });
    if (!client) {
      return res.status(400).json({
        msg: "User not found.",
        isSuccess: false,
      });
    }

    // If a parent folder is specified, verify it exists
    if (parentFolderId) {
      parentFolder = await clientDocumentsDB.getDocumentFolderById({
        id: parentFolderId,
      });

      if (!parentFolder) {
        log.error(
          `[${C}], [${F}], Client Id [${clientId}], Parent Folder Id [${parentFolderId}], Parent folder does not exist`,
        );
        return res.status(400).json({
          msg: "Parent folder does not exist.",
          isSuccess: false,
        });
      }

      if (parentFolder.clientId !== clientId) {
        log.error(
          `[${C}], [${F}], Client Id [${clientId}], Parent Folder Id [${parentFolderId}], User does not own the parent folder`,
        );
        return res.status(403).json({
          msg: "You do not have permission to use this parent folder.",
          isSuccess: false,
        });
      }

      parentFolderPath = parentFolder?.path;
    }

    // Check if folder with same name already exists under the same parent
    const existingFolder = await clientDocumentsDB.getByDocumentFolderByClientIdAndNameAndParentId({
      clientId: clientId,
      name,
      parentFolderId: parentFolderId || null,
    });

    if (existingFolder) {
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Folder [${name}] already exists under Parent Folder Id [${parentFolderId}]`,
      );
      return res.status(400).json({
        msg: "A folder with this name already exists in this location.",
        isSuccess: false,
      });
    }

    const folderName = `${name}`;
    let basePath = `uploads/clientDocuments/${moment().format("YYYY")}/client_${clientId}`;
    if (parentFolderId && parentFolder && parentFolderPath) {
      basePath = parentFolderPath;
      basePath = basePath.replace(
        `${process.env.UPLOAD_PATH}/`,
        "uploads/"
      );
    } 
    const folderPath = `${basePath}/${folderName}`;
    const urlBase = folderPath.replace(
      "uploads/",
      `${process.env.UPLOAD_PATH}/`
    );

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

    // Create the folder record in DB
    const folderId = await clientDocumentsDB.createDocumentFolder({
      clientId: clientId,
      name,
      path: urlBase,
      parentFolderId: parentFolderId || null,
      createdBy: req.id,
    });

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Folder Id [${folderId}], Name [${name}], Parent Folder Id [${parentFolderId}], Folder created successfully`,
    );

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

officialDocuments.ListFolderForClient = async (req: CustomRequest, res: Response) => {
  const C = "OfficialDoc Controller";
  const F = "ListFolderForClient";

  try {
    const { folderIdQuery } = req.query;

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

    const formattedFolderId = folderIdQuery
      ? parseInt(folderIdQuery as string, 10)
      : null;

    log.info(`[${C}], [${F}], Formatted Folder Id [${formattedFolderId}]`);

    if (formattedFolderId && isNaN(formattedFolderId)) {
      return res.status(400).json({
        msg: "Invalid folderId provided.",
        isSuccess: false,
      });
    }

    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 [${staff?.id}], Staff Requesting.....`,
      );
    }

    const client = await clientDB.getById({ id: clientId });
    if (!client) {
      return res.status(400).json({
        msg: "User not found.",
        isSuccess: false,
      });
    }

    if (formattedFolderId) {
      const folder = await clientDocumentsDB.getDocumentFolderById({ id: formattedFolderId });
      if (!folder) {
        return res.status(400).json({
          msg: "Folder does not exist.",
          isSuccess: false,
        });
      }
      if (folder.clientId !== client.id) {
        return res.status(403).json({
          msg: "You do not have access to this folder.",
          isSuccess: false,
        });
      }
    }

    const folders = await clientDocumentsDB.getFoldersByClientIdAndParentId({
      clientId: client.id,
      parentFolderId: formattedFolderId,
    });

    let files = await clientDocumentsDB.getDocumentsByClientIdAndFolderId({
      clientId: client.id,
      folderId: formattedFolderId,
    });

    const formattedFolders = folders.map((f: any) => ({
      ...f,
      type: "folder",
      totalItems: Number(f.totalFiles || 0) + Number(f.totalSubFolders || 0),
    }));

    const formattedFiles = files.map((f: any) => ({
      ...f,
      type: "file",
    }));

    const data = [...formattedFolders, ...formattedFiles];

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

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

officialDocuments.UploadDocument = async (req: CustomRequest, res: Response) => {
  const C = "OfficialDoc Controller";
  const F = "Upload";

  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 {
    let { folderId, title } = req.body;

    log.info(
      `[${C}], [${F}], Folder Id [${folderId}], Title [${title}], Is File Uploaded [${file ? `Yes, File [${JSON.stringify(file)}]` : "No"}]`,
    );

    if (!folderId || !Number(folderId) || String(folderId).trim() === '' || String(folderId).toLowerCase() === 'null' || String(folderId).toLowerCase() === 'undefined') {
      folderId = null;
    }

    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`);
        
        if (file) {
          await fsPromises.unlink(file.path);
        }

        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Staff Id [${staff?.id}], Staff Requesting.....`,
      );
    }

    let folder = null;

    if (folderId) {
      folder = await clientDocumentsDB.getDocumentFolderById({
        id: folderId,
      });
      if (!folder) {
        log.info(
          `[${C}], [${F}], Client Id [${clientId}], Folder Id [${folderId}], No Folder Found`
        );
        
        if (file) {
          await fsPromises.unlink(file.path);
        }
  
        return res.status(400).json({ 
          msg: CONSTANTS.MSG.INVALID_REQUEST, 
          isSuccess: false 
        });
      }
    }

    if (file) {
      // const folderName = `${folder?.name}`;
      let folderPath = `uploads/clientDocuments/${moment().format("YYYY")}/client_${clientId}`;
     
      if (folderId && folder) {
        folderPath = folder?.path;
         log.info(
          `[${C}], [${F}], Client Id [${clientId}], Folder Id [${folderId}], Path [${folderPath}]`
        );
        folderPath = folderPath.replace(
          `${process.env.UPLOAD_PATH}/`,
          "uploads/"
        );
      }
      log.info(
          `[${C}], [${F}], Client Id [${clientId}], Folder Id [${folderId}], Final Path [${folderPath}]`
        );
      const fileExtension = file.mimetype.split("/")[1];
      const filename = `${title}.${fileExtension}`;

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

      const oldPath = `uploads/tmp/${file.filename}`;
      const newPath = `${folderPath}/${filename}`;
      log.info(
          `[${C}], [${F}], Client Id [${clientId}], Folder Id [${folderId}], Replace`
        );
      const url = newPath.replace(
      "uploads/",
      `${process.env.UPLOAD_PATH}/`
      );

      await fsPromises.copyFile(oldPath, newPath);

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

      await clientDocumentsDB.addDocument({
        clientId,
        folderId,
        title,
        path: url,
        fileName: file.filename,
      });
    } else {
      log.info(`[${C}], [${F}], Client Id [${clientId}], Folder Id [${folderId}], Title [${title}], No File Upload`);
      return res.status(400).json({
        msg: "File is required.",
        isSuccess: false,
      });
    }

    log.info(
      `[${C}], [${F}], Client Id [${clientId}], Folder Id [${folderId}], Title [${title}], Document uploaded successfully`,
    );

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

officialDocuments.DeleteFileFolder = async (req: CustomRequest, res: Response) => {
  const C = "OfficialDoc Controller";
  const F = "Delete File Folder";

  try {
    const { id, type } = req.body;
    // Type 1-folder, 2-file

    log.info(
      `[${C}], [${F}], Id [${id}], Type [${type}]`
    );

    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 [${staff?.id}], Staff Requesting.....`,
      );
    }

    if (Number(type) === 1) {
      const folder = await clientDocumentsDB.getDocumentFolderById({ id });
      if (!folder) {
        log.info(`[${C}], [${F}], Folder Id [${id}], No Folder Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
  
      if (folder.clientId !== clientId) {
        log.info(`[${C}], [${F}], Folder Id [${id}], Client Id [${clientId}], Not Authorized`);
        return res
          .status(403)
          .json({ msg: "You do not have access to this folder.", isSuccess: false });
      }

      let folderPath = folder?.path;
      if (folderPath) {
        folderPath = folderPath.replace(
          `${process.env.UPLOAD_PATH}/`,
          "uploads/"
        );
        await fs.promises.rm(folderPath, { recursive: true, force: true });
      }
  
      await clientDocumentsDB.deleteFolderById({ id });

      log.info(
        `[${C}], [${F}], Client Id [${clientId}], Folder Id [${id}], Folder deleted successfully`,
      );

    } else {
      const file = await clientDocumentsDB.getDocumentById({ id });
      if (!file) {
        log.info(`[${C}], [${F}], File Id [${id}], No File Found`);
        return res
          .status(400)
          .json({ msg: CONSTANTS.MSG.INVALID_REQUEST, isSuccess: false });
      }
  
      if (file.clientId !== clientId) {
        log.info(`[${C}], [${F}], File Id [${id}], Client Id [${clientId}], Not Authorized`);
        return res
          .status(403)
          .json({ msg: "You do not have access to this file.", isSuccess: false });
      }

      const filePath = file?.path;
      if (filePath) {
        const pathWithoutPrefix = filePath.replace(
          `${process.env.UPLOAD_PATH}/`,
          "uploads/"
        );

        // Check if file exists before attempting to delete
        if (fs.existsSync(pathWithoutPrefix)) {
          await fsPromises.unlink(pathWithoutPrefix);

          log.info(
            `[${C}], [${F}], Client Id [${clientId}], File Id [${id}], File deleted successfully`,
          );
        } else {
          log.info(
            `[${C}], [${F}], Client Id [${clientId}], File Id [${id}], File not found on server`,
          );
        }
      }
  
      await clientDocumentsDB.deleteDocument({ id });
    }

    return res.status(200).json({
      msg: Number(type) === 1 ? "Folder deleted successfully." : "File 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,
    });
  }
};

export default officialDocuments;
