import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Body,
  Param,
  Query,
  UseGuards,
  Request,
  Res,
} from '@nestjs/common';
import type { Response } from 'express';
import { ClassesService } from './classes.service';
import {
  CreateClassDto,
  UpdateClassDto,
  QueryClassDto,
  AddStudentsDto,
  AddTeacherDto,
  QueryClassScheduleDto,
  QueryClassAttendanceSummaryDto,
  BatchImportStudentsDto,
} from './dto/class.dto';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { OperationLogsService } from '../operation-logs/operation-logs.service';
import { extractRequestInfo } from '../common/request-utils';
import { RequirePermission } from '../auth/decorators/permission.decorator';
import { NotificationsService } from '../notifications/notifications.service';
import { NotificationType } from '../entities/notification.entity';
import { TeacherRoleType } from '../entities';
import * as ExcelJS from 'exceljs';
import { AuthorizationService, CaslAction, SubjectName, AuthenticatedUser } from '../authorization';

interface AuthenticatedRequest {
  user: AuthenticatedUser;
}

const teacherRoleLabels: Record<string, string> = {
  [TeacherRoleType.SUBJECT_TEACHER]: '任课老师',
  [TeacherRoleType.HEAD_TEACHER]: '班主任',
  [TeacherRoleType.LIFE_TEACHER]: '生活老师',
  [TeacherRoleType.ACADEMIC_TEACHER]: '学服老师',
};

@UseGuards(JwtAuthGuard)
@Controller('classes')
export class ClassesController {
  constructor(
    private readonly service: ClassesService,
    private readonly logService: OperationLogsService,
    private readonly notificationsService: NotificationsService,
    private readonly authz: AuthorizationService,
  ) {}

  private assertReadAccess(req: AuthenticatedRequest, classId: number) {
    // Legacy: Manage (super_admin) or Update (class:edit) grants broad class access
    const canManageAll =
      this.authz.can(req, CaslAction.Manage, SubjectName.Class) ||
      this.authz.can(req, CaslAction.Update, SubjectName.Class);
    return this.service.assertClassAccess(req.user.id, classId, canManageAll);
  }

  @Get()
  @RequirePermission('class:view')
  async findAll(@Query() query: QueryClassDto, @Request() req: AuthenticatedRequest) {
    const classIds = await this.service.getAccessibleClassIds(
      req.user.id,
      this.authz.can(req, CaslAction.Manage, SubjectName.Class) ||
        this.authz.can(req, CaslAction.Update, SubjectName.Class),
    );
    return this.service.findAll(query, classIds);
  }

  @Get(':id')
  @RequirePermission('class:view')
  async findOne(@Param('id') id: string, @Request() req: AuthenticatedRequest) {
    await this.assertReadAccess(req, +id);
    return this.service.findOne(+id);
  }

  @Get(':id/schedule')
  @RequirePermission('class:view')
  async getSchedule(
    @Param('id') id: string,
    @Query() query: QueryClassScheduleDto,
    @Request() req: AuthenticatedRequest,
  ) {
    await this.assertReadAccess(req, +id);
    return this.service.getSchedule(+id, query);
  }

  @Get(':id/attendance-summary')
  @RequirePermission('class:view')
  async getAttendanceSummary(
    @Param('id') id: string,
    @Query() query: QueryClassAttendanceSummaryDto,
    @Request() req: AuthenticatedRequest,
  ) {
    await this.assertReadAccess(req, +id);
    return this.service.getAttendanceSummary(+id, query);
  }

