import {
  Body,
  Controller,
  Post,
  UseInterceptors,
  UploadedFile,
  BadRequestException,
  Logger,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { Throttle } from '@nestjs/throttler';
import { memoryStorage } from 'multer';
import { extname } from 'node:path';
import { ClsService } from 'nestjs-cls';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { TenantEntity } from '../../entities/tenant.entity';
import { CLS_TENANT_ID, CLS_USER_ID } from '../../common/cls/cls.constants';
import { successResponse } from '../../common/responses/api-response';
import { StorageService } from '../../common/storage/storage.service';
import { Lookup } from '../../common/decorators/lookup.decorator';
import { isAllowedUploadExtension, sniffUpload } from '../../common/storage/file-sniff';
import {
  MAX_IMAGE_BYTES,
  MAX_VIDEO_BYTES,
  MEDIA_POLICIES,
} from '../../common/media/media.constants';
import { UploadFileDto } from './dto/upload-file.dto';

/**
 * Single media-upload entry point for logos, module photos / videos,
 * and ticket PDFs. Excel pricing import is parse-only and stays on
 * those controllers — it never writes to storage.
 *
 * Multer caps the body at the video limit; images and PDFs are
 * re-checked against the smaller limit after sniffing.
 */
@Controller('uploads')
export class UploadController {
  private readonly logger = new Logger(UploadController.name);

  constructor(
    private readonly cls: ClsService,
    private readonly storage: StorageService,
    @InjectRepository(TenantEntity)
    private readonly tenantRepo: Repository<TenantEntity>,
  ) {}

  @Post()
  @Lookup()
  @Throttle({ default: { limit: 30, ttl: 60 * 60 * 1000 } })
  @UseInterceptors(
    FileInterceptor('file', {
      limits: { fileSize: MAX_VIDEO_BYTES },
      fileFilter: (_req, file, cb) => {
        const ext = extname(file.originalname).toLowerCase();
        if (!isAllowedUploadExtension(ext)) {
          cb(new BadRequestException('Only image, video, and PDF files are allowed'), false);
          return;
        }
        cb(null, true);
      },
      storage: memoryStorage(),
    }),
  )
  async uploadFile(@UploadedFile() file: Express.Multer.File, @Body() dto: UploadFileDto) {
    if (!file) {
      throw new BadRequestException('No file uploaded');
    }

    const sniffed = sniffUpload(file.buffer);
    if (!sniffed) {
      throw new BadRequestException('File content does not match a permitted image, video, or PDF');
    }
    if (sniffed.kind !== 'video' && file.size > MAX_IMAGE_BYTES) {
      throw new BadRequestException('Images and PDFs must be 10 MB or smaller');
    }
    if (dto.module) {
      if (sniffed.kind === 'pdf' || sniffed.ext === '.ico') {
        throw new BadRequestException('Only photos and videos can be attached to a record');
      }
      if (sniffed.kind === 'video' && !MEDIA_POLICIES[dto.module].allowVideo) {
        throw new BadRequestException('This module accepts photos only');
      }
    }

    const tenantId = this.cls.get<string>(CLS_TENANT_ID);
    const tenant = await this.tenantRepo.findOne({
      where: { id: tenantId },
      select: { subdomain: true },
    });
    const subdomain = tenant?.subdomain || 'default';

    const stored = await this.storage.save(subdomain, sniffed.ext, file.buffer, dto.module);

    this.logger.log(
      `File upload tenant=${tenantId} user=${this.cls.get<string>(CLS_USER_ID) ?? 'unknown'} ` +
        `filename=${stored.key} size=${file.size} mime=${sniffed.mime}`,
    );

    return successResponse(
      {
        url: stored.url,
        filename: stored.key.split('/').pop(),
        size: file.size,
        type: sniffed.kind,
      },
      'File uploaded successfully',
    );
  }
}
