import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Body,
  Param,
  Query,
  UseGuards,
  Request,
  Res,
  UseInterceptors,
  UploadedFile,
  ParseIntPipe,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import type { Response } from 'express';
import { ExpensesService } from './expenses.service';
import {
  CreateRoomExpenseDto,
  CreatePersonalExpenseDto,
  BatchRoomExpenseDto,
  CreateStudentUtilityBillDto,
  QueryPersonalExpenseDto,
  QueryRoomExpenseDto,
  UpdatePersonalExpenseDto,
  UpdateRoomExpenseDto,
} from './dto/expense.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 * as ExcelJS from 'exceljs';

/** 提取 ExcelJS 单元格的真实值，兼容公式、富文本、日期、超链接等情况 */
function readCell(cell: ExcelJS.Cell): any {
  let v: any = cell?.value;
  if (v == null) return '';
  if (typeof v === 'object') {
    // 公式单元格：{ formula, result }
    if ('result' in v) v = v.result;
    // 富文本：{ richText: [...] }
    else if ('richText' in v && Array.isArray(v.richText)) {
      return v.richText.map((r: any) => r.text || '').join('');
    }
    // 超链接：{ text, hyperlink }
    else if ('text' in v) v = v.text;
    // 错误值：{ error: '#DIV/0!' }
    else if ('error' in v) return '';
  }
  if (v instanceof Date) {
    const y = v.getFullYear();
    const m = String(v.getMonth() + 1).padStart(2, '0');
    const d = String(v.getDate()).padStart(2, '0');
    return `${y}-${m}-${d}`;
  }
  return v;
}

function readCellNum(cell: ExcelJS.Cell): number {
  const v = readCell(cell);
  if (v === '' || v == null) return 0;
  const n = Number(v);
  return isFinite(n) ? n : 0;
}

function readCellStr(cell: ExcelJS.Cell): string {
  const v = readCell(cell);
  return v == null ? '' : String(v).trim();
}

@UseGuards(JwtAuthGuard)
@Controller('expenses')
export class ExpensesController {
  constructor(
    private service: ExpensesService,
    private logService: OperationLogsService,
  ) {}

  @Get('lookups')
  @RequirePermission('expense:create', 'expense:edit')
  getFormLookups() {
    return this.service.getFormLookups();
  }

