/**
 * Harmony Context Service
 *
 * Assembles cross-module brand context from Business Profile, Brand Strategy,
 * Visual Identity, Brand Guidelines, ICP, and Persona data.
 *
 * Used by the Social Media OS pipeline and the /ai/generate and
 * /ai/generate-image-raw endpoints to inject brand-aware context into
 * AI generation prompts.
 *
 * All DB calls are wrapped in try/catch — missing data degrades gracefully
 * and never breaks the pipeline.
 */

import { getModels } from '../../models';

// ============================================
// TYPES
// ============================================

export interface HarmonyContext {
  // Identity
  companyName: string;
  companyDescription?: string;

  // From Brand model
  brandVoice?: string;
  brandVoiceDos?: string[];
  brandVoiceDonts?: string[];
  brandPersonalityPrimary?: string[];
  brandPersonalitySecondary?: string[];
  brandArchetype?: string;
  brandPromise?: string;
  brandGuardrails?: string;
  brandForbiddenWords?: string[];
  brandSymbols?: string[];
  brandSignatureExpressions?: string[];

  // Visual identity from Brand model
  brandColors?: string[];
  brandFonts?: { heading?: string; body?: string };
  visualDescription?: string;

  // From ModuleData(brand-strategy)
  brandStrategyVoice?: string;
  brandStrategyPersonality?: string;
  brandStrategyArchetype?: string;
  brandStrategyValues?: string | string[];
  brandStrategyTone?: string;
  brandStrategyPositioning?: string;

  // From ModuleData(visual-identity)
  visualIdentityColorPalette?: any;
  visualIdentityTypography?: any;
  visualIdentityDesignPrinciples?: string | string[];
  visualIdentityMood?: string;
  visualIdentityStyle?: string;

  // From ModuleData(brand-guidelines)
  brandGuidelinesDosAndDonts?: any;
  brandGuidelinesVoice?: any;
  brandGuidelinesDesignRules?: any;

  // From BusinessProfile
  businessMission?: string;
  businessVision?: string;
  businessCoreValues?: string;
  businessUsp?: string;
  businessIndustry?: string;
  businessModel?: string;
  businessPrimaryOffering?: string;
  businessTargetGeography?: string;

  // From ICP (rich)
  icpName?: string;
  icpDescription?: string;
  icpPainPoints?: string[];
  icpIndustry?: string;

  // From Persona (rich)
  personaNames?: string[];
  personaJobTitles?: string[];
  personaPainPoints?: string[];

  // From Founders
  founderNames?: string[];
  founderBios?: string[];
  founderResponsibilityAreas?: string[];

  // Brand SOP 1.7 — extended guardrails and visual direction
  brandVisualDirection?: string;
  brandVisualTheme?: string;
  brandConsistencyGuardrails?: {
    cannotChange?: string[];
    canEvolve?: string[];
    misuseExamples?: string[];
  };
  brandForbiddenDesignPatterns?: string[];
}

// ============================================
// DATA SOURCES CONFIG
// ============================================

export interface DataSourcesConfig {
  businessProfile?: boolean;
  brand?: boolean;
  brandStrategy?: boolean;
  visualIdentity?: boolean;
  brandGuidelines?: boolean;
  icp?: boolean;
  persona?: boolean;
  founders?: boolean;
}

const DEFAULT_DATA_SOURCES: DataSourcesConfig = {
  businessProfile: true,
  brand: true,
  brandStrategy: true,
  visualIdentity: true,
  brandGuidelines: true,
  icp: true,
  persona: true,
  founders: true,
};

// ============================================
// MAIN: buildHarmonyContext
// ============================================

/**
 * Assembles a full HarmonyContext by fetching data from multiple sources:
 * Company, BusinessProfile, Brand model, ModuleData (brand-strategy,
 * visual-identity, brand-guidelines), ICP, and Persona.
 *
 * Brand model data takes precedence over ModuleData for overlapping fields.
 * All DB calls are individually wrapped in try/catch — missing data
 * never breaks the pipeline.
 *
 * @param companyId - The company ID to fetch data for
 * @param dataSources - Optional filter for which data sources to include
 */
