/**
 * Course Quick Generate AI Pipeline
 *
 * Generates a complete course with chapters, lessons, and MCQs
 * from basic user inputs (title, description, format, difficulty).
 */

import { generateWithAI } from '../../utils/aiProvider';
import {
  buildCourseIdentityPrompt,
  buildCourseChapterPrompt,
  buildCourseLessonPrompt,
  buildCourseEnhancementPrompt,
  CoursePipelineInputs,
  PromptResult,
} from './coursePrompts';
import { parseJsonFromAI } from './parseJsonFromAI';

// ============================================
// TYPES
// ============================================

export interface CoursePipelineResult {
  course: Record<string, any>;
  chapters: ChapterResult[];
  pipelineVersion: string;
  provider: string;
  aiModel: string;
  tokensUsed: number;
  inputTokens: number;
  outputTokens: number;
  processingTimeMs: number;
  latencyMs: number;
  finishReason: string | null;
  apiKeyMasked: string | null;
  overallConfidence: number;
  stageResults: StageResult[];
  errors: string[];
}

export interface ChapterResult {
  chapter: Record<string, any>;
  lessons: Record<string, any>[];
}

export interface StageResult {
  stage: string;
  success: boolean;
  provider?: string;
  aiModel?: string;
  tokensUsed?: number;
  error?: string;
  duration: number;
  enhanced?: boolean;
}

const PIPELINE_VERSION = '1.0';

// ============================================
// PIPELINE CLASS
// ============================================

export class CoursePipeline {
  private inputs: CoursePipelineInputs;
  private courseAccumulated: Record<string, any>;
  private chaptersAccumulated: ChapterResult[];
  private stageResults: StageResult[];
  private errors: string[];
  private totalTokens: number;
  private totalInputTokens: number;
  private totalOutputTokens: number;
  private totalLatencyMs: number;
  private lastFinishReason: string | null;
  private lastApiKeyMasked: string | null;
  private startTime: number;
  private lastProvider: string;
  private lastAiModel: string;
  private onProgress?: (progress: number, step: string) => void;

  constructor(inputs: CoursePipelineInputs, onProgress?: (progress: number, step: string) => void) {
    this.inputs = inputs;
    this.courseAccumulated = {};
    this.chaptersAccumulated = [];
    this.stageResults = [];
    this.errors = [];
    this.totalTokens = 0;
    this.totalInputTokens = 0;
    this.totalOutputTokens = 0;
    this.totalLatencyMs = 0;
    this.lastFinishReason = null;
    this.lastApiKeyMasked = null;
    this.startTime = Date.now();
    this.lastProvider = 'unknown';
    this.lastAiModel = 'unknown';
    this.onProgress = onProgress;
  }

  async run(): Promise<CoursePipelineResult> {
    // Stage 1: Course Identity
    this.onProgress?.(5, 'Creating course concept...');
    await this.runStageWithRetry('course-identity', () =>
      this.executeStage(buildCourseIdentityPrompt(this.inputs), 'identity', 0)
    );
    this.onProgress?.(35, 'Course concept defined');

    // Stage 2: Chapter Structure + Quizzes
    this.onProgress?.(40, 'Building course structure...');
    await this.runStageWithRetry('course-chapters', () =>
      this.executeStage(
        buildCourseChapterPrompt(this.inputs, { course: this.courseAccumulated }),
        'chapters',
        1
      )
    );
    this.onProgress?.(65, 'Course structure generated');

    // Stage 3: Lesson Content + Quizzes
    this.onProgress?.(70, 'Generating lesson content...');
    await this.runStageWithRetry('course-lessons', () =>
      this.executeStage(
        buildCourseLessonPrompt(this.inputs, { course: this.courseAccumulated, chapters: this.chaptersAccumulated.map(c => c.chapter) }),
        'lessons',
        2
      )
    );
    this.onProgress?.(95, 'Finalizing course');

    const overallConfidence = this.computeOverallConfidence();

    return {
      course: this.courseAccumulated,
      chapters: this.chaptersAccumulated,
      pipelineVersion: PIPELINE_VERSION,
      provider: this.lastProvider,
      aiModel: this.lastAiModel,
      tokensUsed: this.totalTokens,
      inputTokens: this.totalInputTokens,
      outputTokens: this.totalOutputTokens,
      processingTimeMs: Date.now() - this.startTime,
      latencyMs: this.totalLatencyMs,
      overallConfidence,
      finishReason: this.lastFinishReason,
      apiKeyMasked: this.lastApiKeyMasked,
      stageResults: this.stageResults,
      errors: this.errors,
    };
  }

