import { Controller, Get, Post, Sse, Body, Param, Query, Request, BadRequestException, ForbiddenException, ParseIntPipe } from '@nestjs/common';
import { Observable, filter } from 'rxjs';
import { AttendanceControllerBase, RequestUser, SseEvent } from './attendance.controller-base';
import { AttendanceService } from './attendance.service';
import { AttendanceImportService } from './attendance-import.service';
import { OperationLogsService } from '../operation-logs/operation-logs.service';
import { AuthorizationService } from '../authorization';
import { logAudit } from '../common/with-audit-log';
import { extractRequestInfo } from '../common/request-utils';
import { RequirePermission } from '../auth/decorators/permission.decorator';
import { DingTalkImportDto } from './dto/dingtalk-import.dto';
import { QueryDingRawDto, MatchDingRecordDto } from './dto/attendance.dto';

@Controller()
export class AttendanceImportController extends AttendanceControllerBase {
  constructor(
    service: AttendanceService,
    importService: AttendanceImportService,
    logService: OperationLogsService,
    authz: AuthorizationService,
  ) {
    super(service, importService, logService, authz);
  }

  @Get('ding-attendance-raw')
  @RequirePermission('attendance:view')
  async getDingRaw(@Query() query: QueryDingRawDto, @Request() req: { user: RequestUser }) {
    if (query.classId) await this.assertClassAccess(req, query.classId);
    return this.service.getDingRaw(query, await this.getAccessibleClassIds(req));
  }

  // ── Match a dingtalk record to a student ──
  @Post('ding-attendance-raw/:id/match')
  @RequirePermission('attendance:edit')
  async matchDingRecord(
    @Param('id', ParseIntPipe) id: number,
    @Body() dto: MatchDingRecordDto,
    @Request() req: any,
  ) {
    const result = await this.service.matchDingRecord(id, dto);
    await logAudit(this.logService, req, {
      module: '考勤管理', action: '匹配考勤记录', targetId: id, targetType: 'dingAttendanceRaw', detail: `匹配到学生 ${dto.studentId}`,
    });
    return result;
  }

  // ── Attendance class-based report export ──

  @Post('ding-attendance-raw/auto-match')
  @RequirePermission('attendance:edit')
  async autoMatch() {
    return this.service.autoMatchDingRecords();
  }

  // ═══════════════════════════════════════════════════════════════
  // DingTalk attendance import with SSE streaming progress
  // ═══════════════════════════════════════════════════════════════

  @Get('attendance-records/import/dingtalk/classes')
  @RequirePermission('attendance:create')
  getDingTalkImportClasses(@Request() req: { user: RequestUser }) {
    return this.service.getImportableClasses(req.user.id, this.canManageAllAttendance(req));
  }

  /**
   * Trigger DingTalk attendance import.
   * Mirrors `dws attendance check result` pipeline:
   *   fetch → parse → deduplicate → save → auto-match.
   */
  @Post('attendance-records/import/dingtalk')
  @RequirePermission('attendance:create')
  async importFromDingTalk(@Body() dto: DingTalkImportDto, @Request() req: { user: RequestUser }) {
    const { ipAddress, userAgent } = extractRequestInfo(req);
    const canManageAll = this.canManageAllAttendance(req);
    let userIds: string[];

    if (dto.users) {
      if (!canManageAll) {
        throw new ForbiddenException('仅管理员可指定钉钉用户范围');
      }
      userIds = dto.users
        .split(',')
        .map((value) => value.trim())
        .filter(Boolean);
    } else {
      if (!dto.classId) {
        throw new BadRequestException('请选择要拉取考勤的班级');
      }
      userIds = await this.service.getTeacherClassDingUserIds(
        req.user.id,
        dto.classId,
        canManageAll,
        dto.start,
      );
    }

    const startDate = dto.start ?? this.getTodayDateOnly();
    const endDate = dto.end ?? startDate;
    const result = await this.importService.importFromDingTalk({
      startDate,
      endDate,
      userIds,
      autoMatch: true,
      userId: req.user.id,
    });

    await this.logService.log({
      userId: req.user?.id,
      username: req.user?.username,
      module: '考勤管理',
      action: '钉钉考勤导入',
      detail: `${startDate}~${endDate}, 导入=${result.imported}, 跳过=${result.skipped}, 匹配=${result.matched}`,
      ipAddress,
      userAgent,
    });

    return result;
  }

  /**
   * SSE stream for live import progress.
   * Connect before triggering the import to receive real-time progress events.
   *
   * NOTE: @RequirePermission works with @Sse() in NestJS because guards
   * execute in the standard request pipeline before the SSE handler is invoked.
   * If this ever breaks after a NestJS upgrade, verify guard execution order.
   */
  @Sse('attendance-records/import/dingtalk/stream')
  @RequirePermission('attendance:view')
  importProgressStream(@Request() req: { user: RequestUser }): Observable<SseEvent> {
    const userId = req.user.id;
    return new Observable<SseEvent>((subscriber) => {
      const subscription = this.importService.progress$
        .pipe(filter((event) => event.userId === userId))
        .subscribe({
          next: (event) => {
            subscriber.next({ data: JSON.stringify(event) });
            if (event.phase === 'complete' || event.phase === 'error') {
              subscriber.complete();
            }
          },
          error: (err: unknown) => subscriber.error(err),
        });
      return () => subscription.unsubscribe();
    });
  }
}