  @Post()
  @RequirePermission('class:create')
  async create(@Body() dto: CreateClassDto, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.create(dto);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '班级管理',
      action: '创建班级',
      targetId: result.id,
      targetType: 'class',
      detail: `班级${result.code} ${result.name}`,
      ipAddress,
      userAgent,
    });
    return result;
  }

  /** 批量导入学生到班级（通过钉钉用户ID） */
  @Post(':id/students/import')
  @RequirePermission('class:edit')
  async batchImportStudents(@Param('id') id: string, @Body() dto: BatchImportStudentsDto) {
    return this.service.batchImportStudents(+id, dto.users);
  }

  /** 归档班级 */
  @Put(':id/archive')
  @RequirePermission('class:edit')
  async archive(@Param('id') id: string) {
    return this.service.archive(+id);
  }

  /** 取消归档 */
  @Put(':id/restore')
  @RequirePermission('class:edit')
  async restore(@Param('id') id: string) {
    return this.service.restore(+id);
  }

  @Put(':id')
  @RequirePermission('class:edit')
  async update(@Param('id') id: string, @Body() dto: UpdateClassDto, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.update(+id, dto);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '班级管理',
      action: '编辑班级',
      targetId: +id,
      targetType: 'class',
      detail: JSON.stringify(dto),
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Delete(':id')
  @RequirePermission('class:delete')
  async remove(@Param('id') id: string, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.remove(+id);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '班级管理',
      action: '归档班级',
      targetId: +id,
      targetType: 'class',
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Get(':id/roster/export')
  @RequirePermission('class:view')
  async exportRoster(
    @Param('id') id: string,
    @Res() res: Response,
    @Request() req: AuthenticatedRequest,
  ) {
    await this.assertReadAccess(req, +id);
    const classEntity = await this.service.findOne(+id);
    const classStudents = await this.service.getStudents(+id);

    const workbook = new ExcelJS.Workbook();
    const ws = workbook.addWorksheet('班级花名册');
    ws.columns = [
      { header: '姓名', key: 'name', width: 15 },
      { header: '学号', key: 'studentNo', width: 15 },
      { header: '加入日期', key: 'joinDate', width: 15 },
      { header: '状态', key: 'status', width: 10 },
    ];
    ws.getRow(1).font = { bold: true };
    ws.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } };

    for (const cs of classStudents) {
      ws.addRow({
        name: cs.student?.name || '',
        studentNo: cs.student?.idNumber || '',
        joinDate: cs.joinDate || '',
        status: cs.status || '',
      });
    }

    res.setHeader(
      'Content-Type',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    );
    res.setHeader(
      'Content-Disposition',
      `attachment; filename=${encodeURIComponent(`班级花名册-${classEntity.name}`)}.xlsx`,
    );
    await workbook.xlsx.write(res);
    res.end();
  }

  @Get(':id/students')
  @RequirePermission('class:view')
  async getStudents(@Param('id') id: string, @Request() req: AuthenticatedRequest) {
    await this.assertReadAccess(req, +id);
    return this.service.getStudents(+id);
  }

  @Post(':id/students')
  @RequirePermission('class:edit')
  async addStudents(@Param('id') id: string, @Body() dto: AddStudentsDto, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.addStudents(+id, dto.studentIds);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '班级管理',
      action: '添加学生',
      targetId: +id,
      targetType: 'class',
      detail: `新增${result.added}名学生`,
      ipAddress,
      userAgent,
    });
    try {
      const cls = await this.service.findOne(+id);
      if (cls.headTeacherId) {
        void this.notificationsService.create({
          recipientIds: [cls.headTeacherId],
          type: NotificationType.CLASS_CHANGE,
          title: '学员变动',
          content: `班级新增${result.added}名学生`,
        });
      }
    } catch {}
    return result;
  }

  @Delete(':id/students/:studentId')
  @RequirePermission('class:edit')
  async removeStudent(
    @Param('id') id: string,
    @Param('studentId') studentId: string,
    @Request() req: any,
  ) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.removeStudent(+id, +studentId);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '班级管理',
      action: '移除学生',
      targetId: +id,
      targetType: 'class',
      detail: `移除学生${studentId}`,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Get(':id/teachers')
  @RequirePermission('class:view')
  async getTeachers(@Param('id') id: string, @Request() req: AuthenticatedRequest) {
    await this.assertReadAccess(req, +id);
    return this.service.getTeachers(+id);
  }

  @Post(':id/teachers')
  @RequirePermission('class:edit')
  async addTeacher(@Param('id') id: string, @Body() dto: AddTeacherDto, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.addTeacher(+id, dto);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '班级管理',
      action: '添加教师',
      targetId: +id,
      targetType: 'class',
      detail: `教师${dto.userId} 角色${dto.roleType}`,
      ipAddress,
      userAgent,
    });
    try {
      void this.notificationsService.create({
        recipientIds: [dto.userId],
        type: NotificationType.CLASS_CHANGE,
        title: '班级分配',
        content: `您已被分配到班级担任${teacherRoleLabels[dto.roleType] ?? dto.roleType}角色`,
      });
    } catch {}
    return result;
  }

  @Delete(':id/teacher-assignments/:assignmentId')
  @RequirePermission('class:edit')
  async removeTeacherAssignment(
    @Param('id') id: string,
    @Param('assignmentId') assignmentId: string,
    @Request() req: any,
  ) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.removeTeacherAssignment(+id, +assignmentId);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '班级管理',
      action: '移除教师角色',
      targetId: +id,
      targetType: 'class',
      detail: `移除教师分配${assignmentId}`,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Delete(':id/teachers/:userId')
  @RequirePermission('class:edit')
  async removeTeacher(
    @Param('id') id: string,
    @Param('userId') userId: string,
    @Request() req: any,
  ) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.removeTeacher(+id, +userId);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '班级管理',
      action: '移除教师',
      targetId: +id,
      targetType: 'class',
      detail: `移除教师${userId}`,
      ipAddress,
      userAgent,
    });
    return result;
  }
}