  // ============================================
  // STAGE EXECUTION
  // ============================================

  private async executeStage(promptConfig: PromptResult, stageName: string, stageIndex: number): Promise<void> {
    console.log(`[Course-Pipeline] Executing stage "${stageName}" with ${promptConfig.maxTokens} maxTokens`);

    const result = await generateWithAI(
      promptConfig.userPrompt,
      promptConfig.systemPrompt,
      promptConfig.maxTokens
    );

    this.lastProvider = result.provider;
    this.lastAiModel = result.model;
    this.totalTokens += result.tokenUsage?.totalTokens ?? 0;
    this.totalInputTokens += result.tokenUsage?.inputTokens ?? 0;
    this.totalOutputTokens += result.tokenUsage?.outputTokens ?? 0;
    if (result.latencyMs) this.totalLatencyMs += result.latencyMs;
    if (result.finishReason) this.lastFinishReason = result.finishReason;
    if (result.keyUsed) this.lastApiKeyMasked = result.keyUsed;

    console.log(`[Course-Pipeline] Stage "${stageName}" response received. Provider: ${result.provider}, Content length: ${result.content?.length || 0}`);

    const parsed = parseJsonFromAI(result.content);
    if (!parsed) {
      throw new Error(`AI response could not be parsed as JSON for stage: ${stageName}`);
    }

    // Extract course data
    if (parsed.course && typeof parsed.course === 'object' && !Array.isArray(parsed.course)) {
      this.mergeCourse(parsed.course);
    }

    // Extract chapters
    if (parsed.chapters && Array.isArray(parsed.chapters)) {
      for (let i = 0; i < parsed.chapters.length; i++) {
        const ch = parsed.chapters[i];
        if (ch && typeof ch === 'object' && ch.title) {
          this.mergeIntoChapters(i, ch);
        }
      }
    }
  }

  private async runStageWithRetry(stageName: string, stageFn: () => Promise<void>): Promise<void> {
    const stageStart = Date.now();

    try {
      await stageFn();
      this.stageResults.push({
        stage: stageName,
        success: true,
        provider: this.lastProvider,
        aiModel: this.lastAiModel,
        duration: Date.now() - stageStart,
      });
    } catch (error: any) {
      this.errors.push(`Stage ${stageName} failed: ${error.message}`);
      this.stageResults.push({
        stage: stageName,
        success: false,
        error: error.message,
        duration: Date.now() - stageStart,
      });
      return;
    }

    const lowFields = this.getLowConfidenceFields(stageName);
    if (lowFields.length > 0) {
      await this.enhanceStage(stageName, lowFields);
    }
  }

  private async enhanceStage(stageName: string, lowConfidenceFields: string[]): Promise<void> {
    const enhanceStart = Date.now();

    try {
      const stageOutput = { course: this.courseAccumulated, chapters: this.chaptersAccumulated.map(c => c.chapter) };
      const enhancePrompt = buildCourseEnhancementPrompt(stageName, stageOutput, lowConfidenceFields);

      const result = await generateWithAI(
        enhancePrompt.userPrompt,
        enhancePrompt.systemPrompt,
        enhancePrompt.maxTokens
      );

      this.totalTokens += result.tokenUsage?.totalTokens ?? 0;
      this.totalInputTokens += result.tokenUsage?.inputTokens ?? 0;
      this.totalOutputTokens += result.tokenUsage?.outputTokens ?? 0;
      if (result.latencyMs) this.totalLatencyMs += result.latencyMs;
      if (result.finishReason) this.lastFinishReason = result.finishReason;
      if (result.keyUsed) this.lastApiKeyMasked = result.keyUsed;
      const parsed = parseJsonFromAI(result.content);
      if (parsed) {
        if (parsed.course) this.mergeCourse(parsed.course);
        if (parsed.chapters && Array.isArray(parsed.chapters)) {
          for (let i = 0; i < parsed.chapters.length; i++) {
            const ch = parsed.chapters[i];
            if (ch && typeof ch === 'object') this.mergeIntoChapters(i, ch);
          }
        }
        this.stageResults.push({
          stage: `${stageName}-enhancement`,
          success: true,
          provider: result.provider,
          aiModel: result.model,
          duration: Date.now() - enhanceStart,
          enhanced: true,
        });
      }
    } catch (error: any) {
      console.warn(`[Course-Pipeline] Enhancement for ${stageName} failed: ${error.message}`);
    }
  }

