import fs from "fs";
import multer, { type FileFilterCallback } from "multer";
import path from "path";
import type { Request } from "express";
import { HttpError } from "../middleware/errorHandler";
import { env } from "./env";

export const uploadRoot = path.resolve(process.cwd(), env.UPLOAD_DIR);

for (const sub of ["avatars", "attachments", "board-backgrounds", "workspace-logos", "organization-logos", "drive"]) {
  fs.mkdirSync(path.join(uploadRoot, sub), { recursive: true });
}

function storageFor(subdir: string) {
  return multer.diskStorage({
    destination: (_req, _file, cb) => cb(null, path.join(uploadRoot, subdir)),
    filename: (_req, file, cb) => {
      const ext = path.extname(file.originalname);
      cb(null, `${Date.now()}-${Math.round(Math.random() * 1e9)}${ext}`);
    },
  });
}

const IMAGE_MIME_TYPES = new Set(["image/jpeg", "image/png", "image/gif", "image/webp"]);

// Avatars, board backgrounds, and workspace logos are always meant to be images —
// enforced strictly. SVG is deliberately excluded: it can embed <script>/event
// handlers and would be stored-XSS if opened directly from /uploads.
function imageOnlyFilter(_req: Request, file: Express.Multer.File, cb: FileFilterCallback) {
  if (IMAGE_MIME_TYPES.has(file.mimetype)) {
    cb(null, true);
    return;
  }
  cb(new HttpError(400, "Only JPEG, PNG, GIF, or WebP images are allowed"));
}

// Attachments and Drive files legitimately need to hold arbitrary business
// documents, so we deny-list executable/script/markup types that could be
// abused for stored XSS or drive-by execution if opened directly from
// /uploads, rather than maintaining a narrow allow-list.
const DANGEROUS_MIME_TYPES = new Set([
  "text/html",
  "application/xhtml+xml",
  "image/svg+xml",
  "application/x-msdownload",
  "application/x-msdos-program",
  "application/vnd.microsoft.portable-executable",
  "application/x-sh",
  "application/x-bat",
  "application/javascript",
  "text/javascript",
]);
const DANGEROUS_EXTENSIONS = new Set([
  ".html", ".htm", ".svg", ".exe", ".bat", ".cmd", ".sh", ".js", ".mjs", ".msi", ".com", ".scr", ".jar", ".ps1",
]);

function safeDocumentFilter(_req: Request, file: Express.Multer.File, cb: FileFilterCallback) {
  const ext = path.extname(file.originalname).toLowerCase();
  if (DANGEROUS_MIME_TYPES.has(file.mimetype) || DANGEROUS_EXTENSIONS.has(ext)) {
    cb(new HttpError(400, "This file type isn't allowed"));
    return;
  }
  cb(null, true);
}

export const uploadAvatar = multer({
  storage: storageFor("avatars"),
  limits: { fileSize: 5 * 1024 * 1024 },
  fileFilter: imageOnlyFilter,
});

export const uploadAttachment = multer({
  storage: storageFor("attachments"),
  limits: { fileSize: 25 * 1024 * 1024 },
  fileFilter: safeDocumentFilter,
});

export const uploadBoardBackground = multer({
  storage: storageFor("board-backgrounds"),
  limits: { fileSize: 8 * 1024 * 1024 },
  fileFilter: imageOnlyFilter,
});

export const uploadWorkspaceLogo = multer({
  storage: storageFor("workspace-logos"),
  limits: { fileSize: 5 * 1024 * 1024 },
  fileFilter: imageOnlyFilter,
});

export const uploadOrganizationLogo = multer({
  storage: storageFor("organization-logos"),
  limits: { fileSize: 5 * 1024 * 1024 },
  fileFilter: imageOnlyFilter,
});

export const uploadDriveFile = multer({
  storage: storageFor("drive"),
  limits: { fileSize: 50 * 1024 * 1024 },
  fileFilter: safeDocumentFilter,
});
