import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Post,
  Query,
  Request,
  Res,
  UploadedFile,
  UseGuards,
  UseInterceptors,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import type { Response, Request as ExpressRequest } from 'express';
import { randomUUID } from 'crypto';
import { JwtAuthGuard } from '@modules/organization/auth/guards/jwt-auth.guard';
import { SkipTransform } from '@common/decorators/skip-transform.decorator';
import { RequirePermissions } from '@common/decorators/permissions.decorator';
import { handleMeetingAttendanceError } from '../errors/handle-controller-error';
import { MeetingAttachmentService } from '../services/meeting-attachment.service';
import { MeetingAccessService } from '../services/meeting-access.service';
import { MeetingAttendanceError } from '../errors/meeting-attendance.error';
import { attachmentTooLarge } from '../errors/agenda.error';
import { QueryMeetingAttachmentsDto, UploadMeetingAttachmentBodyDto } from '../dto/agenda.dto';
import { MAX_FILE_SIZE_BYTES, getTmpUploadDir } from '../constants/attachment';

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { diskStorage } = require('multer');

interface MulterFile {
  fieldname: string;
  originalname: string;
  encoding: string;
  mimetype: string;
  destination: string;
  filename: string;
  path: string;
  size: number;
}

interface ReqUser {
  userId: string;
  id?: string;
  email?: string;
  permissions?: string[];
  organizationId?: string | null;
  roles?: any[];
  organizationRoles?: Record<string, string[]>;
  role?: string;
}

/**
 * 会议级附件 v1.0 controller（[117] / [118] / [119]）。
 *
 * 仅 `meeting:attachment:upload:any` 可上传（无 task 命中路径）。
 */
@Controller('meeting-attendance/meetings/:id/attachments')
@SkipTransform()
@UseGuards(JwtAuthGuard)
export class MeetingAttendanceMeetingAttachmentController {
  constructor(
    private readonly attachmentService: MeetingAttachmentService,
    private readonly accessService: MeetingAccessService,
  ) {}

  /** [117] 上传会议级附件。 */
  @Post()
  @RequirePermissions('meeting:attachment:upload:any')
  @UseInterceptors(
    FileInterceptor('file', {
      storage: diskStorage({
        destination: (
          _req: unknown,
          _file: MulterFile,
          cb: (e: Error | null, dest: string) => void,
        ) => cb(null, getTmpUploadDir()),
        filename: (
          _req: unknown,
          _file: MulterFile,
          cb: (e: Error | null, name: string) => void,
        ) => cb(null, `${randomUUID()}.tmp`),
      }),
      limits: { fileSize: MAX_FILE_SIZE_BYTES, files: 1, fieldSize: 10 * 1024 * 1024 },
    }),
  )
  async upload(
    @Param('id') meetingId: string,
    @UploadedFile() file: MulterFile | undefined,
    @Body() body: UploadMeetingAttachmentBodyDto,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = this.requireActor(req, res);
      if (!actor) return res;
      if (!file) {
        return res.status(400).json({ error: 'No file uploaded' });
      }
      // 必须先是参会人（manager/creator 自动通过）
      if (!(await this.accessService.isManagerOrCreator(meetingId, actor))) {
        return res
          .status(403)
          .json({
            code: 'AGENDA_FORBIDDEN_NOT_MANAGER_OR_CREATOR',
            error: 'Only MeetingManager or meeting creator can upload meeting-level attachments',
          });
      }

      const result = await this.attachmentService.upload(
        meetingId,
        file,
        body?.category,
        {
          userId: actor.userId,
          organizationId: actor.organizationId ?? null,
          permissions: actor.permissions ?? [],
        },
        req,
      );
      return res.status(201).json(result);
    } catch (error: any) {
      if (error?.code === 'LIMIT_FILE_SIZE') {
        const err = attachmentTooLarge();
        return res.status(err.status).json({ code: err.code, error: err.message });
      }
      return handleMeetingAttendanceError(res, error, 'Failed to upload meeting attachment', 'MeetingAttendanceMeetingAttachmentController');
    }
  }

  /** [118] 列表会议级附件（参会人可读）。 */
  @Get()
  @RequirePermissions('meeting:agenda:read')
  async list(
    @Param('id') meetingId: string,
    @Query() query: QueryMeetingAttachmentsDto,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = this.requireActor(req, res);
      if (!actor) return res;
      if (!(await this.accessService.isAttendee(meetingId, actor))) {
        return res
          .status(403)
          .json({ code: 'MEETING_ATTENDANCE_011', error: 'Not in the meeting attendee list' });
      }
      const result = await this.attachmentService.listByMeeting(meetingId, query);
      return res.status(200).json(result);
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Failed to list meeting attachments', 'MeetingAttendanceMeetingAttachmentController');
    }
  }

  /** [119] 软删会议级附件（uploader 本人或 manager）。 */
  @Delete(':attachmentId')
  async delete(
    @Param('id') meetingId: string,
    @Param('attachmentId') attachmentId: string,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = this.requireActor(req, res);
      if (!actor) return res;
      if (!(await this.accessService.isAttendee(meetingId, actor))) {
        return res
          .status(403)
          .json({ code: 'MEETING_ATTENDANCE_011', error: 'Not in the meeting attendee list' });
      }
      const isAdminOrManager = this.accessService.isAdminOrManager(actor);
      await this.attachmentService.deleteAttachment(
        meetingId,
        attachmentId,
        {
          userId: actor.userId,
          organizationId: actor.organizationId ?? null,
          permissions: actor.permissions ?? [],
          isAdminOrManager,
        },
        req,
      );
      return res.status(204).send();
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Failed to delete meeting attachment', 'MeetingAttendanceMeetingAttachmentController');
    }
  }

  private requireActor(req: ExpressRequest, res: Response): ReqUser | null {
    const user = req.user as ReqUser | undefined;
    const userId = user?.userId ?? user?.id;
    if (!userId) {
      res.status(401).json({ error: 'Unauthorized' });
      return null;
    }
    return { ...user, userId } as ReqUser;
  }
}
