import { Body, Controller, Param, Post, Query, Req, UseGuards, UsePipes, ValidationPipe } from '@nestjs/common';
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
import { PrismaService } from '../../infrastructure/database/prisma.service';
import { BatchUploadReadingEventsDto } from './reading-event.dto';
import { ReadingEventProcessorService } from './reading-event-processor.service';

const MAX_BATCH_SIZE = 100;

@Controller()
@UseGuards(JwtAuthGuard)
export class ReadingEventController {
  constructor(
    private readonly prisma: PrismaService,
    private readonly processor: ReadingEventProcessorService,
  ) {}

  @Post('learning/reading-events/batch')
  @UsePipes(new ValidationPipe({ whitelist: true, forbidNonWhitelisted: true }))
  async uploadBatch(
    @Req() req: any,
    @Body() body: BatchUploadReadingEventsDto,
  ) {
    const userId = req.user.id;
    const { events } = body;

    if (events.length > MAX_BATCH_SIZE) {
      return {
        processed: 0,
        duplicate: 0,
        failed: events.length,
        warnings: [{
          code: 'BATCH_LIMIT_EXCEEDED',
          message: `Batch size ${events.length} exceeds limit of ${MAX_BATCH_SIZE}`,
        }],
      };
    }

    return this.processor.processBatch(userId, events);
  }

  @Post('internal/learning/reading-events/:id/reprocess')
  async reprocessOne(
    @Req() req: any,
    @Param('id') id: string,
    @Query('force') force?: string,
  ) {
    const event = await this.prisma.readingEvent.findUnique({ where: { id } });
    if (!event) return { error: 'Event not found' };

    // processed events require force=true to re-process
    if (event.status === 'processed' && force !== 'true') {
      return { error: 'Event already processed. Use ?force=true to re-process.' };
    }
    if (!['failed', 'pending', 'processing'].includes(event.status) && force !== 'true') {
      return { error: `Event status ${event.status} not eligible for reprocess. Use ?force=true.` };
    }

    const result = await this.processor.processOne(event.userId, {
      eventId: event.eventId,
      clientSessionId: event.clientSessionId,
      materialId: event.materialId,
      readingTargetType: event.readingTargetType,
      eventType: event.eventType,
      position: event.position,
      activeSecondsDelta: event.activeSecondsDelta,
      clientTimestampMs: Number(event.clientTimestampMs),
      clientTimezoneOffsetMinutes: event.clientTimezoneOffsetMinutes,
      sequence: event.sequence,
      platform: event.platform,
      appVersion: event.appVersion,
    });

    return { id, result };
  }

  @Post('internal/learning/reading-events/reprocess-failed')
  async reprocessFailed(@Req() req: any, @Query('limit') limitStr?: string) {
    const limit = Math.min(Number(limitStr ?? 50) || 50, 200);

    const failed = await this.prisma.readingEvent.findMany({
      where: { status: 'failed' },
      take: limit,
      orderBy: { createdAt: 'asc' },
    });

    const results: Array<{ id: string; outcome: string }> = [];
    for (const event of failed) {
      const r = await this.processor.processOne(event.userId, {
        eventId: event.eventId,
        clientSessionId: event.clientSessionId,
        materialId: event.materialId,
        readingTargetType: event.readingTargetType,
        eventType: event.eventType,
        position: event.position,
        activeSecondsDelta: event.activeSecondsDelta,
        clientTimestampMs: Number(event.clientTimestampMs),
        clientTimezoneOffsetMinutes: event.clientTimezoneOffsetMinutes,
        sequence: event.sequence,
        platform: event.platform,
        appVersion: event.appVersion,
      });
      results.push({ id: event.id, outcome: r.outcome });
    }

    return { reprocessed: results.length, results };
  }
}
