import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Body,
  Param,
  UseGuards,
  Query,
  Request,
  Res,
  UseInterceptors,
  UploadedFile,
  Inject,
  ParseIntPipe,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Organization } from '../entities/organization.entity';
import { ClassTeacher } from '../entities/class-teacher.entity';
import { FileInterceptor } from '@nestjs/platform-express';
import type { Response } from 'express';
import { StudentsService } from './students.service';
import { CreateStudentDto, QueryStudentDto, UpdateStudentDto } from './dto/student.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 { AuthorizationService, CaslAction, SubjectName } from '../authorization';
import type { AuthenticatedUser } from '../authorization';
import * as ExcelJS from 'exceljs';
import {
  createStudentImportTemplateWorkbook,
  parseStudentImportWorkbook,
  STUDENT_EXPORT_COLUMNS,
} from './student-import';

interface AuthenticatedRequest {
  user: AuthenticatedUser;
}

@UseGuards(JwtAuthGuard)
@Controller('students')
export class StudentsController {
  constructor(
    private service: StudentsService,
    private logService: OperationLogsService,
    @InjectRepository(Organization) private organizationRepo: Repository<Organization>,
    private authz: AuthorizationService,
  ) {}

  private canManageAllStudents(req: AuthenticatedRequest): boolean {
    return (
      this.authz.can(req, CaslAction.Manage, SubjectName.Student) ||
      // Legacy: class:edit grants broad student access for teacher scoping
      this.authz.can(req, CaslAction.Update, SubjectName.Class)
    );
  }

  @Get('basic-lookups')
  @RequirePermission('student:basic-view', 'student:view')
  getBasicLookups() {
    return this.service.getBasicLookups();
  }

  @Get('filter-lookups')
  @RequirePermission('student:view')
  async getFilterLookups(@Request() req: AuthenticatedRequest) {
    const classIds = await this.service.getAccessibleClassIds(
      req.user.id,
      this.canManageAllStudents(req),
    );
    return this.service.getFilterLookups(classIds);
  }

  @Get()
  @RequirePermission('student:view')
  async findAll(
    @Query() query: QueryStudentDto,
    @Request() req: AuthenticatedRequest,
  ) {
    const classIds = await this.service.getAccessibleClassIds(
      req.user.id,
      this.canManageAllStudents(req),
    );
    return this.service.findAll(
      query,
      classIds,
    );
  }

