import {
  BadRequestException,
  Controller,
  Post,
  Query,
  UploadedFile,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { mkdir, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { randomUUID } from 'node:crypto';
import sharp from 'sharp';
import { AdminGuard } from '../auth/admin.guard.js';

export const UPLOAD_ROOT = join(process.cwd(), 'uploads');

/** Longest edge per use, so the storefront never downloads more than it shows. */
const sizes = {
  product: 1400,
  banner: 2000,
  category: 900,
  logo: 600,
} as const;
type Kind = keyof typeof sizes;

const MAX_BYTES = 8 * 1024 * 1024;
const ACCEPTED = new Set(['image/jpeg', 'image/png', 'image/webp', 'image/avif']);

@UseGuards(AdminGuard)
@Controller('admin/uploads')
export class UploadsController {
  /**
   * Every upload is re-encoded to WebP: it is smaller than JPEG/PNG and the
   * re-encode drops anything that is not image data (scripts, EXIF/GPS).
   */
  @Post()
  @UseInterceptors(FileInterceptor('file', { limits: { fileSize: MAX_BYTES, files: 1 } }))
  async upload(
    @UploadedFile() file: { buffer: Buffer; mimetype: string } | undefined,
    @Query('kind') kindParam?: string,
  ) {
    if (!file) throw new BadRequestException('Choose an image to upload.');
    if (!ACCEPTED.has(file.mimetype)) {
      throw new BadRequestException('Upload a JPG, PNG, WebP or AVIF image.');
    }
    const kind: Kind = kindParam && kindParam in sizes ? (kindParam as Kind) : 'product';

    let output: { data: Buffer; info: sharp.OutputInfo };
    try {
      output = await sharp(file.buffer, { limitInputPixels: 50_000_000 })
        .rotate()
        .resize({ width: sizes[kind], height: sizes[kind], fit: 'inside', withoutEnlargement: true })
        .webp({ quality: kind === 'logo' ? 90 : 80, alphaQuality: 90 })
        .toBuffer({ resolveWithObject: true });
    } catch {
      throw new BadRequestException('That file could not be read as an image.');
    }

    const now = new Date();
    const folder = `${now.getFullYear()}/${String(now.getMonth() + 1).padStart(2, '0')}`;
    await mkdir(join(UPLOAD_ROOT, folder), { recursive: true });
    const name = `${randomUUID()}.webp`;
    await writeFile(join(UPLOAD_ROOT, folder, name), output.data);

    return {
      url: `/uploads/${folder}/${name}`,
      width: output.info.width,
      height: output.info.height,
      bytes: output.info.size,
    };
  }
}
