const util = require("util");
var fs = require('fs');
var multer = require('multer');
const path = require("path");
const sharp = require("sharp");
const { splitSite } = require("../middlewares");
const dbConfig = require("../config/db.config");
const MongoClient = require("mongodb").MongoClient;
const mongoClient = new MongoClient(dbConfig.url);
const BASE_UPLOAD_DIR = `/www/wwwroot/upload/`;
const DRIVER_SIGNATURE_DIR = path.join(BASE_UPLOAD_DIR, 'driver', 'sig');
const GENERAL_UPLOAD_DIR = path.join(BASE_UPLOAD_DIR, 'app-files');
const uploadToMemory = multer({
  storage: multer.memoryStorage(),
  limits: { fileSize: 10 * 1024 * 1024 }
}).single("file");
const uploadToMemoryAsync = util.promisify(uploadToMemory);

const readdir = util.promisify(fs.readdir);
const stat = util.promisify(fs.stat);

const isDriverSignatureFileName = (name = '') => {
  const safeName = `${name || ''}`.trim();
  return /^.+_.+_\d{2}_\d{2}$/.test(safeName);
};

const getDriverSignatureYear = (req) => {
  const queryYear = `${req?.query?.year || ''}`.trim();
  if (/^\d{4}$/.test(queryYear)) {
    return queryYear;
  }
  return `${new Date().getFullYear()}`;
};

const getDriverSignatureSiteYearFolder = (req) => {
  const year = getDriverSignatureYear(req);
  const site = splitSite.findSiteNumber(req);
  return `site${site}_${year}`;
};

const resolveSignatureExt = (mimetype = '', originalName = '') => {
  const safeMimetype = `${mimetype || ''}`.toLowerCase();
  if (safeMimetype.includes('png')) return '.png';
  if (safeMimetype.includes('jpeg') || safeMimetype.includes('jpg')) return '.jpg';
  const ext = path.extname(`${originalName || ''}`.toLowerCase());
  if (ext === '.png' || ext === '.jpg' || ext === '.jpeg') return ext;
  return '.jpg';
};

const resizeDriverSignatureBuffer = async (fileBuffer, ext = '.jpg') => {
  const pipeline = sharp(fileBuffer).resize(400, 200, {
    fit: 'fill'
  });
  if (ext === '.png') {
    return pipeline.png().toBuffer();
  }
  return pipeline.jpeg({ quality: 90 }).toBuffer();
};

const findDriverSignatureFilePath = async (fileBaseName = '') => {
  const safeName = `${fileBaseName || ''}`.trim();
  if (!safeName) return null;
  if (!fs.existsSync(DRIVER_SIGNATURE_DIR)) return null;

  const yearFolders = await readdir(DRIVER_SIGNATURE_DIR, { withFileTypes: true });
  const siteYearDirs = yearFolders
    .filter((entry) => entry.isDirectory() && /^site\d+_\d{4}$/.test(entry.name))
    .map((entry) => path.join(DRIVER_SIGNATURE_DIR, entry.name));
  for (const siteYearDir of siteYearDirs) {
    const candidates = [
      path.join(siteYearDir, safeName),
      path.join(siteYearDir, `${safeName}.jpg`),
      path.join(siteYearDir, `${safeName}.jpeg`),
      path.join(siteYearDir, `${safeName}.png`)
    ];
    for (const candidate of candidates) {
      if (fs.existsSync(candidate)) {
        return candidate;
      }
    }
  }
  return null;
};

const getGeneralUploadSubDir = (name = '') => {
  const safeName = `${name || ''}`.trim();
  if (safeName.startsWith('center_director_signature_site_')) {
    return path.join('center', 'signature');
  }
  if (/^[a-f0-9]{24}$/i.test(safeName)) {
    return path.join('customer', 'avatar');
  }
  return 'misc';
};

const findFileByBaseNameInDir = async (targetDir, fileBaseName = '') => {
  const safeName = `${fileBaseName || ''}`.trim();
  if (!safeName || !fs.existsSync(targetDir)) return null;
  const candidates = [
    path.join(targetDir, safeName),
    path.join(targetDir, `${safeName}.jpg`),
    path.join(targetDir, `${safeName}.jpeg`),
    path.join(targetDir, `${safeName}.png`)
  ];
  for (const candidate of candidates) {
    if (fs.existsSync(candidate)) {
      return candidate;
    }
  }
  return null;
};

