import {
  Controller,
  Get,
  Post,
  Put,
  Delete,
  Param,
  Body,
  Query,
  UseGuards,
  Request,
  Res,
  Req,
  ParseIntPipe,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, In } from 'typeorm';
import { BillsService } from './bills.service';
import { NotificationsService } from '../notifications/notifications.service';
import { NotificationType } from '../entities/notification.entity';
import { Student } from '../entities/student.entity';
import { Bill } from '../entities/bill.entity';
import { BillsExportService } from './bills-export.service';
import { CancelBillDto, GenerateBillsDto, UpdateBillStatusDto } from './dto/bill.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 type { Response } from 'express';

@UseGuards(JwtAuthGuard)
@Controller('bills')
export class BillsController {
  constructor(
    private service: BillsService,
    private exportService: BillsExportService,
    private logService: OperationLogsService,
    private readonly notificationsService: NotificationsService,
    @InjectRepository(Student) private studentRepo: Repository<Student>,
    @InjectRepository(Bill) private billRepo: Repository<Bill>,
  ) {}

  @Post('generate')
  @RequirePermission('bill:generate')
  async generateBills(@Body() dto: GenerateBillsDto, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.generateBills(dto);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '账单管理',
      action: '生成账单',
      detail: `周期 ${result.periodStart}~${result.periodEnd}, 生成 ${result.count} 条`,
      ipAddress,
      userAgent,
    });
    // Send bill_generated notifications
    try {
      for (const bill of result.bills) {
        const student = await this.studentRepo.findOne({ where: { id: bill.studentId } });
        if (student?.userId) {
          void this.notificationsService.create({
            recipientIds: [student.userId],
            type: NotificationType.BILL_GENERATED,
            title: '新账单',
            content: `您有一笔新账单,金额: ¥${bill.totalAmount}, 周期: ${result.periodStart}~${result.periodEnd}`,
          });
        }
      }
    } catch (_) { /* don't block response */ }
    return result;
  }

  @Get()
  @RequirePermission('bill:view')
  findAll(
    @Query('periodStart') periodStart?: string,
    @Query('periodEnd') periodEnd?: string,
    @Query('studentId', new ParseIntPipe({ optional: true })) studentId?: number,
    @Query('status') status?: string,
    @Query('expenseType') expenseType?: string,
  ) {
    return this.service.findAll({
      periodStart, periodEnd,
      studentId,
      status, expenseType,
    });
  }

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

  @Put(':id/status')
  @RequirePermission('bill:confirm')
  async updateStatus(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: UpdateBillStatusDto,
    @Request() req: any,
  ) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.updateStatus(id, dto);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '账单管理',
      action: '确认账单',
      targetId: id,
      targetType: 'bill',
      ipAddress,
      userAgent,
    });
    // Send bill_paid notification
    try {
      const student = await this.studentRepo.findOne({ where: { id: result.studentId } });
      if (student?.userId) {
        void this.notificationsService.create({
          recipientIds: [student.userId],
          type: NotificationType.BILL_PAID,
          title: '账单已确认',
          content: `账单 #${result.id} 已确认收款,金额: ¥${result.totalAmount}`,
        });
      }
    } catch (_) { /* don't block response */ }
    return result;
  }

  @Put('batch/status')
  @RequirePermission('bill:confirm')
  async batchUpdateStatus(@Body() body: { ids: number[]; status: string }, @Request() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const result = await this.service.batchUpdateStatus(body.ids, body.status);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '账单管理',
      action: '确认账单',
      detail: `IDs: ${body.ids.join(',')}`,
      ipAddress,
      userAgent,
    });
    // Send bill_paid notifications (batch)
    try {
      const bills = await this.billRepo.findBy({ id: In(body.ids) });
      for (const bill of bills) {
        const student = await this.studentRepo.findOne({ where: { id: bill.studentId } });
        if (student?.userId) {
          void this.notificationsService.create({
            recipientIds: [student.userId],
            type: NotificationType.BILL_PAID,
            title: '账单已确认',
            content: `账单 #${bill.id} 已确认收款,金额: ¥${bill.totalAmount}`,
          });
        }
      }
    } catch (_) { /* don't block response */ }
    return result;
  }

  @Post(':id/cancel')
  @RequirePermission('bill:delete')
  async cancel(@Param('id', ParseIntPipe) id: number, @Body() dto: CancelBillDto, @Request() req: any) {
    const result = await this.service.cancel(id, dto, req.user?.id);
    const { ipAddress, userAgent } = extractRequestInfo(req);
    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '账单管理',
      action: '取消账单并冲正',
      targetId: id,
      targetType: 'bill',
      detail: dto.reason,
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Delete(':id')
  @RequirePermission('bill: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: 'bill',
      ipAddress,
      userAgent,
    });
    return result;
  }

  @Post('batch/delete')
  @RequirePermission('bill: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;
  }

  @Get('export/excel')
  @RequirePermission('bill:export-excel')
  async exportExcel(
    @Query('periodStart') periodStart?: string,
    @Query('periodEnd') periodEnd?: string,
    @Query('studentId', new ParseIntPipe({ optional: true })) studentId?: number,
    @Query('status') status?: string,
    @Res() res?: Response,
    @Req() req?: any,
  ) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    await this.logService.log({
      userId: req?.user?.id,
      username: req?.user?.username,
      module: '账单管理',
      action: '导出账单',
      detail: `筛选: 周期${periodStart || '全部'}~${periodEnd || '全部'}, 状态${status || '全部'}`,
      ipAddress,
      userAgent,
    });
    return this.exportService.exportExcel(
      {
        periodStart,
        periodEnd,
        studentId,
        status,
      },
      res!,
    );
  }

  @Get('export/pdf/:id')
  @RequirePermission('bill:export-pdf')
  async exportPdf(@Param('id', ParseIntPipe) id: number, @Res() res: Response, @Req() req: any) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    await this.logService.log({
      userId: req?.user?.id,
      username: req?.user?.username,
      module: '账单管理',
      action: '导出账单',
      targetId: id,
      targetType: 'bill',
      ipAddress,
      userAgent,
    });
    return this.exportService.exportStudentPdf(id, res);
  }
}
