import {
  Body,
  Controller,
  Delete,
  Get,
  Param,
  Patch,
  Post,
  Query,
  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 { UploadTaskService } from '../services/upload-task.service';
import { AgendaService } from '../services/agenda.service';
import { MeetingAccessService } from '../services/meeting-access.service';
import {
  AssignUploadTaskDto,
  QueryAgendaItemUploadTasksDto,
  QueryMyTasksDto,
  UpdateUploadTaskDto,
} from '../dto/agenda.dto';
import { agendaForbiddenNotManagerOrCreator, agendaItemNotFound } from '../errors/agenda.error';

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

/**
 * 上传任务 v1.0 controller：assign / list / patch / cancel + 我的待办。
 *
 * 端点路径（注意 my-upload-tasks 是顶级路径，不带 :itemId 占位）：
 * - POST   /meeting-attendance/agenda-items/:itemId/upload-tasks            [109]
 * - GET    /meeting-attendance/agenda-items/:itemId/upload-tasks            [110]
 * - PATCH  /meeting-attendance/agenda-items/:itemId/upload-tasks/:taskId    [111]
 * - DELETE /meeting-attendance/agenda-items/:itemId/upload-tasks/:taskId    [112]
 * - GET    /meeting-attendance/my-upload-tasks                              [113]
 */
@Controller('meeting-attendance')
@SkipTransform()
@UseGuards(JwtAuthGuard)
export class MeetingAttendanceUploadTaskController {
  constructor(
    private readonly uploadTaskService: UploadTaskService,
    private readonly agendaService: AgendaService,
    private readonly accessService: MeetingAccessService,
  ) {}

  /** [113] 我的待办：放在 :itemId 之前避免路径冲突。 */
  @Get('my-upload-tasks')
  async listMyTasks(
    @Query() query: QueryMyTasksDto,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = this.requireActor(req, res);
      if (!actor) return res;
      const result = await this.uploadTaskService.listMyTasks(actor.userId, query);
      return res.status(200).json(result);
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Failed to fetch my upload tasks', 'MeetingAttendanceUploadTaskController');
    }
  }

  /** [109] 分配任务（manager / creator）。 */
  @Post('agenda-items/:itemId/upload-tasks')
  @RequirePermissions('meeting:upload-task:assign')
  async assignTasks(
    @Param('itemId') itemId: string,
    @Body() dto: AssignUploadTaskDto,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = this.requireActor(req, res);
      if (!actor) return res;
      const meetingId = await this.agendaService.findMeetingIdByItem(itemId);
      if (!meetingId) throw agendaItemNotFound();
      if (!(await this.accessService.isManagerOrCreator(meetingId, actor))) {
        throw agendaForbiddenNotManagerOrCreator();
      }
      const result = await this.uploadTaskService.assignTasks(itemId, dto, actor, req);
      return res.status(201).json(result);
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Failed to assign upload tasks', 'MeetingAttendanceUploadTaskController');
    }
  }

  /** [110] 列表议程项下任务（参会人可读）。 */
  @Get('agenda-items/:itemId/upload-tasks')
  @RequirePermissions('meeting:agenda:read')
  async listTasks(
    @Param('itemId') itemId: string,
    @Query() query: QueryAgendaItemUploadTasksDto,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = this.requireActor(req, res);
      if (!actor) return res;
      const meetingId = await this.agendaService.findMeetingIdByItem(itemId);
      if (!meetingId) throw agendaItemNotFound();
      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.uploadTaskService.listByItem(itemId, query);
      return res.status(200).json(result);
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Failed to list upload tasks', 'MeetingAttendanceUploadTaskController');
    }
  }

  /** [111] manager 修改任务（dueAt 或显式 CANCELLED）。 */
  @Patch('agenda-items/:itemId/upload-tasks/:taskId')
  @RequirePermissions('meeting:upload-task:assign')
  async patchTask(
    @Param('itemId') itemId: string,
    @Param('taskId') taskId: string,
    @Body() dto: UpdateUploadTaskDto,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = this.requireActor(req, res);
      if (!actor) return res;
      const meetingId = await this.agendaService.findMeetingIdByItem(itemId);
      if (!meetingId) throw agendaItemNotFound();
      if (!(await this.accessService.isManagerOrCreator(meetingId, actor))) {
        throw agendaForbiddenNotManagerOrCreator();
      }
      const result = await this.uploadTaskService.updateTask(itemId, taskId, dto, actor, req);
      return res.status(200).json(result);
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Failed to update upload task', 'MeetingAttendanceUploadTaskController');
    }
  }

  /** [112] manager 软删任务（幂等）。 */
  @Delete('agenda-items/:itemId/upload-tasks/:taskId')
  @RequirePermissions('meeting:upload-task:assign')
  async deleteTask(
    @Param('itemId') itemId: string,
    @Param('taskId') taskId: string,
    @Request() req: ExpressRequest,
    @Res() res: Response,
  ) {
    try {
      const actor = this.requireActor(req, res);
      if (!actor) return res;
      const meetingId = await this.agendaService.findMeetingIdByItem(itemId);
      if (!meetingId) throw agendaItemNotFound();
      if (!(await this.accessService.isManagerOrCreator(meetingId, actor))) {
        throw agendaForbiddenNotManagerOrCreator();
      }
      await this.uploadTaskService.deleteTask(itemId, taskId, actor, req);
      return res.status(204).send();
    } catch (error) {
      return handleMeetingAttendanceError(res, error, 'Failed to delete upload task', 'MeetingAttendanceUploadTaskController');
    }
  }

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