import { BadRequestException, CanActivate, ExecutionContext, Injectable, Type, mixin } from '@nestjs/common'

import { DEFAULT_MAX_SIZE_BYTES, type UploadGuardOptions, type UploadRequest } from './upload.types'
import { UploadService } from './upload.service'

export function UploadGuard(options?: UploadGuardOptions): Type<CanActivate> {
  @Injectable()
  class UploadGuardMixin implements CanActivate {
    constructor(private readonly uploadService: UploadService) {}

    async canActivate(context: ExecutionContext): Promise<boolean> {
      const req = context.switchToHttp().getRequest<UploadRequest>()
      if (!req.isMultipart?.()) {
        throw new BadRequestException("Request must use 'multipart/form-data'")
      }

      const multipartFile = await req.file({
        limits: {
          fileSize: options?.maxFileSizeBytes ?? DEFAULT_MAX_SIZE_BYTES,
        },
      })

      if (!multipartFile) {
        throw new BadRequestException('No file uploaded')
      }

      const user = req.viewer?.user ?? this.uploadService.getRootUser()
      req.uploadedFileData = await this.uploadService.processUpload({
        multipartFile,
        user,
        options,
      })

      return true
    }
  }

  return mixin(UploadGuardMixin)
}