export async function buildHarmonyContext(
  companyId: string,
  dataSources?: DataSourcesConfig
): Promise<HarmonyContext> {
  const ctx: HarmonyContext = {
    companyName: '',
    companyDescription: '',
  };

  // Merge with defaults - if dataSources is provided, only include those that are true
  const sources = { ...DEFAULT_DATA_SOURCES, ...dataSources };

  const models = getModels();

  // --- Company ---
  try {
    const { Company } = models;
    const company = await Company.findById(companyId);
    if (company) {
      ctx.companyName = company.name || '';
      ctx.companyDescription = company.description || undefined;
    }
  } catch (e) {
    console.warn('[Harmony] Company fetch failed:', (e as Error).message);
  }

  // --- BusinessProfile ---
  if (sources.businessProfile) {
    try {
      const { BusinessProfile } = models;
      const bp = await BusinessProfile.findOne({ companyId });
      if (bp) {
        if (!ctx.companyDescription && bp.description) ctx.companyDescription = bp.description;
        ctx.businessMission = bp.mission || undefined;
        ctx.businessVision = bp.vision || undefined;
        ctx.businessCoreValues = bp.coreValues || undefined;
        ctx.businessUsp = bp.usp || undefined;
        ctx.businessIndustry = bp.primaryIndustry || undefined;
        ctx.businessModel = bp.businessModel || undefined;
        ctx.businessPrimaryOffering = bp.primaryOffering || undefined;
        ctx.businessTargetGeography = bp.targetGeography || undefined;
      }
    } catch (e) {
      console.warn('[Harmony] BusinessProfile fetch failed:', (e as Error).message);
    }
  }

  // --- Brand model ---
  if (sources.brand) {
    try {
      const { Brand } = models;
      const brand = await Brand.findOne({ companyId });
      if (brand) {
        // Brand model voice takes precedence
        if (brand.voiceDescription) ctx.brandVoice = brand.voiceDescription;
        if (brand.voiceDos?.length) ctx.brandVoiceDos = brand.voiceDos;
        if (brand.voiceDonts?.length) ctx.brandVoiceDonts = brand.voiceDonts;
        if (brand.personalityPrimary?.length) ctx.brandPersonalityPrimary = brand.personalityPrimary;
        if (brand.personalitySecondary?.length) ctx.brandPersonalitySecondary = brand.personalitySecondary;
        if (brand.purposeStatement) ctx.brandPromise = brand.purposeStatement;
        if (brand.guardrailsDescription) ctx.brandGuardrails = brand.guardrailsDescription;
        if (brand.rulesVoiceForbiddenWords?.length) ctx.brandForbiddenWords = brand.rulesVoiceForbiddenWords;
        if (brand.diffBrandSymbols?.length) ctx.brandSymbols = brand.diffBrandSymbols;
        if (brand.diffSignatureExpressions?.length) ctx.brandSignatureExpressions = brand.diffSignatureExpressions;

        // Visual identity from Brand model
        const colors = [brand.primaryColor, brand.secondaryColor, brand.accentColor].filter(Boolean);
        if (colors.length > 0) ctx.brandColors = colors;
        if (brand.headingFont || brand.bodyFont) {
          ctx.brandFonts = { heading: brand.headingFont || undefined, body: brand.bodyFont || undefined };
        }
        if (brand.visualDescription) ctx.visualDescription = brand.visualDescription;

        // Brand SOP 1.7 — extended visual direction and guardrails
        if (brand.visualTheme) ctx.brandVisualTheme = brand.visualTheme;
        if (brand.visualColourPsychology) ctx.brandVisualDirection = brand.visualColourPsychology;
        else if (brand.visualTypography) ctx.brandVisualDirection = brand.visualTypography;
        else if (brand.visualImageryStyle) ctx.brandVisualDirection = brand.visualImageryStyle;

        // Consistency guardrails
        const cannotChange = brand.guardCannotChange;
        const canEvolve = brand.guardCanEvolve;
        const misuseExamples = brand.guardMisuseExamples;
        if (cannotChange?.length || canEvolve?.length || misuseExamples?.length) {
          ctx.brandConsistencyGuardrails = {
            cannotChange: cannotChange || [],
            canEvolve: canEvolve || [],
            misuseExamples: misuseExamples || [],
          };
        }

        // Forbidden design patterns
        if (brand.rulesDesignForbiddenPatterns?.length) {
          ctx.brandForbiddenDesignPatterns = brand.rulesDesignForbiddenPatterns;
        }
      }
    } catch (e) {
      console.warn('[Harmony] Brand model fetch failed:', (e as Error).message);
    }
  }

  // --- ModuleData(brand-strategy) ---
  if (sources.brandStrategy) {
    try {
      const { ModuleData } = models;
      const brandStrategyDoc = await ModuleData.findOne({ moduleId: 'brand-strategy', companyId });
      if (brandStrategyDoc?.data) {
        const d = brandStrategyDoc.data;
        // Brand model takes precedence for overlapping fields (voice, personality)
        if (!ctx.brandVoice && d.brandVoice) ctx.brandStrategyVoice = d.brandVoice;
        if (!ctx.brandPersonalityPrimary && d.brandPersonality) {
          ctx.brandStrategyPersonality = Array.isArray(d.brandPersonality)
            ? d.brandPersonality.join(', ')
            : d.brandPersonality;
        }
        if (d.brandArchetype) ctx.brandStrategyArchetype = d.brandArchetype;
        if (d.brandValues) ctx.brandStrategyValues = d.brandValues;
        if (d.toneGuidelines) ctx.brandStrategyTone = d.toneGuidelines;
        if (d.brandPositioning) ctx.brandStrategyPositioning = d.brandPositioning;
      }
    } catch (e) {
      console.warn('[Harmony] Brand Strategy ModuleData fetch failed:', (e as Error).message);
    }
  }

  // --- ModuleData(visual-identity) ---
  if (sources.visualIdentity) {
    try {
      const { ModuleData } = models;
      const visualIdentityDoc = await ModuleData.findOne({ moduleId: 'visual-identity', companyId });
      if (visualIdentityDoc?.data) {
        const d = visualIdentityDoc.data;
        if (d.colorPalette) ctx.visualIdentityColorPalette = d.colorPalette;
        if (d.typography) ctx.visualIdentityTypography = d.typography;
        if (d.designPrinciples) ctx.visualIdentityDesignPrinciples = d.designPrinciples;
        if (d.moodDescription) ctx.visualIdentityMood = d.moodDescription;
        if (d.visualStyle) ctx.visualIdentityStyle = d.visualStyle;
        // Use visual-identity style description if Brand model doesn't have one
        if (!ctx.visualDescription && d.visualStyle) ctx.visualDescription = d.visualStyle;
        // The Visual Identity module stores colours as FLAT hex fields
        // (primaryColor, secondaryColor, accentColor, …), not a nested
        // colorPalette object. Normalise them into a labelled palette so
        // downstream consumers (e.g. the primary-logo prompt) can apply the
        // brand's exact colours. Only brand colours belong here — background /
        // surface / text are excluded so they aren't rendered as logo colours.
        if (!ctx.visualIdentityColorPalette) {
          const paletteObj: Record<string, string> = {};
          for (const key of ['primaryColor', 'secondaryColor', 'accentColor']) {
            const v = (d as any)[key];
            if (typeof v === 'string' && v.trim()) {
              paletteObj[key.replace('Color', '')] = v.trim();
            }
          }
          if (Object.keys(paletteObj).length) ctx.visualIdentityColorPalette = paletteObj;
        }
      }
    } catch (e) {
      console.warn('[Harmony] Visual Identity ModuleData fetch failed:', (e as Error).message);
    }
  }

  // --- ModuleData(brand-guidelines) ---
  if (sources.brandGuidelines) {
    try {
      const { ModuleData } = models;
      const brandGuidelinesDoc = await ModuleData.findOne({ moduleId: 'brand-guidelines', companyId });
      if (brandGuidelinesDoc?.data) {
        const d = brandGuidelinesDoc.data;
        if (d.dosAndDonts) ctx.brandGuidelinesDosAndDonts = d.dosAndDonts;
        if (d.voiceGuidelines) ctx.brandGuidelinesVoice = d.voiceGuidelines;
        if (d.designRules) ctx.brandGuidelinesDesignRules = d.designRules;
      }
    } catch (e) {
      console.warn('[Harmony] Brand Guidelines ModuleData fetch failed:', (e as Error).message);
    }
  }

  // --- ICP (rich data, not just names) ---
  if (sources.icp) {
    try {
      const { ICP } = models;
      const icp = await ICP.findOne({ companyId, isActive: true }).sort({ createdAt: -1 });
      if (icp) {
        ctx.icpName = icp.name || undefined;
        const icpParts: string[] = [];
        if (icp.name) icpParts.push(icp.name);
        if (icp.industry) icpParts.push(icp.industry);
        if (icp.companySize) icpParts.push(`Company size: ${icp.companySize}`);
        if (icp.description) icpParts.push(icp.description);
        if (icpParts.length > 0) ctx.icpDescription = icpParts.join(' — ');
        if (icp.painPoints?.length) ctx.icpPainPoints = icp.painPoints;
        if (icp.industry) ctx.icpIndustry = icp.industry;
      }
    } catch (e) {
      console.warn('[Harmony] ICP fetch failed:', (e as Error).message);
    }
  }

  // --- Persona (rich data, not just names) ---
  if (sources.persona) {
    try {
      const { Persona } = models;
      const personas = await Persona.find({ companyId }).limit(10);
      if (personas.length > 0) {
        ctx.personaNames = personas.map((p: any) => p.name || 'Persona').filter(Boolean);
        const jobTitles = personas
          .map((p: any) => p.demographicSnapshot?.jobTitle || p.jobTitle || '')
          .filter(Boolean) as string[];
        if (jobTitles.length > 0) ctx.personaJobTitles = jobTitles;
        const allPainPoints = personas
          .flatMap((p: any) => p.painPoints || p.demographicSnapshot?.painPoints || [])
          .filter((p: any) => Boolean(p)) as string[];
        if (allPainPoints.length > 0) {
          ctx.personaPainPoints = [...new Set(allPainPoints)];
        }
      }
    } catch (e) {
      console.warn('[Harmony] Persona fetch failed:', (e as Error).message);
    }
  }

  // --- Competitor ---
  // Not inlined into HarmonyContext to avoid changing every consumer; the
  // dedicated `buildCompetitorContextBlock(companyId)` helper below fetches
  // competitors on demand (used by the Primary Logo prompt builder).

  // --- Founders ---
  if (sources.founders) {
    try {
      const { Founder } = models;
      const founders = await Founder.find({ companyId }).limit(10);
      if (founders.length > 0) {
        ctx.founderNames = founders.map((f: any) => f.name).filter(Boolean);
        ctx.founderBios = founders.map((f: any) => f.bio).filter(Boolean);
        ctx.founderResponsibilityAreas = founders
          .map((f: any) => f.responsibilityArea || f.expertise?.join(', '))
          .filter(Boolean);
      }
    } catch (e) {
      console.warn('[Harmony] Founder fetch failed:', (e as Error).message);
    }
  }

  return ctx;
}

