import { Request, Response, NextFunction, RequestHandler } from "express";
import path from "path";
import fs from "fs/promises";
import { existsSync } from "fs";
import multer from "multer";
import { v4 as uuidv4 } from "uuid";

const UPLOAD_BASE_DIR = "/var/www/nytsolvers/dist/client";

// Ensure the base upload directory exists
if (!existsSync(UPLOAD_BASE_DIR)) {
  fs.mkdir(UPLOAD_BASE_DIR, { recursive: true });
}

// Multer storage configuration
const storage = multer.diskStorage({
destination: (req, file, cb) => {
  const subfolder =
    (req.body && req.body.subfolder) ||
    (req.query && req.query.subfolder) ||
    "";

  const uploadPath = path.join(UPLOAD_BASE_DIR, subfolder);

  fs.mkdir(uploadPath, { recursive: true })
    .then(() => cb(null, uploadPath))
    .catch((err) => cb(new Error(`Cannot create upload directory: ${err.message}`), ""));
},

  filename: (req, file, cb) => {
    const customName = req.body.customName || req.body.customName || "";
    const baseName = customName.trim() ? customName.trim() : file.originalname;
    const ext = path.extname(baseName);
    const nameWithoutExt = path.basename(baseName, ext);
    const uniqueSuffix = uuidv4();
    cb(null, nameWithoutExt + '-' + uniqueSuffix + ext);
  },
});


export const upload = multer({
  storage: storage,
  fileFilter: (req, file, cb) => {
    cb(null, true); // Accept all files
  },
  limits: {
    fileSize: 10 * 1024 * 1024, // 10MB
  },
});

export class FileManagerController {
  private sendError(res: Response, status: number, message: string): void {
    res.status(status).json({ message });
  }

  private sendSuccess(res: Response, message: string, data?: any): void {
    res.status(200).json({ success: true, message, data });
  }

  listContents: RequestHandler = async (req, res) => {
    const { subfolder = "" } = req.query;
    const currentDir = path.join(UPLOAD_BASE_DIR, subfolder as string);

    if (!currentDir.startsWith(UPLOAD_BASE_DIR)) {
      return this.sendError(res, 400, "Invalid path");
    }

    try {
      const contents = await fs.readdir(currentDir, { withFileTypes: true });
      const files = [];
      const folders = [];

      for (const item of contents) {
        if (item.isDirectory()) {
          folders.push({ name: item.name, type: "folder" });
        } else {
          const filePath = path.join(currentDir, item.name);
          const fileUrl = `${process.env.FRONTEND_DOMAIN}/${path.relative(UPLOAD_BASE_DIR, filePath).replace(/\\/g, '/')}`;
          files.push({ name: item.name, type: "file", url: fileUrl });
        }
      }

      this.sendSuccess(res, "Contents listed successfully", { folders, files });
    } catch (error: any) {
      if (error.code === "ENOENT") {
        return this.sendError(res, 404, "Directory not found");
      }
      console.error("Error listing directory contents:", error);
      this.sendError(res, 500, "Failed to list directory contents");
    }
  };

  createFolder: RequestHandler = async (req, res) => {
    const { folderName, subfolder = "" } = req.body;
    if (!folderName) {
      return this.sendError(res, 400, "Folder name is required");
    }

    const newFolderPath = path.join(UPLOAD_BASE_DIR, subfolder as string, folderName);

    if (!newFolderPath.startsWith(UPLOAD_BASE_DIR)) {
      return this.sendError(res, 400, "Invalid path");
    }

    try {
      await fs.mkdir(newFolderPath, { recursive: true });
      this.sendSuccess(res, `Folder '${folderName}' created successfully`);
    } catch (error: any) {
      if (error.code === "EEXIST") {
        return this.sendError(res, 409, "Folder already exists");
      }
      console.error("Error creating folder:", error);
      this.sendError(res, 500, "Failed to create folder");
    }
  };

uploadFile: RequestHandler = async (req, res) => {
  upload.single("file")(req, res, async (err) => {
    console.log("Multer error:", err);
    console.log("Request file:", req.file);

    if (err instanceof multer.MulterError) {
      return this.sendError(res, 400, err.message);
    } else if (err) {
      console.error("File upload error:", err);
      return this.sendError(res, 500, "Failed to upload file");
    }

    if (!req.file) {
      return this.sendError(res, 400, "No file uploaded");
    }

    // ✅ Read subfolder from either body or query (safe fallback)
    const subfolder =
      (req.body && req.body.subfolder) ||
      (req.query && req.query.subfolder) ||
      "";

    // ✅ Build proper file path URL
    const filePath = path
      .join("/", subfolder, req.file.filename)
      .replace(/\\/g, "/");

    this.sendSuccess(res, "File uploaded successfully", {
      filename: req.file.filename,
      url: `${process.env.FRONTEND_DOMAIN}${filePath}`,
    });
  });
};


