import crypto from "crypto";
import { promises as fs } from "fs";
import path from "path";
import sharp from "sharp";
import { isSafeFilename, resolveUploadPath } from "@/server/uploads/paths";

export interface UploadedImage {
  filename: string;
  path: string;
  url: string;
  originalFilename?: string;
  originalUrl?: string;
}

export class ImageUploadService {
  private readonly uploadDir: string;
  private readonly allowedMimeTypes = ["image/jpeg", "image/png", "image/gif", "image/webp"];
  private readonly maxFileSize = 25 * 1024 * 1024; // 25MB

  constructor() {
    // Prefer explicit env configuration (Docker/VPS should set UPLOAD_DIR=/app/uploads).
    // Fallback to a local folder to avoid build-time filesystem issues.
    this.uploadDir = process.env.UPLOAD_DIR || path.join(process.cwd(), "uploads");
  }

  private async ensureUploadDir() {
    try {
      await fs.mkdir(this.uploadDir, { recursive: true });
      console.log(`Upload directory ensured: ${this.uploadDir}`);
    } catch (error) {
      console.error("Failed to create upload directory:", error);
      console.error("Upload directory path:", this.uploadDir);
      console.error("Current working directory:", process.cwd());
      console.error("NODE_ENV:", process.env.NODE_ENV);
      throw new Error("Failed to initialize upload directory");
    }
  }

  private generateUniqueFilename(originalFilename: string, extOverride?: string): string {
    const timestamp = Date.now();
    const randomString = crypto.randomBytes(4).toString("hex");
    const extension = extOverride || path.extname(originalFilename);
    const shortName = originalFilename.slice(0, 10).replace(/[^a-zA-Z0-9]/g, "");
    return `${timestamp}-${shortName}-${randomString}${extension}`;
  }

  private validateFile(file: File): void {
    if (!this.allowedMimeTypes.includes(file.type)) {
      throw new Error(`Invalid file type. Allowed types: ${this.allowedMimeTypes.join(", ")}`);
    }

    if (file.size > this.maxFileSize) {
      throw new Error(`File too large. Maximum size: ${this.maxFileSize / 1024 / 1024}MB`);
    }
  }

  async uploadImage(file: File): Promise<UploadedImage> {
    await this.ensureUploadDir();
    this.validateFile(file);

    const originalFilename = this.generateUniqueFilename(file.name);
    const webpFilename = this.generateUniqueFilename(file.name, ".webp");
    const originalFilepath = path.join(this.uploadDir, originalFilename);
    const webpFilepath = path.join(this.uploadDir, webpFilename);

    try {
      // Read the file as ArrayBuffer and convert to Buffer
      const arrayBuffer = await file.arrayBuffer();
      const buffer = Buffer.from(arrayBuffer);

      // Save original (temporarily)
      await fs.writeFile(originalFilepath, buffer);
      // Convert to WebP
      await sharp(buffer).webp({ quality: 85 }).toFile(webpFilepath);
      // Delete the original file after conversion
      await fs.unlink(originalFilepath);
      return {
        filename: webpFilename,
        path: webpFilepath,
        url: `/uploads/${webpFilename}`,
      };
    } catch (error) {
      console.error("Failed to save/convert file:", error);
      throw new Error("Failed to save/convert uploaded file");
    }
  }

  async uploadMultipleImages(files: File[]): Promise<UploadedImage[]> {
    await this.ensureUploadDir();
    const uploadPromises = files.map((file) => this.uploadImage(file));
    return Promise.all(uploadPromises);
  }

  async deleteImage(filename: string): Promise<void> {
    // Validate filename to prevent path traversal attacks
    if (!isSafeFilename(filename)) {
      throw new Error("Invalid filename");
    }

    await this.ensureUploadDir();
    const filepath = resolveUploadPath(filename);
    try {
      await fs.unlink(filepath);
      // Also try to delete the original file if it exists (jpg, jpeg, png)
      const base = filename.replace(/\.webp$/, "");
      for (const ext of [".jpg", ".jpeg", ".png"]) {
        const origFilename = base + ext;
        if (isSafeFilename(origFilename)) {
          try {
            const origPath = resolveUploadPath(origFilename);
            await fs.unlink(origPath);
          } catch {}
        }
      }
    } catch (error) {
      console.error("Failed to delete file:", error);
      throw new Error("Failed to delete file");
    }
  }
}