// ============================================
// TEXT CONTEXT BUILDER
// ============================================

/** Max length for long text fields to avoid bloating prompts */
const MAX_TEXT_FIELD_LENGTH = 500;

function truncate(text: string | undefined, maxLen: number = MAX_TEXT_FIELD_LENGTH): string | undefined {
  if (!text) return undefined;
  return text.length > maxLen ? text.slice(0, maxLen) + '…' : text;
}

/**
 * Formats HarmonyContext into a text block suitable for injecting
 * into text generation system prompts. Includes brand voice,
 * personality, promise, guardrails, forbidden words, ICP details,
 * persona details, and business mission/vision/values.
 */
export function buildHarmonyTextContextBlock(ctx: HarmonyContext): string {
  const parts: string[] = [];

  // Brand voice
  const voice = ctx.brandVoice || ctx.brandStrategyVoice;
  if (voice) parts.push(`Brand Voice: ${truncate(voice)}`);

  // Brand personality
  const personality = ctx.brandPersonalityPrimary?.length
    ? ctx.brandPersonalityPrimary
    : ctx.brandStrategyPersonality
      ? [ctx.brandStrategyPersonality]
      : undefined;
  if (personality?.length) parts.push(`Brand Personality: ${personality.join(', ')}`);

  // Brand archetype
  const archetype = ctx.brandArchetype || ctx.brandStrategyArchetype;
  if (archetype) parts.push(`Brand Archetype: ${archetype}`);

  // Brand values
  if (ctx.brandStrategyValues) {
    const values = Array.isArray(ctx.brandStrategyValues)
      ? ctx.brandStrategyValues.join(', ')
      : ctx.brandStrategyValues;
    if (values) parts.push(`Brand Values: ${truncate(values)}`);
  }

  // Brand promise
  if (ctx.brandPromise) parts.push(`Brand Promise: ${truncate(ctx.brandPromise)}`);

  // Brand guardrails
  if (ctx.brandGuardrails) parts.push(`Brand Guardrails: ${truncate(ctx.brandGuardrails)}`);

  // Brand guidelines
  if (ctx.brandGuidelinesDosAndDonts) {
    try {
      const d = typeof ctx.brandGuidelinesDosAndDonts === 'string'
        ? ctx.brandGuidelinesDosAndDonts
        : JSON.stringify(ctx.brandGuidelinesDosAndDonts);
      parts.push(`Brand Guidelines (Dos & Don'ts): ${truncate(d)}`);
    } catch { /* skip */ }
  }
  if (ctx.brandGuidelinesVoice) {
    try {
      const v = typeof ctx.brandGuidelinesVoice === 'string'
        ? ctx.brandGuidelinesVoice
        : JSON.stringify(ctx.brandGuidelinesVoice);
      parts.push(`Voice Guidelines: ${truncate(v)}`);
    } catch { /* skip */ }
  }

  // Forbidden words
  if (ctx.brandForbiddenWords?.length) {
    parts.push(`FORBIDDEN WORDS — Never use these: ${ctx.brandForbiddenWords.join(', ')}`);
  }

  // Voice dos/don'ts
  if (ctx.brandVoiceDos?.length) parts.push(`Voice Always: ${ctx.brandVoiceDos.join('; ')}`);
  if (ctx.brandVoiceDonts?.length) parts.push(`Voice Never: ${ctx.brandVoiceDonts.join('; ')}`);

  // Brand symbols and expressions
  if (ctx.brandSymbols?.length) parts.push(`Brand Symbols: ${ctx.brandSymbols.join(', ')}`);
  if (ctx.brandSignatureExpressions?.length) parts.push(`Signature Expressions: ${ctx.brandSignatureExpressions.join('; ')}`);

  // Brand strategy positioning & tone
  if (ctx.brandStrategyPositioning) parts.push(`Brand Positioning: ${truncate(ctx.brandStrategyPositioning)}`);
  if (ctx.brandStrategyTone) parts.push(`Tone Guidelines: ${truncate(ctx.brandStrategyTone)}`);

  // Business profile enrichment
  if (ctx.businessMission) parts.push(`Mission: ${truncate(ctx.businessMission)}`);
  if (ctx.businessVision) parts.push(`Vision: ${truncate(ctx.businessVision)}`);
  if (ctx.businessCoreValues) parts.push(`Core Values: ${truncate(ctx.businessCoreValues)}`);
  if (ctx.businessUsp) parts.push(`USP: ${truncate(ctx.businessUsp)}`);
  if (ctx.businessPrimaryOffering) parts.push(`Primary Offering: ${truncate(ctx.businessPrimaryOffering)}`);

  // ICP enrichment
  if (ctx.icpDescription) parts.push(`Ideal Customer: ${truncate(ctx.icpDescription)}`);
  if (ctx.icpPainPoints?.length) parts.push(`Customer Pain Points: ${ctx.icpPainPoints.join(', ')}`);

  // Persona enrichment
  if (ctx.personaJobTitles?.length) parts.push(`Target Personas: ${ctx.personaJobTitles.join(', ')}`);
  if (ctx.personaPainPoints?.length) parts.push(`Persona Pain Points: ${ctx.personaPainPoints.join(', ')}`);

  if (parts.length === 0) return '';
  return parts.join('\n');
}

