/**
 * Intro Script AI Pipeline
 *
 * 2-stage pipeline:
 *   Stage 1 — Generate all script variants using company context + profile data
 *   Stage 2 — Refine with brand guardrails if available
 *
 * Follows the same pattern as SalesScriptPipeline.
 */

import { generateWithAI } from '../../utils/aiProvider';
import { parseJsonFromAI } from './parseJsonFromAI';
import {
  buildIntroScriptPrompt,
  buildIntroScriptRefinementPrompt,
  IntroScriptInputs,
  PartialIntroScriptAnalysis,
  PromptResult,
} from './introScriptPrompts';

// ============================================
// TYPES
// ============================================

export interface IntroScriptPipelineResult {
  scripts: Record<string, any>[];
  pipelineVersion: string;
  provider: string;
  aiModel: string;
  tokensUsed: number;
  inputTokens: number;
  outputTokens: number;
  processingTimeMs: number;
  latencyMs: number;
  overallConfidence: number;
  finishReason: string | null;
  apiKeyMasked: string | null;
  stageResults: StageResult[];
  errors: string[];
}

export interface StageResult {
  stage: string;
  success: boolean;
  provider?: string;
  aiModel?: string;
  tokensUsed?: number;
  error?: string;
  duration: number;
  enhanced?: boolean;
}

const PIPELINE_VERSION = '1.0';
const CONFIDENCE_THRESHOLD = 60;

// ============================================
// JSON PARSING UTILITIES
// ============================================

function extractFieldsFromRawContent(content: string): Record<string, any> {
  const result: Record<string, any> = {};
  if (!content || typeof content !== 'string') return result;

  const stringPairRegex = /"(\w+)":\s*"((?:[^"\\]|\\.)*)"/g;
  let match;
  while ((match = stringPairRegex.exec(content)) !== null) {
    const [, key, value] = match;
    result[key] = value.replace(/\\"/g, '"').replace(/\\n/g, '\n');
  }

  const arrayPairRegex = /"(\w+)":\s*\[((?:\s*"(?:[^"\\]|\\.)*"\s*,?\s*)+)\]/g;
  while ((match = arrayPairRegex.exec(content)) !== null) {
    const [, key, arrayContent] = match;
    const values = [...arrayContent.matchAll(/"((?:[^"\\]|\\.)*)"/g)].map(m => m[1]);
    if (values.length > 0) {
      result[key] = values;
    }
  }

  return result;
}

function extractScriptArray(parsed: Record<string, any>): any[] {
  if (Array.isArray(parsed)) return parsed;
  if (parsed.scripts && Array.isArray(parsed.scripts)) return parsed.scripts;
  return [parsed];
}

// ============================================
// PIPELINE CLASS
// ============================================

