import {
  Controller,
  Post,
  Body,
  HttpCode,
  HttpStatus,
  Logger,
  Headers,
  UnauthorizedException,
} from '@nestjs/common';
import { ApiTags, ApiOperation, ApiResponse, ApiHeader } from '@nestjs/swagger';
import { ConfigService } from '@nestjs/config';
import { EventsGateway } from '../events/events.gateway';
import { PrismaService } from '../../database/prisma.service';
import { NotificationsService } from '../notifications/notifications.service';
import { Public } from '../../common/decorators';
import { ProjectStatus, RenderStage } from '@prisma/client';

// ── DTO Tanımları ──────────────────────────────────────────

export class RenderProgressDto {
  projectId: string;
  renderJobId?: string;
  progress: number;
  stage: string;
  stageLabel: string;
  currentScene?: number;
  totalScenes?: number;
  eta?: number;
  stepDurationMs?: number;
}

export class RenderCompletedDto {
  projectId: string;
  renderJobId?: string;
  finalVideoUrl: string;
  thumbnailUrl?: string;
  processingTimeMs: number;
  fileSize: number;
}

export class RenderFailedDto {
  projectId: string;
  renderJobId?: string;
  error: string;
  stage: string;
  attemptNumber: number;
  canRetry?: boolean;
}

/**
 * Render Callback Controller
 *
 * C# Media Worker, render ilerlemesini bu endpoint'ler aracılığıyla bildirir.
 * Public endpoint — JWT gerekmez, bunun yerine API key ile korunur.
 *
 * Flow: C# Worker → HTTP POST → Bu controller → EventsGateway → Frontend (WebSocket)
 */
@ApiTags('render-callback')
@Controller('render-callback')
export class RenderCallbackController {
  private readonly logger = new Logger(RenderCallbackController.name);
  private readonly apiKey: string;

  constructor(
    private readonly eventsGateway: EventsGateway,
    private readonly db: PrismaService,
    private readonly configService: ConfigService,
    private readonly notificationsService: NotificationsService,
  ) {
    this.apiKey = this.configService.get<string>('RENDER_CALLBACK_API_KEY', 'contgen-worker-secret-2026');
  }

  /**
   * API key doğrulaması — C# Worker'ın kimliğini kontrol eder.
   */
  private validateApiKey(authHeader?: string) {
    const key = authHeader?.replace('Bearer ', '');
    if (key !== this.apiKey) {
      throw new UnauthorizedException('Geçersiz worker API key');
    }
  }

  /**
   * Render ilerleme bildirimi.
   * C# Worker her aşamada bu endpoint'i çağırır.
   */
  @Public()
  @Post('progress')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Render ilerleme bildirimi (Worker → API)' })
  @ApiHeader({ name: 'Authorization', description: 'Bearer {WORKER_API_KEY}' })
  @ApiResponse({ status: 200, description: 'İlerleme kaydedildi' })
  async reportProgress(
    @Headers('authorization') authHeader: string,
    @Body() body: RenderProgressDto,
  ) {
    this.validateApiKey(authHeader);

    this.logger.log(
      `Progress: ${body.projectId} — %${body.progress} [${body.stage}] sahne ${body.currentScene}/${body.totalScenes}`,
    );

    // DB'de proje progress'ini güncelle
    await this.db.project.update({
      where: { id: body.projectId },
      data: {
        progress: body.progress,
        status: this.mapStageToStatus(body.stage) as ProjectStatus,
      },
    });

    // RenderJob log yaz
    if (body.renderJobId) {
      await this.db.renderLog.create({
        data: {
          renderJobId: body.renderJobId,
          stage: body.stage as RenderStage,
          level: 'INFO',
          message: body.stageLabel || `${body.stage} — %${body.progress}`,
          durationMs: body.stepDurationMs,
        },
      });
    }

    // WebSocket ile frontend'e bildir
    this.eventsGateway.emitRenderProgress(body.projectId, {
      progress: body.progress,
      stage: body.stage,
      stageLabel: body.stageLabel,
      currentScene: body.currentScene,
      totalScenes: body.totalScenes,
      eta: body.eta,
    });

    return { status: 'ok', progress: body.progress };
  }