// ============================================
// IMAGE CONTEXT BUILDER
// ============================================

/**
 * Formats HarmonyContext into a concise image context block suitable
 * for prepending to image generation prompts. Focuses on visual identity:
 * colors, fonts, visual style, mood, brand archetype, and business description.
 */
export function buildHarmonyImageContextBlock(ctx: HarmonyContext): string {
  const parts: string[] = [];

  // Brand name for context
  if (ctx.companyName) parts.push(`Brand: ${ctx.companyName}`);

  // Colors — from Brand model (primary, secondary, accent)
  if (ctx.brandColors?.length) {
    parts.push(`Brand Colors: ${ctx.brandColors.join(', ')}`);
  } else if (ctx.visualIdentityColorPalette) {
    // Fallback to visual-identity ModuleData color palette
    try {
      const palette = ctx.visualIdentityColorPalette;
      if (typeof palette === 'object') {
        const colorStr = Object.entries(palette)
          .map(([name, hex]) => `${name}: ${hex}`)
          .join(', ');
        if (colorStr) parts.push(`Brand Colors: ${colorStr}`);
      }
    } catch { /* skip */ }
  }

  // Fonts
  if (ctx.brandFonts) {
    const fontParts: string[] = [];
    if (ctx.brandFonts.heading) fontParts.push(`Headings: ${ctx.brandFonts.heading}`);
    if (ctx.brandFonts.body) fontParts.push(`Body: ${ctx.brandFonts.body}`);
    if (fontParts.length) parts.push(`Typography: ${fontParts.join(', ')}`);
  } else if (ctx.visualIdentityTypography) {
    try {
      const typo = ctx.visualIdentityTypography;
      if (typeof typo === 'object') {
        parts.push(`Typography: ${JSON.stringify(typo)}`);
      }
    } catch { /* skip */ }
  }

  // Visual style / description
  const visualStyle = ctx.visualDescription || ctx.visualIdentityStyle;
  if (visualStyle) parts.push(`Visual Style: ${truncate(visualStyle)}`);

  // Mood
  if (ctx.visualIdentityMood) parts.push(`Mood: ${truncate(ctx.visualIdentityMood)}`);

  // Design principles
  if (ctx.visualIdentityDesignPrinciples) {
    const principles = Array.isArray(ctx.visualIdentityDesignPrinciples)
      ? ctx.visualIdentityDesignPrinciples.join(', ')
      : ctx.visualIdentityDesignPrinciples;
    if (principles) parts.push(`Design Principles: ${truncate(principles as string, 300)}`);
  }

  // Brand archetype for visual tone
  const archetype = ctx.brandArchetype || ctx.brandStrategyArchetype;
  if (archetype) parts.push(`Archetype: ${archetype}`);

  // Brand personality for visual mood
  const personality = ctx.brandPersonalityPrimary?.length
    ? ctx.brandPersonalityPrimary
    : ctx.brandStrategyPersonality
      ? [ctx.brandStrategyPersonality]
      : undefined;
  if (personality?.length) parts.push(`Personality: ${personality.join(', ')}`);

  // Business description for context
  if (ctx.companyDescription) parts.push(`Description: ${truncate(ctx.companyDescription, 200)}`);
  if (ctx.businessIndustry) parts.push(`Industry: ${ctx.businessIndustry}`);

  // Brand visual direction (SOP 1.7 extended)
  if (ctx.brandVisualDirection) parts.push(`Visual Direction: ${truncate(ctx.brandVisualDirection)}`);
  if (ctx.brandVisualTheme) parts.push(`Visual Theme: ${ctx.brandVisualTheme}`);

  // Brand consistency guardrails
  if (ctx.brandConsistencyGuardrails) {
    const g = ctx.brandConsistencyGuardrails;
    const guardParts: string[] = [];
    if (g.cannotChange?.length) guardParts.push(`Never change: ${g.cannotChange.join(', ')}`);
    if (g.canEvolve?.length) guardParts.push(`Can evolve: ${g.canEvolve.join(', ')}`);
    if (g.misuseExamples?.length) guardParts.push(`Misuse examples: ${g.misuseExamples.join('; ')}`);
    if (guardParts.length) parts.push(`Brand Guardrails: ${guardParts.join('. ')}`);
  }

  // Forbidden design patterns
  if (ctx.brandForbiddenDesignPatterns?.length) {
    parts.push(`FORBIDDEN DESIGN PATTERNS — Never use: ${ctx.brandForbiddenDesignPatterns.join(', ')}`);
  }

  // Founder context
  if (ctx.founderNames?.length) parts.push(`Founders: ${ctx.founderNames.join(', ')}`);

  if (parts.length === 0) return '';
  return `Brand Context for Image Generation:\n${parts.join('\n')}`;
}

