import prisma from "@shieldai/db";
import { AnalysisService } from "./AnalysisService";
import {
  BatchAnalyzeInput,
  AnalysisJobStatus,
  AnalysisType,
  AnalysisResultOutput,
} from "@shieldai/types";
import { logger } from "../logger";

export class BatchAnalysisService {
  private analysisService: AnalysisService;
  private readonly maxConcurrency = 5;

  constructor() {
    this.analysisService = new AnalysisService();
  }

  async analyzeBatch(
    input: BatchAnalyzeInput,
    userId: string
  ): Promise<BatchResult> {
    const batchId = `batch_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
    logger.info("Starting batch analysis", { batchId, userId, totalFiles: input.audioBuffers.length });
    const results: AnalysisResultOutput[] = [];
    const errors: Array<{ name: string; error: string }> = [];

    const processWithConcurrency = async (limit: number) => {
      for (let i = 0; i < input.audioBuffers.length; i += limit) {
        const chunk = input.audioBuffers.slice(i, i + limit);
        
        const promises = chunk.map(async (audioInput: { name: string; buffer: Buffer; sampleRate?: number }) => {
          try {
            const result = await this.analysisService.analyze(
              {
                audioBuffer: audioInput.buffer,
                sampleRate: audioInput.sampleRate,
                analysisType: input.analysisType || AnalysisType.SYNTHETIC_DETECTION,
              },
              userId
            );
            return { success: true, result, name: audioInput.name };
          } catch (err) {
            const message = err instanceof Error ? err.message : "Analysis failed";
            return { success: false, error: message, name: audioInput.name };
          }
        });
        
        const outcomes = await Promise.allSettled(promises);
        
        for (const outcome of outcomes) {
          if (outcome.status === 'fulfilled') {
            if (outcome.value.success && outcome.value.result) {
              results.push(outcome.value.result);
            } else if (!outcome.value.success && outcome.value.name) {
              errors.push({ name: outcome.value.name, error: outcome.value.error || "Analysis failed" });
            }
          }
        }
      }
    };

    await processWithConcurrency(this.maxConcurrency);

    logger.info("Batch analysis completed", { 
      batchId, 
      successfulResults: results.length, 
      failedCount: errors.length 
    });

    return {
      batchId,
      jobId: `batch_${batchId}`,
      totalFiles: input.audioBuffers.length,
      successfulResults: results.length,
      failedCount: errors.length,
      results,
      errors,
    };
  }

  async getBatchResult(batchJobId: string): Promise<BatchResult | null> {
    const job = await prisma.analysisJob.findUnique({
      where: { id: batchJobId },
      include: { result: true },
    });

    if (!job) return null;

    const childJobs = await prisma.analysisJob.findMany({
      where: {
        userId: job.userId,
        createdAt: { gte: job.createdAt, lt: new Date(job.createdAt.getTime() + 60000) },
        id: { not: job.id },
      },
      include: { result: true },
    });

    const results: AnalysisResultOutput[] = [];
    const errors: Array<{ name: string; error: string }> = [];

    for (const childJob of childJobs) {
      if (childJob.result) {
        const r = childJob.result;
        results.push({
          jobId: childJob.id,
          syntheticScore: r.syntheticScore,
          verdict: r.verdict,
          confidence: r.confidence,
          matchedEnrollmentId: r.matchedEnrollmentId || undefined,
          matchedSimilarity: r.matchedSimilarity || undefined,
          processingTimeMs: r.processingTimeMs,
          modelVersion: r.modelVersion || undefined,
        });
      } else if (childJob.errorMessage) {
        errors.push({ name: childJob.audioFilePath, error: childJob.errorMessage });
      }
    }

    return {
      batchId: job.audioFilePath.split("/").pop() || job.id,
      jobId: job.id,
      totalFiles: childJobs.length,
      successfulResults: results.length,
      failedCount: errors.length,
      results,
      errors,
    };
  }
}

export interface BatchResult {
  batchId: string;
  jobId: string;
  totalFiles: number;
  successfulResults: number;
  failedCount: number;
  results: AnalysisResultOutput[];
  errors: Array<{ name: string; error: string }>;
}