const findGeneralFilePath = async (fileBaseName = '') => {
  const safeName = `${fileBaseName || ''}`.trim();
  if (!safeName) return null;
  const preferredDir = path.join(GENERAL_UPLOAD_DIR, getGeneralUploadSubDir(safeName));
  const preferred = await findFileByBaseNameInDir(preferredDir, safeName);
  if (preferred) return preferred;
  if (!fs.existsSync(GENERAL_UPLOAD_DIR)) return null;
  const firstLevel = await readdir(GENERAL_UPLOAD_DIR, { withFileTypes: true });
  for (const entry of firstLevel) {
    if (!entry.isDirectory()) continue;
    const firstDir = path.join(GENERAL_UPLOAD_DIR, entry.name);
    const directMatch = await findFileByBaseNameInDir(firstDir, safeName);
    if (directMatch) return directMatch;
    const secondLevel = await readdir(firstDir, { withFileTypes: true });
    for (const entry2 of secondLevel) {
      if (!entry2.isDirectory()) continue;
      const secondDir = path.join(firstDir, entry2.name);
      const secondMatch = await findFileByBaseNameInDir(secondDir, safeName);
      if (secondMatch) return secondMatch;
    }
  }
  return null;
};

const getLegacySignatureFromMongo = async (fileName = '') => {
  const safeName = `${fileName || ''}`.trim();
  if (!safeName) return null;
  try {
    await mongoClient.connect();
    const database = mongoClient.db(dbConfig.database);
    const images = database.collection(`${dbConfig.imgBucket}.files`);
    const chunks = database.collection(`${dbConfig.imgBucket}.chunks`);
    const cursor = await images.find({ filename: safeName }).toArray();
    if (!cursor.length) return null;
    const latestFile = cursor[cursor.length - 1];
    const chunkCursor = await chunks.find({ files_id: latestFile._id }).toArray();
    if (!chunkCursor.length) return null;
    return chunkCursor[0].data;
  } catch (_error) {
    return null;
  }
};

const uploadFiles = async (req, res) => {
  try {
    const fileName = `${req?.params?.filename || ''}`.trim();
    if (!fileName) {
      return res.status(400).send({ message: "Invalid filename." });
    }
    if (!isDriverSignatureFileName(fileName)) {
      return res.status(400).send({ message: "This endpoint is only for driver signature upload." });
    }
    await uploadToMemoryAsync(req, res);
    if (!req.file) {
      return res.status(400).send({ message: "You must select a file." });
    }
    const siteYearFolder = getDriverSignatureSiteYearFolder(req);
    const targetDir = path.join(DRIVER_SIGNATURE_DIR, siteYearFolder);
    if (!fs.existsSync(targetDir)) {
      fs.mkdirSync(targetDir, { recursive: true });
    }
    const ext = resolveSignatureExt(req.file?.mimetype, req.file?.originalname);
    const targetFilePath = path.join(targetDir, `${fileName}${ext}`);
    const resizedSignature = await resizeDriverSignatureBuffer(req.file.buffer, ext);
    fs.writeFileSync(targetFilePath, resizedSignature);
    return res.send({
      message: "Driver signature has been uploaded.",
      path: targetFilePath
    });
  } catch (error) {
    console.log(error);
    return res.status(500).send({
      message: error.message || "Error when trying upload signature."
    });
  }
};

const uploadGeneralFile = async (req, res) => {
  try {
    const fileName = `${req?.params?.filename || ''}`.trim();
    if (!fileName) {
      return res.status(400).send({ message: "Invalid filename." });
    }
    await uploadToMemoryAsync(req, res);
    if (!req.file) {
      return res.status(400).send({ message: "You must select a file." });
    }
    const targetDir = path.join(GENERAL_UPLOAD_DIR, getGeneralUploadSubDir(fileName));
    if (!fs.existsSync(targetDir)) {
      fs.mkdirSync(targetDir, { recursive: true });
    }
    const ext = resolveSignatureExt(req.file?.mimetype, req.file?.originalname);
    const targetFilePath = path.join(targetDir, `${fileName}${ext}`);
    fs.writeFileSync(targetFilePath, req.file.buffer);
    return res.status(200).send({
      message: "File has been uploaded.",
      path: targetFilePath
    });
  } catch (error) {
    return res.status(500).send({
      message: error.message || "Error when trying upload file."
    });
  }
};