// ============================================
// COMPETITOR CONTEXT BUILDER
// ============================================

/**
 * Fetches the company's active competitors and formats them into a concise
 * "Competitor Landscape" block for image-generation prompts (Primary Logo).
 *
 * The goal is DIFFERENTIATION, not imitation: the block names each competitor,
 * their positioning/tagline/differentiators, and their strengths/weaknesses so
 * the model can deliberately avoid category visual clichés. Competitor logo URLs
 * are noted textually ("has an existing logo — do not imitate") because the
 * image model cannot fetch URLs.
 *
 * Returns '' when the company has no active competitors (the caller skips empty
 * blocks). All DB access is wrapped in try/catch so missing data never breaks
 * the pipeline.
 */
export async function buildCompetitorContextBlock(companyId: string): Promise<string> {
  if (!companyId) return '';
  try {
    const { Competitor } = getModels();
    const competitors = await Competitor.find({ companyId, isActive: true }).limit(8);
    if (!competitors || competitors.length === 0) return '';

    // De-duplicate by name (case-insensitive). The competitors collection can
    // hold repeated records for the same brand, and feeding the image model the
    // same competitor blob several times starves the actual logo direction and
    // produces off-brand, "weird" results.
    const seenNames = new Set<string>();
    const lines: string[] = [];
    for (const c of competitors) {
      const name = c.name ? String(c.name).trim().toLowerCase() : '';
      if (name && seenNames.has(name)) continue;
      if (name) seenNames.add(name);

      const segs: string[] = [];
      if (c.name) segs.push(String(c.name));
      if (c.tagline) segs.push(`tagline: ${c.tagline}`);
      if (c.valueProposition) segs.push(`positioning: ${c.valueProposition}`);
      if (c.differentiators?.length) segs.push(`differentiators: ${c.differentiators.join(', ')}`);
      if (c.marketPosition) segs.push(`market position: ${c.marketPosition}`);
      if (c.targetAudience) segs.push(`audience: ${c.targetAudience}`);
      if (c.strengths?.length) segs.push(`strengths: ${c.strengths.join(', ')}`);
      if (c.weaknesses?.length) segs.push(`weaknesses: ${c.weaknesses.join(', ')}`);
      if (c.logoUrl) segs.push('has an existing logo — do not imitate');
      if (segs.length > 1) lines.push(`- ${segs.join(' | ')}`);
      else if (segs.length === 1) lines.push(`- ${segs[0]}`);
    }

    if (lines.length === 0) return '';
    return [
      'COMPETITOR LANDSCAPE — differentiate from these; do NOT copy their logos, colors, or marks:',
      ...lines,
      'Our differentiation: avoid the visual clichés shared by the above; stand out while staying appropriate to the category.',
    ].join('\n');
  } catch (e) {
    console.warn('[Harmony] Competitor context block failed:', (e as Error).message);
    return '';
  }
}