  @Post('student-utility')
  @RequirePermission('expense:create')
  async createStudentUtilityBill(@Body() dto: CreateStudentUtilityBillDto, @Request() req: any) {
    const result = await this.service.createStudentUtilityBill(dto, req.user?.id);
    const { ipAddress, userAgent } = extractRequestInfo(req);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '费用管理',
      action: '录入学生水电费并出账',
      targetId: result.bill.id,
      targetType: 'bill',
      detail: `学生${dto.studentId} ${dto.expenseType} ¥${dto.amount}，自动扣款 ¥${result.bill.paidAmount}`,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Post('room')
  @RequirePermission('expense:create')
  async createRoomExpense(@Body() dto: CreateRoomExpenseDto, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.createRoomExpense(dto, req.user?.id);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '费用管理',
      action: '录入费用',
      targetId: result.id,
      targetType: 'room_expense',
      detail: `房间${dto.roomId} ¥${dto.amount} ${dto.expenseType}`,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Post('room/batch')
  @RequirePermission('expense:create')
  async batchCreateRoomExpenses(@Body() dto: BatchRoomExpenseDto, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.batchCreateRoomExpenses(dto, req.user?.id);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '费用管理',
      action: '批量录入费用',
      detail: JSON.stringify(dto),
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Get('room')
  @RequirePermission('expense:view')
  findRoomExpenses(@Query() query: QueryRoomExpenseDto) {
    return this.service.findRoomExpenses(query);
  }

  @Delete('room/:id')
  @RequirePermission('expense:delete')
  async deleteRoomExpense(@Param('id', ParseIntPipe) id: number, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.deleteRoomExpense(id);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '费用管理',
      action: '归档费用',
      targetId: id,
      targetType: 'room_expense',
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Post('room/batch-delete')
  @RequirePermission('expense:delete')
  async batchDeleteRoomExpenses(@Body() body: { ids: number[] }, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.batchDeleteRoomExpenses(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('room/:id')
  @RequirePermission('expense:edit')
  async updateRoomExpense(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: UpdateRoomExpenseDto,
    @Request() req: any,
  ) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.updateRoomExpense(id, dto);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '费用管理',
      action: '编辑费用',
      targetId: id,
      targetType: 'room_expense',
      detail: `¥${dto.amount} ${dto.expenseType}`,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Post('personal')
  @RequirePermission('expense:create')
  async createPersonalExpense(@Body() dto: CreatePersonalExpenseDto, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.createPersonalExpense(dto, req.user?.id);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '费用管理',
      action: '录入费用',
      detail: `学生${dto.studentId} ¥${dto.amount} ${dto.expenseType}`,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Get('personal')
  @RequirePermission('expense:view')
  findPersonalExpenses(@Query() query: QueryPersonalExpenseDto) {
    return this.service.findPersonalExpenses(query);
  }

  @Delete('personal/:id')
  @RequirePermission('expense:delete')
  async deletePersonalExpense(@Param('id', ParseIntPipe) id: number, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.deletePersonalExpense(id);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '费用管理',
      action: '归档费用',
      targetId: id,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Post('personal/batch-delete')
  @RequirePermission('expense:delete')
  async batchDeletePersonalExpenses(@Body() body: { ids: number[] }, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.batchDeletePersonalExpenses(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('personal/:id')
  @RequirePermission('expense:edit')
  async updatePersonalExpense(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: UpdatePersonalExpenseDto,
    @Request() req: any,
  ) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.updatePersonalExpense(id, dto);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '费用管理',
      action: '编辑费用',
      targetId: id,
      detail: `¥${dto.amount} ${dto.expenseType}`,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Get('utility/template')
  @RequirePermission('expense:view')
  async downloadUtilityTemplate(@Res() res: Response) {
    const workbook = new ExcelJS.Workbook();
    const ws = workbook.addWorksheet('水电费导入模板');
    ws.columns = [
      { header: '序号', key: 'seq', width: 8 },
      { header: '时间', key: 'period', width: 30 },
      { header: '房间号', key: 'roomNumber', width: 12 },
      { header: '房间电量', key: 'electricity', width: 12 },
      { header: '电费', key: 'electricityFee', width: 10 },
      { header: '冷水用量(吨)', key: 'water', width: 14 },
      { header: '水费', key: 'waterFee', width: 10 },
      { header: '应缴金额', key: 'total', width: 12 },
    ];
    ws.getRow(1).font = { bold: true };
    ws.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } };
    ws.addRow({
      seq: 1,
      period: '2026-01-21 - 2026-02-08',
      roomNumber: '4-102',
      electricity: 50,
      electricityFee: 25.5,
      water: 3,
      waterFee: 14.7,
      total: 40.2,
    });
    res.setHeader(
      'Content-Type',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    );
    res.setHeader('Content-Disposition', 'attachment; filename=utility_template.xlsx');
    await workbook.xlsx.write(res);
    res.end();
  }

  @Post('utility/import')
  @RequirePermission('expense:create')
  @UseInterceptors(FileInterceptor('file'))
  async importUtilityExpenses(@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: any[] = [];
    ws.eachRow((row, idx) => {
      if (idx === 1) return; // 跳过表头
      const roomNumber = readCellStr(row.getCell(3));
      if (!roomNumber) return;
      rows.push({
        periodStr: readCellStr(row.getCell(2)),
        roomNumber,
        electricityAmount: readCellNum(row.getCell(4)),
        electricityFee: readCellNum(row.getCell(5)),
        waterAmount: readCellNum(row.getCell(6)),
        waterFee: readCellNum(row.getCell(7)),
        totalFee: readCellNum(row.getCell(8)),
      });
    });
    const result = await this.service.batchImportUtilityExpenses(rows, req.user?.id);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '费用管理',
      action: '导入水电费',
      detail: result.message,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Get('personal/template')
  @RequirePermission('expense:view')
  async downloadPersonalTemplate(@Res() res: Response) {
    const workbook = new ExcelJS.Workbook();
    const ws = workbook.addWorksheet('个人附加费导入模板');
    ws.columns = [
      { header: '学生姓名', key: 'studentName', width: 15 },
      { header: '费用类型', key: 'expenseType', width: 15 },
      { header: '金额', key: 'amount', width: 12 },
      { header: '费用日期', key: 'expenseDate', width: 15 },
      { header: '说明', key: 'description', width: 25 },
    ];
    ws.getRow(1).font = { bold: true };
    ws.getRow(1).fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFE0E0E0' } };
    ws.addRow({
      studentName: '张三',
      expenseType: '钥匙费',
      amount: 30,
      expenseDate: '2026-01-15',
      description: '丢失宿舍钥匙',
    });
    // 添加费用类型说明
    const noteSheet = workbook.addWorksheet('费用类型说明');
    noteSheet.columns = [{ header: '费用类型可用值', key: 'type', width: 25 }];
    ['物品损坏', '保洁费', '罚款', '钥匙费', '空调遥控器', '押金扣除', '其他'].forEach((t) =>
      noteSheet.addRow({ type: t }),
    );
    res.setHeader(
      'Content-Type',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    );
    res.setHeader('Content-Disposition', 'attachment; filename=personal_expense_template.xlsx');
    await workbook.xlsx.write(res);
    res.end();
  }

  @Post('personal/import')
  @RequirePermission('expense:create')
  @UseInterceptors(FileInterceptor('file'))
  async importPersonalExpenses(@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: any[] = [];
    ws.eachRow((row, idx) => {
      if (idx === 1) return;
      const studentName = readCellStr(row.getCell(1));
      if (!studentName) return;
      rows.push({
        studentName,
        expenseType: readCellStr(row.getCell(2)),
        amount: readCellNum(row.getCell(3)),
        expenseDate: readCellStr(row.getCell(4)),
        description: readCellStr(row.getCell(5)) || undefined,
      });
    });
    const result = await this.service.batchImportPersonalExpenses(rows, req.user?.id);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '费用管理',
      action: '导入个人附加费',
      detail: result.message,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Get('personal/export')
  @RequirePermission('expense:view')
  async exportPersonalExpenses(@Res() res: Response) {
    const data = await this.service.findPersonalExpenses();
    const workbook = new ExcelJS.Workbook();
    const ws = workbook.addWorksheet('个人附加费');
    ws.columns = [
      { header: '学生姓名', key: 'studentName', width: 15 },
      { header: '费用类型', key: 'expenseType', width: 15 },
      { header: '金额', key: 'amount', width: 12 },
      { header: '费用日期', key: 'expenseDate', width: 15 },
      { header: '说明', key: 'description', width: 30 },
    ];
    ws.getRow(1).font = { bold: true };
    data.forEach((d: any) => {
      ws.addRow({
        studentName: d.student?.name || '',
        expenseType: d.expenseType,
        amount: Number(d.amount),
        expenseDate: d.expenseDate,
        description: d.description || '',
      });
    });
    res.setHeader(
      'Content-Type',
      'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
    );
    res.setHeader('Content-Disposition', 'attachment; filename=personal_expenses_export.xlsx');
    await workbook.xlsx.write(res);
    res.end();
  }
}
