/**
 * Brand Asset Guidelines Pipeline
 *
 * Multi-stage pipeline that generates text-based brand guidelines
 * (clear space, minimum size, usage rules, do's and don'ts) using
 * all available brand context.
 *
 * Stage 1: Generate guidelines content based on brand context
 * Stage 2: Refine and validate with brand-specific guardrails
 */

import { buildGuidelinesPrompt, getGuidelineTypeLabel, type GuidelineType, type BrandGuidelinesInputs } from './brandAssetGuidelinesPrompts';
import { generateWithAI } from '../../utils/aiProvider';
import { parseJsonFromAI } from './parseJsonFromAI';

// ============================================
// TYPES
// ============================================

export interface StageResult {
  stage: number;
  raw: string;
  parsed: Record<string, any> | null;
  confidence: number;
  tokensUsed: number;
  inputTokens: number;
  outputTokens: number;
  provider: string;
  model: string;
}

export interface BrandGuidelinesPipelineResult {
  guidelineType: GuidelineType;
  guidelineLabel: string;
  content: Record<string, any>;
  pipelineVersion: string;
  provider: string;
  aiModel: string;
  tokensUsed: number;
  inputTokens: number;
  outputTokens: number;
  processingTimeMs: number;
  overallConfidence: number;
  stageResults: StageResult[];
  errors: string[];
}

// ============================================
// PIPELINE
// ============================================

const PIPELINE_VERSION = '1.0.0';

export class BrandAssetGuidelinesPipeline {
  private inputs: BrandGuidelinesInputs;
  private accumulated: Record<string, any>;
  private stageResults: StageResult[];
  private errors: string[];
  private startTime: number;

  constructor(inputs: BrandGuidelinesInputs) {
    this.inputs = inputs;
    this.accumulated = {};
    this.stageResults = [];
    this.errors = [];
    this.startTime = Date.now();
  }

  async run(): Promise<BrandGuidelinesPipelineResult> {
    // Stage 1: Generate guidelines content
    await this.runStage(1, 'Generate guidelines');

    // Stage 2: Refine with guardrails (if brand guardrails exist)
    const hasGuardrails = this.inputs.brandGuardrails
      || this.inputs.brandConsistencyGuardrails
      || this.inputs.brandForbiddenDesignPatterns?.length
      || this.inputs.brandVoiceDonts?.length
      || this.inputs.brandGuidelinesDosAndDonts;

    if (hasGuardrails && Object.keys(this.accumulated).length > 0) {
      await this.runStage(2, 'Refine with brand guardrails');
    }

    return {
      guidelineType: this.inputs.guidelineType,
      guidelineLabel: getGuidelineTypeLabel(this.inputs.guidelineType),
      content: this.accumulated,
      pipelineVersion: PIPELINE_VERSION,
      provider: this.stageResults[0]?.provider || 'unknown',
      aiModel: this.stageResults[0]?.model || 'unknown',
      tokensUsed: this.stageResults.reduce((sum, s) => sum + s.tokensUsed, 0),
      inputTokens: this.stageResults.reduce((sum, s) => sum + s.inputTokens, 0),
      outputTokens: this.stageResults.reduce((sum, s) => sum + s.outputTokens, 0),
      processingTimeMs: Date.now() - this.startTime,
      overallConfidence: this.stageResults.reduce((sum, s) => sum + s.confidence, 0) / Math.max(this.stageResults.length, 1),
      stageResults: this.stageResults,
      errors: this.errors,
    };
  }

  private async runStage(stage: number, stageName: string): Promise<void> {
    try {
      let systemPrompt: string;
      let userPrompt: string;
      let maxTokens: number;

      if (stage === 1) {
        const prompt = buildGuidelinesPrompt(this.inputs);
        systemPrompt = prompt.systemPrompt;
        userPrompt = prompt.userPrompt;
        maxTokens = prompt.maxTokens;
      } else {
        // Stage 2: Refinement prompt
        systemPrompt = this.buildRefinementSystemPrompt();
        userPrompt = this.buildRefinementUserPrompt();
        maxTokens = 3000;
      }

      const result = await generateWithAI(userPrompt, systemPrompt, maxTokens);
      const parsed = parseJsonFromAI(result.content);

      if (!parsed || typeof parsed !== 'object') {
        this.errors.push(`Stage ${stage}: Failed to parse AI response as JSON`);
        return;
      }

      // Merge into accumulated
      for (const [key, value] of Object.entries(parsed)) {
        if (value !== null && value !== undefined && value !== '') {
          this.accumulated[key] = value;
        }
      }

      const confidence = this.calculateConfidence(parsed);

      this.stageResults.push({
        stage,
        raw: result.content,
        parsed,
        confidence,
        tokensUsed: result.tokenUsage?.totalTokens || 0,
        inputTokens: result.tokenUsage?.inputTokens || 0,
        outputTokens: result.tokenUsage?.outputTokens || 0,
        provider: result.provider || 'unknown',
        model: result.model || 'unknown',
      });
    } catch (error) {
      this.errors.push(`Stage ${stage} (${stageName}): ${(error as Error).message}`);
    }
  }

