import { randomUUID } from "node:crypto";
import { writeFileSync, mkdirSync, existsSync, readFileSync, unlinkSync, readdirSync, statSync } from "fs";
import { join, extname } from "path";
import crypto from "crypto";
import { DATA_DIR } from "../utils/paths.js";
const IMAGES_DIR = join(DATA_DIR, "images");
// Ensure images directory exists
mkdirSync(IMAGES_DIR, { recursive: true });
// UUID v4 format: 8-4-4-4-12 hex characters
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
function isValidImageId(imageId) {
    return UUID_REGEX.test(imageId);
}
const ALLOWED_MIME_TYPES = ["image/png", "image/jpeg", "image/jpg", "image/gif", "image/webp"];
const MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
export class ImageStorageService {
    /**
     * Store an uploaded image file
     */
    static async storeImage(buffer, originalName, mimeType) {
        try {
            // Validate file type
            if (!ALLOWED_MIME_TYPES.includes(mimeType)) {
                return {
                    success: false,
                    error: `Invalid file type: ${mimeType}. Allowed types: ${ALLOWED_MIME_TYPES.join(", ")}`,
                };
            }
            // Validate file size
            if (buffer.length > MAX_FILE_SIZE) {
                return {
                    success: false,
                    error: `File size exceeds ${MAX_FILE_SIZE / 1024 / 1024}MB limit`,
                };
            }
            // Generate SHA256 hash for deduplication
            const sha256 = crypto.createHash("sha256").update(buffer).digest("hex");
            // Generate unique filename
            const id = randomUUID();
            const ext = extname(originalName) || this.getExtensionFromMimeType(mimeType);
            const storedAs = `${id}${ext}`;
            const storagePath = join(IMAGES_DIR, storedAs);
            // Check for existing file with same hash (optional deduplication)
            // For now, we'll store each upload separately for simplicity
            // Write file to disk
            writeFileSync(storagePath, buffer);
            const image = {
                id,
                filename: storedAs,
                originalName,
                storedAs,
                mimeType,
                size: buffer.length,
                sha256,
                uploadedAt: new Date().toISOString(),
                storagePath,
            };
            return {
                success: true,
                image,
            };
        }
        catch (error) {
            return {
                success: false,
                error: `Failed to store image: ${error instanceof Error ? error.message : "Unknown error"}`,
            };
        }
    }
    /**
     * Retrieve an image by ID
     */
    static getImage(imageId) {
        try {
            // Validate image ID is a proper UUID to prevent directory traversal
            if (!isValidImageId(imageId)) {
                return null;
            }
            const files = readdirSync(IMAGES_DIR);
            const imageFile = files.find((f) => f.startsWith(imageId));
            if (!imageFile) {
                return null;
            }
            const imagePath = join(IMAGES_DIR, imageFile);
            if (!existsSync(imagePath))
                return null;
            const buffer = readFileSync(imagePath);
            const stats = statSync(imagePath);
            // Reconstruct image metadata (in production, store this in DB)
            const image = {
                id: imageId,
                filename: imageFile,
                originalName: imageFile,
                storedAs: imageFile,
                mimeType: this.getMimeTypeFromExtension(extname(imageFile)),
                size: stats.size,
                sha256: crypto.createHash("sha256").update(buffer).digest("hex"),
                uploadedAt: stats.birthtime.toISOString(),
                storagePath: imagePath,
            };
            return { buffer, image };
        }
        catch {
            return null;
        }
    }
    /**
     * Delete an image by ID
     */
    static deleteImage(imageId) {
        try {
            // Validate image ID is a proper UUID to prevent directory traversal
            if (!isValidImageId(imageId)) {
                return false;
            }
            const files = readdirSync(IMAGES_DIR);
            const imageFile = files.find((f) => f.startsWith(imageId));
            if (!imageFile)
                return false;
            const imagePath = join(IMAGES_DIR, imageFile);
            if (existsSync(imagePath)) {
                unlinkSync(imagePath);
                return true;
            }
            return false;
        }
        catch {
            return false;
        }
    }
    /**
     * Load image buffers for a list of image IDs.
     * Returns an array of { buffer, mimeType } for successfully loaded images.
     * Logs errors for individual failures without throwing.
     */
    static loadImageBuffers(imageIds) {
        const results = [];
        for (const imageId of imageIds) {
            try {
                const result = ImageStorageService.getImage(imageId);
                if (result) {
                    results.push({
                        buffer: result.buffer,
                        mimeType: result.image.mimeType,
                    });
                }
            }
            catch (error) {
                console.error(`Failed to load image ${imageId}:`, error);
            }
        }
        return results;
    }
    /**
     * Get file extension from MIME type
     */
    static getExtensionFromMimeType(mimeType) {
        const mapping = {
            "image/png": ".png",
            "image/jpeg": ".jpg",
            "image/jpg": ".jpg",
            "image/gif": ".gif",
            "image/webp": ".webp",
        };
        return mapping[mimeType] || ".bin";
    }
    /**
     * Get MIME type from file extension
     */
    static getMimeTypeFromExtension(ext) {
        const mapping = {
            ".png": "image/png",
            ".jpg": "image/jpeg",
            ".jpeg": "image/jpeg",
            ".gif": "image/gif",
            ".webp": "image/webp",
        };
        return mapping[ext.toLowerCase()] || "application/octet-stream";
    }
}
/**
 * Store an image from base64 data (e.g. extracted from session log).
 * Uses SHA256 dedup: if an image with the same hash already exists on disk,
 * returns the existing ID without writing a new file.
 */
export function storeBase64Image(base64Data, mimeType) {
    try {
        const buffer = Buffer.from(base64Data, "base64");
        const sha256 = crypto.createHash("sha256").update(buffer).digest("hex");
        // Check if already stored by scanning existing files for matching hash
        const cached = base64ImageCache.get(sha256);
        if (cached)
            return cached;
        // Scan disk for existing file with same hash
        if (existsSync(IMAGES_DIR)) {
            for (const file of readdirSync(IMAGES_DIR)) {
                const filePath = join(IMAGES_DIR, file);
                try {
                    const existing = readFileSync(filePath);
                    const existingHash = crypto.createHash("sha256").update(existing).digest("hex");
                    if (existingHash === sha256) {
                        const existingId = file.split(".")[0];
                        base64ImageCache.set(sha256, existingId);
                        return existingId;
                    }
                }
                catch { }
            }
        }
        // Not found — store it
        const id = randomUUID();
        const ext = ImageStorageService["getExtensionFromMimeType"](mimeType);
        const storedAs = `${id}${ext}`;
        mkdirSync(IMAGES_DIR, { recursive: true });
        writeFileSync(join(IMAGES_DIR, storedAs), buffer);
        base64ImageCache.set(sha256, id);
        return id;
    }
    catch {
        return null;
    }
}
// In-memory SHA256 → imageId cache to avoid repeated disk scans
const base64ImageCache = new Map();
/** Convenience re-export of ImageStorageService.loadImageBuffers for direct import. */
export const loadImageBuffers = ImageStorageService.loadImageBuffers.bind(ImageStorageService);