  deleteItem: RequestHandler = async (req, res) => {
    const { itemPath } = req.body;
    if (!itemPath) {
      return this.sendError(res, 400, "Item path is required");
    }

    const fullPath = path.join(UPLOAD_BASE_DIR, itemPath);

    if (!fullPath.startsWith(UPLOAD_BASE_DIR)) {
      return this.sendError(res, 400, "Invalid path");
    }

    try {
      const stats = await fs.stat(fullPath);
      if (stats.isDirectory()) {
        await fs.rm(fullPath, { recursive: true, force: true });
        this.sendSuccess(res, `Folder '${itemPath}' deleted successfully`);
      } else {
        await fs.unlink(fullPath);
        this.sendSuccess(res, `File '${itemPath}' deleted successfully`);
      }
    } catch (error: any) {
      if (error.code === "ENOENT") {
        return this.sendError(res, 404, "Item not found");
      }
      console.error("Error deleting item:", error);
      this.sendError(res, 500, "Failed to delete item");
    }
  };

  renameItem: RequestHandler = async (req, res) => {
    const { oldPath, newName } = req.body;
    if (!oldPath || !newName) {
      return this.sendError(res, 400, "Old path and new name are required");
    }

    const oldFullPath = path.join(UPLOAD_BASE_DIR, oldPath);
    const newFullPath = path.join(path.dirname(oldFullPath), newName);

    if (!oldFullPath.startsWith(UPLOAD_BASE_DIR) || !newFullPath.startsWith(UPLOAD_BASE_DIR)) {
      return this.sendError(res, 400, "Invalid path");
    }

    try {
      await fs.rename(oldFullPath, newFullPath);
      this.sendSuccess(res, `Item renamed to '${newName}' successfully`);
    } catch (error: any) {
      if (error.code === "ENOENT") {
        return this.sendError(res, 404, "Item not found");
      }
      if (error.code === "EEXIST") {
        return this.sendError(res, 409, "New name already exists");
      }
      console.error("Error renaming item:", error);
      this.sendError(res, 500, "Failed to rename item");
    }
  };

  copyUrl: RequestHandler = async (req, res) => {
    const { itemPath } = req.body;
    if (!itemPath) {
      return this.sendError(res, 400, "Item path is required");
    }

    const fullPath = path.join(UPLOAD_BASE_DIR, itemPath);

    if (!fullPath.startsWith(UPLOAD_BASE_DIR)) {
      return this.sendError(res, 400, "Invalid path");
    }

    try {
      const stats = await fs.stat(fullPath);
      if (!stats.isFile()) {
        return this.sendError(res, 400, "Only files have a direct URL");
      }

      const fileUrl = `${process.env.FRONTEND_DOMAIN}/${itemPath.replace(/\\/g, '/')}`;
      this.sendSuccess(res, "URL copied successfully", { url: fileUrl });
    } catch (error: any) {
      if (error.code === "ENOENT") {
        return this.sendError(res, 404, "File not found");
      }
      console.error("Error copying URL:", error);
      this.sendError(res, 500, "Failed to copy URL");
    }
  };
}