  /**
   * Render tamamlandı bildirimi.
   */
  @Public()
  @Post('completed')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Render tamamlandı bildirimi (Worker → API)' })
  @ApiHeader({ name: 'Authorization', description: 'Bearer {WORKER_API_KEY}' })
  async reportCompleted(
    @Headers('authorization') authHeader: string,
    @Body() body: RenderCompletedDto,
  ) {
    this.validateApiKey(authHeader);

    this.logger.log(
      `COMPLETED: ${body.projectId} — video: ${body.finalVideoUrl} (${body.processingTimeMs}ms)`,
    );

    // Proje durumunu güncelle
    await this.db.project.update({
      where: { id: body.projectId },
      data: {
        status: 'COMPLETED',
        progress: 100,
        finalVideoUrl: body.finalVideoUrl,
        thumbnailUrl: body.thumbnailUrl || null,
        completedAt: new Date(),
      },
    });

    // RenderJob durumunu güncelle
    if (body.renderJobId) {
      await this.db.renderJob.update({
        where: { id: body.renderJobId },
        data: {
          status: 'COMPLETED',
          finalVideoUrl: body.finalVideoUrl,
          processingTimeMs: body.processingTimeMs,
          completedAt: new Date(),
        },
      });

      await this.db.renderLog.create({
        data: {
          renderJobId: body.renderJobId,
          stage: 'FINALIZATION' as RenderStage,
          level: 'INFO',
          message: `Video tamamlandı — ${body.processingTimeMs}ms, ${body.fileSize} bytes`,
          durationMs: body.processingTimeMs,
        },
      });
    }

    // WebSocket ile frontend'e bildir
    this.eventsGateway.emitRenderCompleted(body.projectId, {
      finalVideoUrl: body.finalVideoUrl,
      thumbnailUrl: body.thumbnailUrl,
      processingTimeMs: body.processingTimeMs,
      fileSize: body.fileSize,
    });

    this.eventsGateway.emitProjectStatusChanged(body.projectId, 'COMPLETED');

    // Proje sahibine in-app bildirim gönder
    const project = await this.db.project.findUnique({
      where: { id: body.projectId },
      select: { userId: true, title: true },
    });
    if (project) {
      await this.notificationsService.createNotification({
        userId: project.userId,
        type: 'render_complete',
        title: 'Video hazır! 🎬',
        message: `"${project.title}" videonuz başarıyla oluşturuldu.`,
        metadata: {
          projectId: body.projectId,
          renderJobId: body.renderJobId,
          finalVideoUrl: body.finalVideoUrl,
        },
      });
    }

    return { status: 'ok', projectId: body.projectId };
  }

  /**
   * Render hatası bildirimi.
   */
  @Public()
  @Post('failed')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Render hatası bildirimi (Worker → API)' })
  @ApiHeader({ name: 'Authorization', description: 'Bearer {WORKER_API_KEY}' })
  async reportFailed(
    @Headers('authorization') authHeader: string,
    @Body() body: RenderFailedDto,
  ) {
    this.validateApiKey(authHeader);

    this.logger.error(
      `FAILED: ${body.projectId} — ${body.error} [${body.stage}] attempt: ${body.attemptNumber}`,
    );

    // Proje durumunu güncelle
    await this.db.project.update({
      where: { id: body.projectId },
      data: {
        status: 'FAILED',
        errorMessage: body.error,
      },
    });

    // RenderJob durumunu güncelle
    if (body.renderJobId) {
      await this.db.renderJob.update({
        where: { id: body.renderJobId },
        data: {
          status: 'FAILED',
          errorMessage: body.error,
          completedAt: new Date(),
        },
      });

      await this.db.renderLog.create({
        data: {
          renderJobId: body.renderJobId,
          stage: body.stage as RenderStage,
          level: 'ERROR',
          message: body.error,
        },
      });
    }

    // WebSocket ile frontend'e bildir
    this.eventsGateway.emitRenderFailed(body.projectId, {
      error: body.error,
      stage: body.stage,
      attemptNumber: body.attemptNumber,
      canRetry: body.canRetry ?? true,
    });

    this.eventsGateway.emitProjectStatusChanged(body.projectId, 'FAILED');

    // Proje sahibine in-app bildirim gönder
    const project = await this.db.project.findUnique({
      where: { id: body.projectId },
      select: { userId: true, title: true },
    });
    if (project) {
      await this.notificationsService.createNotification({
        userId: project.userId,
        type: 'render_failed',
        title: 'Video oluşturulamadı ⚠️',
        message: `"${project.title}" işlenirken hata oluştu: ${body.error}`,
        metadata: {
          projectId: body.projectId,
          renderJobId: body.renderJobId,
          stage: body.stage,
          canRetry: body.canRetry ?? true,
        },
      });
    }

    return { status: 'ok', projectId: body.projectId };
  }

  private mapStageToStatus(stage: string): string {
    switch (stage) {
      case 'tts':
      case 'image_generation':
      case 'music_generation':
        return 'GENERATING_MEDIA';
      case 'compositing':
      case 'encoding':
        return 'RENDERING';
      default:
        return 'GENERATING_MEDIA';
    }
  }
}

