import {
  BadRequestException,
  Body,
  Controller,
  Get,
  ParseIntPipe,
  Post,
  Query,
  UploadedFile,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import {
  ApiBearerAuth,
  ApiBody,
  ApiConsumes,
  ApiOperation,
  ApiTags,
} from '@nestjs/swagger';
import { FileInterceptor } from '@nestjs/platform-express';
import { AccessTokenGuard } from '../../auth/access-token.guard.js';
import { CurrentActor } from '../../auth/current-actor.decorator.js';
import { Roles } from '../../auth/roles.decorator.js';
import { RolesGuard } from '../../auth/roles.guard.js';
import type { ActorContext } from '../../common/actor-context.js';
import { Role } from '../../generated/prisma/enums.js';
import { MESSAGES } from '../../common/messages.js';
import { UploadAssetQueryDto } from '../dto/upload-asset-query.dto.js';
import {
  ensureUploadDirectories,
  resolveUploadTempDir,
} from '../upload-path.util.js';
import { UploadsService } from '../uploads.service.js';
import { diskStorage } from 'multer';
import { extname } from 'node:path';
import { randomUUID } from 'node:crypto';

const MAX_UPLOAD_SIZE = 1024 * 1024 * 200;
const ALLOWED_FILE_EXTENSIONS_BY_MIME = new Map<string, string[]>([
  ['application/pdf', ['.pdf']],
  ['application/msword', ['.doc']],
  [
    'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
    ['.docx'],
  ],
  ['application/vnd.ms-excel', ['.xls']],
  [
    'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    ['.xlsx'],
  ],
]);

function isAllowedMimeType(mimeType: string) {
  return (
    mimeType.startsWith('image/') ||
    mimeType.startsWith('video/') ||
    [
      'application/pdf',
      'application/msword',
      'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      'application/vnd.ms-excel',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    ].includes(mimeType)
  );
}

function isAllowedOriginalExtension(
  originalName: string,
  mimeType: string,
) {
  if (mimeType.startsWith('image/') || mimeType.startsWith('video/')) {
    return true;
  }

  const allowedExtensions = ALLOWED_FILE_EXTENSIONS_BY_MIME.get(mimeType);
  if (!allowedExtensions) {
    return false;
  }

  return allowedExtensions.includes(extname(originalName).toLowerCase());
}

@ApiTags('上传资产(B端)')
@ApiBearerAuth('bearer')
@Controller('b/uploads')
@UseGuards(AccessTokenGuard, RolesGuard)
export class BUploadsController {
  constructor(private readonly uploadsService: UploadsService) {}

  /**
   * 上传图片/视频/文件。
   */
  @Post()
  @Roles(
    Role.SYSTEM_ADMIN,
    Role.HOSPITAL_ADMIN,
    Role.DIRECTOR,
    Role.LEADER,
    Role.DOCTOR,
    Role.ENGINEER,
  )
  @UseInterceptors(
    FileInterceptor('file', {
      storage: diskStorage({
        destination: (_req, _file, cb) => {
          ensureUploadDirectories();
          cb(null, resolveUploadTempDir());
        },
        filename: (_req, file, cb) => {
          cb(null, `${randomUUID()}${extname(file.originalname)}`);
        },
      }),
      limits: { fileSize: MAX_UPLOAD_SIZE },
      fileFilter: (_req, file, cb) => {
        if (
          !isAllowedMimeType(file.mimetype) ||
          !isAllowedOriginalExtension(file.originalname, file.mimetype)
        ) {
          cb(
            new BadRequestException(MESSAGES.UPLOAD.UNSUPPORTED_FILE_TYPE),
            false,
          );
          return;
        }
        cb(null, true);
      },
    }),
  )
  @ApiOperation({ summary: '上传图片/视频/文件' })
  @ApiConsumes('multipart/form-data')
  @ApiBody({
    schema: {
      type: 'object',
      properties: {
        file: {
          type: 'string',
          format: 'binary',
        },
        hospitalId: {
          type: 'integer',
        },
      },
      required: ['file'],
    },
  })
  create(
    @CurrentActor() actor: ActorContext,
    @UploadedFile() file?: Express.Multer.File,
    @Body('hospitalId') hospitalId?: string,
  ) {
    const requestedHospitalId =
      hospitalId == null || hospitalId === '' ? undefined : Number(hospitalId);

    return this.uploadsService.createUpload(actor, file, requestedHospitalId);
  }

  /**
   * 查询图库/视频库/文件库。
   */
  @Get()
  @Roles(Role.SYSTEM_ADMIN, Role.HOSPITAL_ADMIN)
  @ApiOperation({ summary: '查询上传资产列表' })
  findAll(
    @CurrentActor() actor: ActorContext,
    @Query() query: UploadAssetQueryDto,
  ) {
    return this.uploadsService.findAll(actor, query);
  }
}