export class IntroScriptPipeline {
  private inputs: IntroScriptInputs;
  private targetCount: number;
  private scriptAccumulated: Record<string, any>[];
  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: IntroScriptInputs, onProgress?: (progress: number, step: string) => void) {
    this.inputs = inputs;
    this.targetCount = Math.min(inputs.targetCount || 10, 10);
    this.scriptAccumulated = Array.from({ length: this.targetCount }, () => ({}));
    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<IntroScriptPipelineResult> {
    // Stage 1: Generate all script variants
    this.onProgress?.(5, 'Generating intro script variants...');
    await this.runStageWithRetry('script-generation', () =>
      this.executeStage(
        buildIntroScriptPrompt(this.inputs),
        'generation',
        0
      )
    );
    this.onProgress?.(50, 'Script variants generated');

    // Stage 2: Refine with brand guardrails (if harmony context available)
    if (this.inputs.harmonyText) {
      this.onProgress?.(55, 'Refining with brand guardrails...');
      await this.runStageWithRetry('brand-refinement', () =>
        this.executeStage(
          buildIntroScriptRefinementPrompt(this.inputs, this.scriptAccumulated),
          'refinement',
          1
        )
      );
      this.onProgress?.(85, 'Brand refinement complete');
    } else {
      this.onProgress?.(85, 'Skipping brand refinement (no harmony context)');
    }

    // Validate key fields
    const validationErrors = this.validateScripts();
    if (validationErrors.length > 0) {
      this.errors.push(`Missing or insufficient content for: ${validationErrors.join(', ')}`);
    }

    const overallConfidence = this.computeOverallConfidence();

    this.onProgress?.(100, 'Complete');

    return {
      scripts: this.scriptAccumulated,
      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(`[IntroScript-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(`[IntroScript-Pipeline] Stage "${stageName}" response received. Provider: ${result.provider}, Content length: ${result.content?.length || 0}`);

    const parsed = parseJsonFromAI(result.content);
    if (!parsed) {
      // Fallback: extract fields from raw content
      const extracted = extractFieldsFromRawContent(result.content);
      if (Object.keys(extracted).length > 0) {
        console.warn(`[IntroScript-Pipeline] Stage "${stageName}": JSON parsing failed, extracted ${Object.keys(extracted).length} fields as single script`);
        this.mergeIntoScript(0, extracted);
        return;
      }
      throw new Error(`AI response could not be parsed as JSON for stage: ${stageName}`);
    }

    const scripts = extractScriptArray(parsed);
    console.log(`[IntroScript-Pipeline] Stage "${stageName}" extracted ${scripts.length} scripts`);

    // Merge each parsed script into its corresponding slot
    for (let i = 0; i < scripts.length && i < this.targetCount; i++) {
      this.mergeIntoScript(i, scripts[i]);
    }

    // If fewer scripts returned than target, fill remaining with differentiated variants
    if (scripts.length < this.targetCount && scripts.length > 0) {
      const variantAngles = [
        'a different angle focusing on personal story and authenticity',
        'an alternative approach emphasising expertise and credibility',
        'a fresh perspective highlighting mission and vision',
        'a results-oriented angle showcasing achievements and impact',
        'a relationship-building approach emphasising connection and trust',
        'a bold angle leading with a surprising fact or contrarian insight',
        'a narrative approach using storytelling and emotional resonance',
        'a concise, punchy approach optimised for short attention spans',
      ];
      for (let i = scripts.length; i < this.targetCount; i++) {
        const base = { ...scripts[i % scripts.length] };
        const variantNum = i - scripts.length + 2;
        const angle = variantAngles[(i - scripts.length) % variantAngles.length];
        base.name = base.name ? `${base.name} (Variant ${variantNum})` : `Intro Script Variant ${i + 1}`;
        if (base.content) base.content = `[Variant ${variantNum} — ${angle}] ${base.content}`;
        this.mergeIntoScript(i, base);
      }
    }
  }

  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,
      });
    }
  }

  // ============================================
  // RESULT MERGING
  // ============================================

  private mergeIntoScript(index: number, parsed: Record<string, any>): void {
    if (!parsed || typeof parsed !== 'object') return;
    for (const [key, value] of Object.entries(parsed)) {
      if (value !== null && value !== undefined) {
        const existing = this.scriptAccumulated[index][key];
        if (existing === undefined || existing === null || existing === '' || (Array.isArray(existing) && existing.length === 0)) {
          this.scriptAccumulated[index][key] = value;
        } else if (Array.isArray(value) && value.length > 0 && Array.isArray(existing)) {
          if (value.length > existing.length) {
            this.scriptAccumulated[index][key] = value;
          }
        }
      }
    }
  }

  // ============================================
  // VALIDATION
  // ============================================

  private validateScripts(): string[] {
    const missingFields: string[] = [];
    const requiredFields = ['name', 'content'] as const;

    for (let i = 0; i < this.scriptAccumulated.length; i++) {
      for (const field of requiredFields) {
        const value = this.scriptAccumulated[i][field];
        if (!value || (typeof value === 'string' && value.trim().length < 10)) {
          missingFields.push(`Script ${i + 1}.${field}`);
        }
      }
    }

    return missingFields;
  }

  // ============================================
  // CONFIDENCE ANALYSIS
  // ============================================

  private computeOverallConfidence(): number {
    const successCount = this.stageResults.filter(s => s.success).length;
    const totalStages = this.inputs.harmonyText ? 2 : 1;
    return Math.round((successCount / totalStages) * 100);
  }
}