import { Controller, Get, Param, Request, Res, UseGuards } from '@nestjs/common';
import type { Response, Request as ExpressRequest } from 'express';
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 { AttachmentDownloadService } from '../services/attachment-download.service';
import { MeetingAccessService } from '../services/meeting-access.service';

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

/**
 * 附件下载 v1.0 controller（[120] / [121]）。
 *
 * - 流式 + RFC 5987 Content-Disposition（中文/特殊字符 filename 兼容）
 * - 参会身份校验在 service 内部通过传入的 actorIsAttendeeChecker 回调完成
 *
 * 因下载用 res 流，不能 await response，但 catch 仍走统一 helper（service 抛错时
 * 在 headersSent 前回写 JSON；流式中途断开走 stream.on('error') 兜底）。
 */
@Controller('meeting-attendance/attachments')
@SkipTransform()
@UseGuards(JwtAuthGuard)
export class MeetingAttendanceAttachmentDownloadController {
  constructor(
    private readonly downloadService: AttachmentDownloadService,
    private readonly accessService: MeetingAccessService,
  ) {}

  /** [120] 议程项资料下载。 */
  @Get('agenda-item/:id/download')
  @RequirePermissions('meeting:attachment:download')
  async downloadAgendaItem(
    @Param('id') attachmentId: string,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = this.requireActor(req, res);
      if (!actor) return res;
      await this.downloadService.downloadAgendaItemAttachment(
        attachmentId,
        res,
        (meetingId) => this.accessService.isAttendee(meetingId, actor),
      );
      // 流回写已经接管 res，下面不再 res.json
      return res;
    } catch (error) {
      if (res.headersSent) {
        try {
          res.end();
        } catch {
          // ignore
        }
        return res;
      }
      return handleMeetingAttendanceError(res, error, 'Failed to download attachment', 'MeetingAttendanceAttachmentDownloadController');
    }
  }

  /** [121] 会议级资料下载。 */
  @Get('meeting/:id/download')
  @RequirePermissions('meeting:attachment:download')
  async downloadMeeting(
    @Param('id') attachmentId: string,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = this.requireActor(req, res);
      if (!actor) return res;
      await this.downloadService.downloadMeetingAttachment(
        attachmentId,
        res,
        (meetingId) => this.accessService.isAttendee(meetingId, actor),
      );
      return res;
    } catch (error) {
      if (res.headersSent) {
        try {
          res.end();
        } catch {
          // ignore
        }
        return res;
      }
      return handleMeetingAttendanceError(res, error, 'Failed to download attachment', 'MeetingAttendanceAttachmentDownloadController');
    }
  }

  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;
  }
}