// ============================================
// PIPELINE INPUTS BUILDER
// ============================================

/**
 * Maps a HarmonyContext to the additional fields needed by
 * SocialMediaPipelineInputs. Returns an object with only the
 * harmony-enriched fields (callers merge this into existing pipelineInputs).
 */
export function mapHarmonyToPipelineInputs(ctx: HarmonyContext): Record<string, any> {
  const fields: Record<string, any> = {};

  // Brand identity
  if (ctx.brandVoice) fields.brandVoice = ctx.brandVoice;
  else if (ctx.brandStrategyVoice) fields.brandVoice = ctx.brandStrategyVoice;

  if (ctx.brandPersonalityPrimary?.length) fields.brandPersonality = ctx.brandPersonalityPrimary;
  else if (ctx.brandStrategyPersonality) fields.brandPersonality = [ctx.brandStrategyPersonality];

  if (ctx.brandArchetype || ctx.brandStrategyArchetype) {
    fields.brandArchetype = ctx.brandArchetype || ctx.brandStrategyArchetype;
  }

  if (ctx.brandStrategyValues) fields.brandValues = ctx.brandStrategyValues;
  if (ctx.brandPromise) fields.brandPromise = ctx.brandPromise;
  if (ctx.brandGuardrails) fields.brandGuardrails = ctx.brandGuardrails;
  if (ctx.brandForbiddenWords?.length) fields.brandForbiddenWords = ctx.brandForbiddenWords;
  if (ctx.brandVoiceDos?.length) fields.brandVoiceDos = ctx.brandVoiceDos;
  if (ctx.brandVoiceDonts?.length) fields.brandVoiceDonts = ctx.brandVoiceDonts;
  if (ctx.brandSymbols?.length) fields.brandSymbols = ctx.brandSymbols;
  if (ctx.brandSignatureExpressions?.length) fields.brandSignatureExpressions = ctx.brandSignatureExpressions;

  // Visual identity
  if (ctx.brandColors?.length) fields.brandColors = ctx.brandColors;
  if (ctx.brandFonts) fields.brandFonts = ctx.brandFonts;
  if (ctx.visualDescription) fields.visualDescription = ctx.visualDescription;

  // Business profile
  if (ctx.businessMission) fields.businessMission = ctx.businessMission;
  if (ctx.businessVision) fields.businessVision = ctx.businessVision;
  if (ctx.businessCoreValues) fields.businessCoreValues = ctx.businessCoreValues;

  // ICP enrichment
  if (ctx.icpDescription) fields.icpDescription = ctx.icpDescription;
  if (ctx.icpPainPoints?.length) fields.icpPainPoints = ctx.icpPainPoints;

  // Persona enrichment
  if (ctx.personaJobTitles?.length) fields.personaJobTitles = ctx.personaJobTitles;
  if (ctx.personaPainPoints?.length) fields.personaPainPoints = ctx.personaPainPoints;

  // Founder enrichment
  if (ctx.founderNames?.length) fields.founderNames = ctx.founderNames;
  if (ctx.founderBios?.length) fields.founderBios = ctx.founderBios;
  if (ctx.founderResponsibilityAreas?.length) fields.founderResponsibilityAreas = ctx.founderResponsibilityAreas;

  // Brand SOP 1.7 — extended visual direction and guardrails
  if (ctx.brandVisualDirection) fields.brandVisualDirection = ctx.brandVisualDirection;
  if (ctx.brandVisualTheme) fields.brandVisualTheme = ctx.brandVisualTheme;
  if (ctx.brandConsistencyGuardrails) fields.brandConsistencyGuardrails = ctx.brandConsistencyGuardrails;
  if (ctx.brandForbiddenDesignPatterns?.length) fields.brandForbiddenDesignPatterns = ctx.brandForbiddenDesignPatterns;

  return fields;
}