  private buildRefinementSystemPrompt(): string {
    const label = getGuidelineTypeLabel(this.inputs.guidelineType);

    return `You are a senior brand consultant reviewing and refining ${label} for "${this.inputs.companyName}".

Your task is to take the generated guidelines and ensure they are:
1. Fully consistent with the brand's specific guardrails, forbidden patterns, and consistency rules
2. Reference the brand's ACTUAL colours, fonts, personality, and values — not generic placeholders
3. Professional, actionable, and specific enough for immediate use
4. Free of contradictions or vague advice

IMPORTANT: Output the COMPLETE refined guidelines as valid JSON matching the original schema. Do not omit any fields or sections. Only change content that needs refinement — keep good content unchanged.`;
  }

  private buildRefinementUserPrompt(): string {
    const label = getGuidelineTypeLabel(this.inputs.guidelineType);
    const parts: string[] = [];

    parts.push(`Review and refine the following ${label} for "${this.inputs.companyName}":`);
    parts.push('');
    parts.push(JSON.stringify(this.accumulated, null, 2));
    parts.push('');

    if (this.inputs.brandGuardrails) {
      parts.push(`BRAND GUARDRAILS: ${this.inputs.brandGuardrails}`);
    }
    if (this.inputs.brandConsistencyGuardrails) {
      const g = this.inputs.brandConsistencyGuardrails;
      if (g.cannotChange?.length) parts.push(`NEVER CHANGE: ${g.cannotChange.join('; ')}`);
      if (g.canEvolve?.length) parts.push(`CAN EVOLVE: ${g.canEvolve.join('; ')}`);
      if (g.misuseExamples?.length) parts.push(`MISUSE EXAMPLES: ${g.misuseExamples.join('; ')}`);
    }
    if (this.inputs.brandForbiddenDesignPatterns?.length) {
      parts.push(`FORBIDDEN DESIGN PATTERNS: ${this.inputs.brandForbiddenDesignPatterns.join(', ')}`);
    }
    if (this.inputs.brandVoiceDonts?.length) {
      parts.push(`VOICE NEVER: ${this.inputs.brandVoiceDonts.join('; ')}`);
    }
    if (this.inputs.brandColors?.length) {
      parts.push(`Ensure all colour references use these EXACT brand colours: ${this.inputs.brandColors.join(', ')}`);
    }

    parts.push('');
    parts.push('Refine the guidelines to be more specific, actionable, and aligned with the brand identity. Output the complete JSON.');

    return parts.join('\n');
  }

  private calculateConfidence(parsed: Record<string, any>): number {
    let score = 0;
    let maxScore = 0;

    // Title is present
    maxScore += 10;
    if (parsed.title) score += 10;

    // Summary is present and substantial
    maxScore += 10;
    if (parsed.summary && typeof parsed.summary === 'string' && parsed.summary.length > 20) score += 10;

    // Rules/sections/items are present and non-empty
    maxScore += 30;
    if (Array.isArray(parsed.rules) && parsed.rules.length > 0) score += 30;
    else if (Array.isArray(parsed.sections) && parsed.sections.length > 0) score += 30;
    else if (Array.isArray(parsed.items) && parsed.items.length > 0) score += 30;
    else if (Array.isArray(parsed.digitalMinimums) && parsed.digitalMinimums.length > 0) score += 15;
    else if (Array.isArray(parsed.printMinimums) && parsed.printMinimums.length > 0) score += 15;

    // Content references the brand name
    maxScore += 20;
    const contentStr = JSON.stringify(parsed);
    if (this.inputs.companyName && contentStr.includes(this.inputs.companyName)) score += 20;

    // Content is substantial
    maxScore += 30;
    if (contentStr.length > 500) score += 15;
    if (contentStr.length > 1500) score += 15;

    return maxScore > 0 ? score / maxScore : 0;
  }
}