const getFile = async (req, res) => {
  try {
    const fileName = decodeURIComponent(`${req?.params?.name || ''}`.trim());
    if (!fileName) {
      return res.status(400).send({
        message: "Invalid file name",
      });
    }
    const signaturePath = isDriverSignatureFileName(fileName)
      ? await findDriverSignatureFilePath(fileName)
      : null;
    const generalPath = signaturePath ? null : await findGeneralFilePath(fileName);
    const targetPath = signaturePath || generalPath;
    if (targetPath) {
      const fileBuffer = await fs.promises.readFile(targetPath);
      return res.status(200).send(fileBuffer.toString('base64'));
    }

    // Legacy fallback for old driver signatures kept in Mongo/GridFS.
    if (isDriverSignatureFileName(fileName)) {
      const legacyData = await getLegacySignatureFromMongo(fileName);
      if (legacyData) {
        return res.status(200).send(legacyData);
      }
    }

    if (!targetPath) {
      return res.status(500).send({
        message: "No files found!",
      });
    }
  } catch (error) {
    return res.status(500).send({
      message: error.message,
    });
  }
};

const deleteFile = async (req, res) => {
  try {
    const targetName = `${req?.body?.name || ''}`.trim();
    if (!targetName) {
      return res.status(400).send({ message: 'Invalid file name' });
    }
    const signaturePath = isDriverSignatureFileName(targetName)
      ? await findDriverSignatureFilePath(targetName)
      : null;
    const generalPath = signaturePath ? null : await findGeneralFilePath(targetName);
    const targetPath = signaturePath || generalPath;
    if (targetPath && fs.existsSync(targetPath)) {
      await fs.promises.unlink(targetPath);
    }
    return res.status(200).send({ message: 'Delete Image Succeed'});
  } catch (error) {
    return res.status(500).send({
      message: error.message,
    });
  }
};

const uploadPhysicalFile = async (req, res) => {
  try {
    // Destination and file name would be adjusted to be the same with original file system
    const {objectId, name, fileType, model} = req.query;
    const uploadedFile = req.file;
    if (!uploadedFile) {
      return res.status(400).send({
        message: 'No file uploaded.'
      })
    }
    return res.status(200).send({
      message: `File ${fileType} Uploaded Successfully for ${model} ${objectId}-${name}`
    })
    
  } catch (error) {
    return res.status(500).send({
      message: error.message,
    });
  }
}

const deletePhysicalFile = async (req, res) => {
  try {
    const { objectId, fileType, model, fileName } = req.body || {};
    if (!objectId || !fileType || !model || !fileName) {
      return res.status(400).send({ message: 'Required fields missed' });
    }

    const BASE_UPLOAD_DIR = `/www/wwwroot/upload/`;
    const targetFilePath = path.join(BASE_UPLOAD_DIR, model, objectId, fileType, fileName);

    if (!fs.existsSync(targetFilePath)) {
      return res.status(200).send({ message: 'File already removed.' });
    }

    await fs.promises.unlink(targetFilePath);
    return res.status(200).send({ message: 'File deleted successfully.' });
  } catch (error) {
    return res.status(500).send({
      message: error.message,
    });
  }
}

const getFilesByType = async (req, res) => {
  try {
    const {objectId, fileType, name, model} = req.params;

    if (!objectId || !name || !fileType || !model) {
      return res.status(400).send({message: 'Required fields missed'});
    }

    const BASE_UPLOAD_DIR = `/www/wwwroot/upload/`; 
    const filesDir = `${BASE_UPLOAD_DIR}${model}/${objectId}/${fileType}/`;

    if (!fs.existsSync(filesDir)) {
      return res.status(200).send({
        data: {
          objectId,
          model,
          fileType,
          name,
          files: [],
          count: 0
        }
      })
    }

    const fileEntries = await readdir(filesDir, { withFileTypes: true});

    const filePromises = fileEntries.filter(entry => entry.isFile()).map(async (entry) => {
      const filePath = path.join(filesDir, entry.name);
      const stats = await stat(filePath);

      const fileUrl = `/files/${model}/${objectId}/${fileType}/${entry.name}`;
      return {
        name: entry.name,
        url: fileUrl,
        size: stats.size,
        extension: path.extname(entry.name),
        createdAt: stats.birthtime,
        modifiedAt: stats.mtime
      }
    });
    const files = await Promise.all(filePromises);
    return res.status(200).json({
      data: {
        objectId,
        fileType,
        name,
        files,
        count: files.length
      }
    })

  } catch(err) {
    return res.status(500).send({
      message: err.message
    })
  }
}



module.exports = {
  uploadFiles,
  uploadGeneralFile,
  getFile,
  deleteFile,
  uploadPhysicalFile,
  deletePhysicalFile,
  getFilesByType
};