  // ============================================
  // RESULT MERGING
  // ============================================

  private mergeCourse(parsed: Record<string, any>): void {
    if (!parsed || typeof parsed !== 'object') return;
    for (const [key, value] of Object.entries(parsed)) {
      if (key === 'chapters' || key === 'lessons') continue;
      if (value !== null && value !== undefined) {
        const existing = this.courseAccumulated[key];
        if (existing === undefined || existing === null || existing === '' || (Array.isArray(existing) && existing.length === 0)) {
          this.courseAccumulated[key] = value;
        } else if (Array.isArray(value) && value.length > 0 && Array.isArray(existing) && value.length > existing.length) {
          this.courseAccumulated[key] = value;
        }
      }
    }
  }

  private mergeIntoChapters(index: number, ch: Record<string, any>): void {
    if (!ch || typeof ch === 'object' && ch.title) {
      // ch is a valid chapter object
    } else {
      return;
    }
    if (index < this.chaptersAccumulated.length) {
      const target = this.chaptersAccumulated[index];
      // Merge chapter-level fields
      for (const [key, value] of Object.entries(ch)) {
        if (key === 'lessons') continue;
        if (value !== null && value !== undefined) {
          const existing = target.chapter[key];
          if (existing === undefined || existing === null || existing === '' || (Array.isArray(existing) && existing.length === 0)) {
            target.chapter[key] = value;
          }
        }
      }
      // Merge lessons
      if (ch.lessons && Array.isArray(ch.lessons)) {
        for (let j = 0; j < ch.lessons.length; j++) {
          const lesson = ch.lessons[j];
          if (lesson && typeof lesson === 'object') {
            if (j < target.lessons.length) {
              for (const [key, value] of Object.entries(lesson)) {
                if (value !== null && value !== undefined) {
                  const existing = target.lessons[j][key];
                  if (existing === undefined || existing === null || existing === '' || (Array.isArray(existing) && existing.length === 0)) {
                    target.lessons[j][key] = value;
                  }
                }
              }
            } else {
              target.lessons.push({ ...lesson });
            }
          }
        }
      }
    } else {
      const lessons = (ch.lessons && Array.isArray(ch.lessons))
        ? ch.lessons.map((l: any) => ({ ...l }))
        : [];
      const { lessons: _lessons, ...chapterData } = ch;
      this.chaptersAccumulated.push({ chapter: { ...chapterData }, lessons });
    }
  }

  // ============================================
  // CONFIDENCE ANALYSIS
  // ============================================

  private getLowConfidenceFields(stageName: string): string[] {
    const low: string[] = [];

    if (stageName === 'course-identity') {
      if (!this.courseAccumulated.shortDescription) low.push('shortDescription');
      if (!this.courseAccumulated.detailedDescription) low.push('detailedDescription');
      if (!this.courseAccumulated.learningObjectives?.length) low.push('learningObjectives');
    } else if (stageName === 'course-chapters') {
      const missingChapters = this.chaptersAccumulated.filter(c => !c.chapter.title).length;
      if (missingChapters > 0) low.push('chapter.title');
      const missingLessons = this.chaptersAccumulated.filter(c => !c.lessons?.length).length;
      if (missingLessons > Math.floor(this.inputs.targetChapterCount / 2)) low.push('lessons');
    } else if (stageName === 'course-lessons') {
      const totalLessons = this.chaptersAccumulated.reduce((sum, c) => sum + c.lessons.length, 0);
      const emptyContent = this.chaptersAccumulated.reduce((sum, c) =>
        sum + c.lessons.filter(l => !l.content).length, 0);
      if (totalLessons > 0 && emptyContent > totalLessons / 2) low.push('lesson.content');
    }

    return low;
  }

  private computeOverallConfidence(): number {
    const successCount = this.stageResults.filter(s => s.success && !s.enhanced).length;
    const successRate = successCount / 3;
    return Math.round(successRate * 100);
  }
}