import {
  BadRequestException,
  CallHandler,
  Controller,
  ExecutionContext,
  Get,
  Injectable,
  NestInterceptor,
  NotFoundException,
  Param,
  Post,
  Res,
  UploadedFile,
  UseInterceptors,
} from "@nestjs/common";
import type { Observable } from "rxjs";
import type { Response } from "express";
import { mkdirSync } from "node:fs";
import { open, rename, unlink } from "node:fs/promises";
import { basename, extname, resolve } from "node:path";
import { randomUUID } from "node:crypto";
import multer, { diskStorage } from "multer";
import { Throttle } from "@nestjs/throttler";
import { Public } from "../../common/decorators/public.decorator";

const uploadDirectory = resolve(process.cwd(), "storage", "uploads");
mkdirSync(uploadDirectory, { recursive: true });

const allowedMimeTypes = new Set(["image/jpeg", "image/png", "image/webp"]);
const allowedExtensions = new Set([".jpg", ".jpeg", ".png", ".webp"]);
const uploadLimits = {
  fileSize: 5 * 1024 * 1024,
  files: 1,
  fields: 0,
  parts: 2,
  fieldNameSize: 32,
  headerPairs: 20,
  fieldArrayIndexLimit: 0,
};

function detectImageExtension(bytes: Uint8Array) {
  if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) return ".jpg";
  if (bytes.length >= 8 && bytes.slice(0, 8).every((value, index) => value === [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a][index])) return ".png";
  if (bytes.length >= 12 && Buffer.from(bytes.slice(0, 4)).toString("ascii") === "RIFF" && Buffer.from(bytes.slice(8, 12)).toString("ascii") === "WEBP") return ".webp";
  return null;
}

@Injectable()
class MediaUploadInterceptor implements NestInterceptor {
  private readonly middleware = multer({
    storage: diskStorage({
      destination: uploadDirectory,
      filename: (_request, file, callback) => {
        const requestedExtension = extname(file.originalname).toLowerCase();
        const extension = allowedExtensions.has(requestedExtension) ? requestedExtension : ".jpg";
        callback(null, `${randomUUID()}${extension}`);
      },
    }),
    limits: uploadLimits,
    fileFilter: (_request, file, callback) => {
      const valid = allowedMimeTypes.has(file.mimetype) || (file.mimetype === "application/octet-stream" && allowedExtensions.has(extname(file.originalname).toLowerCase()));
      if (valid) callback(null, true);
      else callback(new BadRequestException("unsupported_image_type"));
    },
  }).single("file");

  async intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<unknown>> {
    const http = context.switchToHttp();
    await new Promise<void>((resolveUpload, rejectUpload) => {
      this.middleware(http.getRequest(), http.getResponse(), (error) => {
        if (error) rejectUpload(error instanceof BadRequestException ? error : new BadRequestException("invalid_upload"));
        else resolveUpload();
      });
    });
    return next.handle();
  }
}

@Controller("media")
export class MediaController {
  @Post("upload")
  @Throttle({ default: { limit: 20, ttl: 60_000, blockDuration: 60_000 } })
  @UseInterceptors(
    MediaUploadInterceptor,
  )
  async upload(@UploadedFile() file?: Express.Multer.File) {
    if (!file) throw new BadRequestException("image_required");
    try {
      const handle = await open(file.path, "r");
      const header = Buffer.alloc(16);
      try {
        await handle.read(header, 0, header.length, 0);
      } finally {
        await handle.close();
      }
      const extension = detectImageExtension(header);
      if (!extension) throw new BadRequestException("unsupported_image_content");
      const filename = `${basename(file.filename, extname(file.filename))}${extension}`;
      const finalPath = resolve(uploadDirectory, filename);
      if (finalPath !== file.path) await rename(file.path, finalPath);
      const mimeType = extension === ".jpg" ? "image/jpeg" : extension === ".png" ? "image/png" : "image/webp";
      return { url: `/api/media/${filename}`, mimeType, size: file.size };
    } catch (error) {
      await unlink(file.path).catch(() => undefined);
      throw error;
    }
  }

  @Public()
  @Get(":filename")
  get(@Param("filename") filename: string, @Res() response: Response) {
    if (!/^[a-f0-9-]+\.(jpe?g|png|webp)$/i.test(filename)) throw new NotFoundException("media_not_found");
    response.setHeader("Cache-Control", "public, max-age=31536000, immutable");
    response.setHeader("X-Content-Type-Options", "nosniff");
    return response.sendFile(filename, { root: uploadDirectory }, (error) => {
      if (error && !response.headersSent) response.status(404).json({ error: { message: "media_not_found" } });
    });
  }
}
