import multer, { type StorageEngine } from "multer";
import cloudinary from "../../config/cloudinary";
import { Request } from "express";
import { prisma } from "../../config/prisma";

type CloudinaryStorageOptions = {
  cloudinary: typeof import("cloudinary").v2;
  params?: (req: Request, file: Express.Multer.File) => Record<string, any> | Promise<Record<string, any>>;
};

class CloudinaryStorage implements StorageEngine {
  private readonly cloudinary: typeof import("cloudinary").v2;
  private readonly params: NonNullable<CloudinaryStorageOptions["params"]>;

  constructor(options: CloudinaryStorageOptions) {
    this.cloudinary = options.cloudinary;
    this.params = options.params ?? (() => ({}));
  }

  async _handleFile(req: Request, file: Express.Multer.File, cb: (error?: Error | null, info?: Partial<Express.Multer.File>) => void) {
    try {
      const uploadParams = await this.params(req, file);
      const uploadStream = this.cloudinary.uploader.upload_stream(
        {
          ...uploadParams,
          resource_type: uploadParams.resource_type ?? "auto",
        },
        (error, result) => {
          if (error) return cb(error);
          if (!result) return cb(new Error("Cloudinary upload failed"));

          file.path = result.secure_url;
          file.filename = result.public_id;
          file.size = result.bytes ?? file.size;
          cb(null, file);
        }
      );

      file.stream.pipe(uploadStream);
    } catch (error) {
      cb(error instanceof Error ? error : new Error(String(error)));
    }
  }

  _removeFile(_req: Request, _file: Express.Multer.File, cb: (error: Error | null) => void) {
    cb(null);
  }
}

const allowedMimeTypes = [
  "application/pdf",
  "image/jpeg",
  "image/png",
  "image/webp",
  "application/msword",
  "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  "application/vnd.ms-excel",
  "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
];

// Resolves client.id + name + serviceCategory from the logged-in user BEFORE multer builds the folder path
export async function attachClientContext(req: Request, _res: any, next: any) {
  const client = await prisma.client.findUnique({
    where: { userId: (req as any).user!.id },
    select: { id: true, name: true },
  });
  let serviceCategory = "general";
  if (req.body?.serviceId) {
    const svc = await prisma.service.findUnique({
      where: { id: req.body.serviceId },
      select: { category: true },
    });
    if (svc) serviceCategory = svc.category.toLowerCase();
  }
  (req as any).clientContext = client ? { ...client, serviceCategory } : null;
  next();
}

const MONTH_NAMES = ["January","February","March","April","May","June","July","August","September","October","November","December"];

const storage = new CloudinaryStorage({
  cloudinary,
  params: async (req: Request, _file: Express.Multer.File) => {
    const ctx = (req as any).clientContext;
    const clientSlug = (ctx?.name ?? "unassigned").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/(^-|-$)/g, "");
    const now = new Date();
    const year = now.getFullYear();
    const month = MONTH_NAMES[now.getMonth()];
    const category = (ctx?.serviceCategory ?? "general").toLowerCase();
    return {
      folder: `taxfend/${clientSlug}/${year}/${month}/${category}`,
      resource_type: "auto",
      public_id: `${Date.now()}-${_file.originalname.split(".")[0]}`,
    };
  },
});

// For admin uploads — resolves client name + service category from request body or existing task
export async function attachAdminClientContext(req: Request, _res: any, next: any) {
  let clientName = "unassigned";
  let serviceCategory = "general";

  // If taskId param exists (document upload to existing task)
  if (req.params?.taskId) {
    const task = await prisma.task.findUnique({
      where: { id: req.params.taskId },
      include: {
        client: { select: { name: true } },
        service: { select: { category: true } },
      },
    });
    if (task) {
      clientName = task.client.name;
      serviceCategory = task.service.category.toLowerCase();
    }
  } else {
    if (req.body?.clientId) {
      const client = await prisma.client.findUnique({
        where: { id: req.body.clientId },
        select: { name: true },
      });
      if (client) clientName = client.name;
    }
    if (req.body?.serviceId) {
      const svc = await prisma.service.findUnique({
        where: { id: req.body.serviceId },
        select: { category: true },
      });
      if (svc) serviceCategory = svc.category.toLowerCase();
    }
  }

  (req as any).clientContext = { name: clientName, serviceCategory };
  next();
}

export const uploadTaskDocuments = multer({
  storage,
  limits: { fileSize: 10 * 1024 * 1024, files: 10 },
  fileFilter: (_req, file, cb) => {
    if (!allowedMimeTypes.includes(file.mimetype)) {
      return cb(new Error("Unsupported file type"));
    }
    cb(null, true);
  },
}).array("files", 10);


// same allowedMimeTypes, attachClientContext, storage as before...

export const uploadAnyTaskFiles = multer({
  storage,
  limits: { fileSize: 10 * 1024 * 1024, files: 15 },
  fileFilter: (_req, file, cb) => {
    if (!allowedMimeTypes.includes(file.mimetype)) {
      return cb(new Error("Unsupported file type"));
    }
    cb(null, true);
  },
}).any(); // fieldname = serviceDocumentId, or "other" for generic uploads