  @Get('export')
  @RequirePermission('student:export')
  async exportExcel(
    @Query() query: QueryStudentDto,
    @Res() res?: Response,
    @Request() req?: any,
  ) {
    const classIds = await this.service.getAccessibleClassIds(
      req.user.id,
      this.canManageAllStudents(req),
    );
    const students = await this.service.findAll(query, classIds);
    const workbook = new ExcelJS.Workbook();
    const ws = workbook.addWorksheet('学生名单');
    ws.columns = STUDENT_EXPORT_COLUMNS;
    ws.getRow(1).font = { bold: true };
    ws.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } };
    const { profiles, results } = await this.service.getArchiveExportMaps(
      students.map((student) => student.id),
    );
    for (const s of students) {
      const profile = profiles.get(s.id);
      const result = results.get(s.id);
      ws.addRow({
        phone: s.phone || '',
        name: s.name,
        studentNo: s.studentNo || '',
        gender: s.gender || '',
        idNumber: s.idNumber || '',
        ethnicity: s.ethnicity || '',
        emergencyContact: s.emergencyContact || '',
        emergencyPhone: s.emergencyPhone || '',
        organization: s.organization?.name || '',
        supervisor: s.supervisor || '',
        targetCollege: profile?.targetCollege || '',
        targetMajor: profile?.targetMajor || '',
        collegeSchool: profile?.collegeSchool || '',
        collegeMajor: profile?.collegeMajor || '',
        subjectDirection: profile?.subjectDirection || '',
        grade: profile?.grade || '',
        profileDate: profile?.profileDate || '',
        notes: profile?.notes || '',
        cultureFinalScore: result?.cultureFinalScore ?? '',
        professionalFinalScore: result?.professionalFinalScore ?? '',
        admissionStatus: result?.admissionStatus || '',
        admittedCollege: result?.admittedCollege || '',
        admittedMajor: result?.admittedMajor || '',
      });
    }
    const { ipAddress, userAgent } = extractRequestInfo(req);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '学生管理',
      action: '导出学生',
      detail: `导出 ${students.length} 名学生`,
      ipAddress,
      userAgent,
    });
    res!.setHeader(
      'Content-Type',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    );
    res!.setHeader('Content-Disposition', 'attachment; filename=students.xlsx');
    await workbook.xlsx.write(res!);
    res!.end();
  }

  @Get('template')
  @RequirePermission('student:view')
  async downloadTemplate(@Res() res: Response) {
    const workbook = createStudentImportTemplateWorkbook();
    res.setHeader(
      'Content-Type',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    );
    res.setHeader('Content-Disposition', 'attachment; filename=student_template.xlsx');
    await workbook.xlsx.write(res);
    res.end();
  }

  @Get(':id')
  @RequirePermission('student:view')
  findOne(@Param('id', ParseIntPipe) id: number) {
    return this.service.findOne(id);
  }

  @Post()
  @RequirePermission('student:create')
  async create(@Body() dto: CreateStudentDto, @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: 'student',
      detail: `姓名: ${dto.name}, 电话: ${dto.phone || '无'}, 学号: ${dto.idNumber || '无'}`,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Put(':id')
  @RequirePermission('student:edit')
  async update(@Param('id', ParseIntPipe) id: number, @Body() dto: UpdateStudentDto, @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: 'student',
      detail: JSON.stringify(dto),
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Delete(':id')
  @RequirePermission('student:delete')
  async remove(@Param('id', ParseIntPipe) id: number, @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: 'student',
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Post('batch-delete')
  @RequirePermission('student:delete')
  async batchRemove(@Body() body: { ids: number[] }, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.batchRemove(body.ids || []);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '学生管理',
      action: '批量归档学生',
      detail: `IDs: ${(body.ids || []).join(',')}`,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Put(':id/restore')
  @RequirePermission('student:edit')
  async restore(@Param('id', ParseIntPipe) id: number, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.restore(id);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '学生管理',
      action: '恢复学生',
      targetId: id,
      targetType: 'student',
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Post('import')
  @RequirePermission('student:import')
  @UseInterceptors(FileInterceptor('file'))
  async importExcel(@UploadedFile() file: Express.Multer.File, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const workbook = new ExcelJS.Workbook();
    await workbook.xlsx.load(file.buffer as any);
    const importData = parseStudentImportWorkbook(workbook);
    // Resolve organization names to IDs
    for (const row of importData.students) {
      if (row.organization) {
        const organization = await this.organizationRepo.findOne({
          where: { name: row.organization },
        });
        if (organization) {
          row.organizationId = organization.id;
        }
      }
    }
    const result = await this.service.batchImport(importData);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '学生管理',
      action: '导入学生',
      detail: result.message,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Post('import-match')
  @RequirePermission('student:import')
  @UseInterceptors(FileInterceptor('file'))
  async matchImport(@UploadedFile() file: Express.Multer.File, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const workbook = new ExcelJS.Workbook();
    await workbook.xlsx.load(file.buffer as unknown as ArrayBuffer);
    const importData = parseStudentImportWorkbook(workbook);
    // Resolve organization names to IDs
    for (const row of importData.students) {
      if (row.organization) {
        const organization = await this.organizationRepo.findOne({
          where: { name: row.organization },
        });
        if (organization) row.organizationId = organization.id;
      }
    }
    const result = await this.service.matchImport(importData);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '学生管理',
      action: '更新已有学生资料',
      detail: result.message,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Get(':id/compare-classes')
  @RequirePermission('student:view')
  compareClasses(@Param('id', ParseIntPipe) id: number) {
    return this.service.compareClasses(id);
  }
}
