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

interface AuthenticatedRequest {
  user: AuthenticatedUser;
}


interface StudentImportRow {
  name: string;
  studentNo?: string;
  phone?: string;
  idNumber?: string;
  gender?: string;
  ethnicity?: string;
  emergencyContact?: string;
  emergencyPhone?: string;
  organization?: string;
  supervisor?: string;
  organizationId?: number;
}

const STUDENT_IMPORT_COLUMNS = [
  { header: '姓名', key: 'name', width: 15 },
  { header: '学号', key: 'studentNo', width: 15 },
  { header: '性别', key: 'gender', width: 8 },
  { header: '电话', key: 'phone', width: 18 },
  { header: '身份证号', key: 'idNumber', width: 22 },
  { header: '民族', key: 'ethnicity', width: 10 },
  { header: '紧急联系人', key: 'emergencyContact', width: 15 },
  { header: '紧急联系人电话', key: 'emergencyPhone', width: 18 },
  { header: '所属机构名称', key: 'organization', width: 18 },
  { header: '负责人/班主任', key: 'supervisor', width: 15 },
];

const STUDENT_EXPORT_COLUMNS = [
  ...STUDENT_IMPORT_COLUMNS.map((column) => ({
    ...column,
    header: column.key === 'organization' ? '所属机构' : column.header,
  })),
  { header: '状态', key: 'status', width: 10 },
];

const STUDENT_IMPORT_HEADER_MAP: Record<string, keyof StudentImportRow> = {
  姓名: 'name',
  学号: 'studentNo',
  电话: 'phone',
  手机号: 'phone',
  '学号/身份证': 'idNumber',
  身份证: 'idNumber',
  身份证号: 'idNumber',
  性别: 'gender',
  民族: 'ethnicity',
  紧急联系人: 'emergencyContact',
  紧急联系人电话: 'emergencyPhone',
  所属机构: 'organization',
  所属机构名称: 'organization',
  负责人: 'supervisor',
  '负责人/班主任': 'supervisor',
};

function getExcelCellText(cell: ExcelJS.Cell): string {
  const value = cell.value;
  if (value === null || value === undefined) return '';
  if (typeof value === 'object') {
    if ('text' in value) return String(value.text || '');
    if ('richText' in value && Array.isArray(value.richText)) {
      return value.richText.map((part) => part.text).join('');
    }
    if ('result' in value) return String(value.result || '');
  }
  return String(value);
}

function parseStudentImportRows(ws: ExcelJS.Worksheet): StudentImportRow[] {
  const headerIndex = new Map<number, keyof StudentImportRow>();
  ws.getRow(1).eachCell((cell, colNumber) => {
    const header = getExcelCellText(cell).trim();
    const field = STUDENT_IMPORT_HEADER_MAP[header];
    if (field) headerIndex.set(colNumber, field);
  });

  const rows: StudentImportRow[] = [];
  ws.eachRow((row, idx) => {
    if (idx === 1) return;

    const parsed: Partial<StudentImportRow> = {};
    if (headerIndex.size > 0) {
      headerIndex.forEach((field, colNumber) => {
        const value = getExcelCellText(row.getCell(colNumber)).trim();
        if (value) {
          Object.assign(parsed, { [field]: value });
        }
      });
    } else {
      parsed.name = getExcelCellText(row.getCell(1)).trim();
      parsed.studentNo = getExcelCellText(row.getCell(2)).trim() || undefined;
      parsed.gender = getExcelCellText(row.getCell(3)).trim() || undefined;
      parsed.phone = getExcelCellText(row.getCell(4)).trim() || undefined;
      parsed.idNumber = getExcelCellText(row.getCell(5)).trim() || undefined;
      parsed.ethnicity = getExcelCellText(row.getCell(6)).trim() || undefined;
      parsed.emergencyContact = getExcelCellText(row.getCell(7)).trim() || undefined;
      parsed.emergencyPhone = getExcelCellText(row.getCell(8)).trim() || undefined;
      parsed.organization = getExcelCellText(row.getCell(9)).trim() || undefined;
      parsed.supervisor = getExcelCellText(row.getCell(10)).trim() || undefined;
    }

    rows.push({
      name: parsed.name || '',
      studentNo: parsed.studentNo,
      phone: parsed.phone,
      idNumber: parsed.idNumber,
      gender: parsed.gender,
      ethnicity: parsed.ethnicity,
      emergencyContact: parsed.emergencyContact,
      emergencyPhone: parsed.emergencyPhone,
      organization: parsed.organization,
      supervisor: parsed.supervisor,
    });
  });
  return rows;
}

@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 statusMap: Record<string, string> = {
      active: '在读',
      graduated: '已毕业',
      withdrawn: '已退训',
      archived: '已归档',
    };
    for (const s of students) {
      ws.addRow({
        name: s.name,
        studentNo: s.studentNo || '',
        gender: s.gender || '',
        phone: s.phone || '',
        idNumber: s.idNumber || '',
        ethnicity: s.ethnicity || '',
        emergencyContact: s.emergencyContact || '',
        emergencyPhone: s.emergencyPhone || '',
        organization: s.organization?.name || '',
        supervisor: s.supervisor || '',
        status: statusMap[s.status] || s.status,
      });
    }
    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 = new ExcelJS.Workbook();
    const ws = workbook.addWorksheet('学生导入模板');
    ws.columns = STUDENT_IMPORT_COLUMNS;
    ws.getRow(1).font = { bold: true };
    ws.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } };
    ws.addRow({
      name: '张三',
      studentNo: '2024001',
      gender: '男',
      phone: '13800138000',
      idNumber: '11010120060101001X',
      ethnicity: '汉族',
      emergencyContact: '张父',
      emergencyPhone: '13900000000',
      organization: 'XX教育公司',
      supervisor: '',
    });
    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 ws = workbook.worksheets[0];
    const rows = parseStudentImportRows(ws);
    // Resolve organization names to IDs
    for (const row of rows) {
      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(rows);
    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 ws = workbook.worksheets[0];
    const rows = parseStudentImportRows(ws);
    // Resolve organization names to IDs
    for (const row of rows) {
      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(rows);
    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);
  }
}
