/**
 * Landing Page AI Context Routes
 *
 * API endpoints for Landing Page AI generation.
 * POST /auto-fill — generate landing page data from company context
 * POST /regenerate — regenerate landing page data using existing context
 */

import express, { Request, Response } from 'express';
import mongoose from 'mongoose';
import { body, validationResult } from 'express-validator';
import { authenticate } from '../middleware/auth';
import { requirePermission } from '../middleware/permissions';
import { LandingPagePipeline } from '../services/aiContext/landingPagePipeline';
import { LandingPagePipelineInputs } from '../services/aiContext/landingPagePrompts';
import { aiContextService, computeLandingPageAutoFillMapping } from '../services/aiContext/aiContextService';
import { getModels } from '../models';
import { LandingPageContentOS } from '../models/LandingPageContentOS';
import { createJob, updateJobProgress, completeJob, failJob, getJob } from '../services/aiContext/aiJobManager';
import { generateLandingPageWebsiteCore, updateGeneratedWebsiteStatus } from './landingPageGenerator';
import { generateImageWithOpenAI, generateImageWithZhipuCogView } from './imageGenerations';
import { saveBrandAssetFile, base64ToBuffer } from '../utils/fileStorage';

const router = express.Router();
router.use(authenticate);

router.get(
  '/status/:jobId',
  async (req: Request, res: Response) => {
    const { jobId } = req.params;
    const job = getJob(jobId);

    if (!job) {
      res.status(404).json({ error: 'Job not found' });
      return;
    }

    res.json({
      jobId: job.jobId,
      status: job.status,
      progress: job.progress,
      step: job.step,
      result: job.result,
      error: job.error,
    });
  }
);

// ============================================
// POST /auto-fill
// ============================================

router.post(
  '/auto-fill',
  requirePermission('landing-pages', 'ai-generate'),
  [
    body('companyId').notEmpty().withMessage('Company ID is required'),
  ],
  async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { companyId, linkedData, primaryGoal, pageType, language, customInstructions } = req.body;

    try {
      // Start async generation
      const job = createJob('landing-page', companyId, req.body._moduleId);

      // Return jobId immediately so the frontend can poll
      res.status(202).json({ jobId: job.jobId, status: 'processing' });

      // Run pipeline in background (do NOT await on the response path)
      setImmediate(async () => {
        try {
          // Generate from company data
          const { Company, BusinessProfile, ICP, Product } = getModels();
          const company = await Company.findById(companyId);
          if (!company) {
            failJob(job.jobId, 'Company not found');
            return;
          }

          let businessProfile: any = null;
          try { businessProfile = await BusinessProfile.findOne({ companyId }); } catch {}

          const companyContexts = await aiContextService.getByCompany(companyId, 'company-creation');
          const latestCompanyContext = companyContexts.find((c: any) => {
            const a = c.analysis?.toObject?.() || c.analysis || {};
            return c.status === 'approved' || a.industryType || a.businessModel || a.businessSummary;
          });

          let icpData: any = null;
          try { icpData = await ICP.findOne({ companyId, isActive: true }).sort({ createdAt: -1 }); } catch {}

          // Load brand strategy context for enrichment
          let brandStrategyData: any = null;
          try {
            const { ModuleData } = getModels();
            const brandStrategyDoc = await ModuleData.findOne({ moduleId: 'brand-strategy', companyId });
            if (brandStrategyDoc?.data) {
              brandStrategyData = brandStrategyDoc.data;
            }
          } catch {}

          // Load product context — respect linkedData.productIds if provided
          let productNames: string[] | undefined;
          let productDescriptions: string[] | undefined;
          try {
            const productFilter = linkedData?.productIds?.length
              ? { companyId, _id: { $in: linkedData.productIds } }
              : { companyId };
            const products = await Product.find(productFilter).limit(10);
            if (products.length > 0) {
              productNames = products.map((p: any) => p.name);
              productDescriptions = products.map((p: any) => p.description || p.shortDescription || '');
            }
          } catch {}

          // Load ICP — respect linkedData.icpIds if provided
          if (linkedData?.icpIds?.length) {
            try {
              icpData = await ICP.findOne({ companyId, _id: { $in: linkedData.icpIds } });
            } catch {}
          }

          // Load brand strategy — respect linkedData.brandStrategyId
          if (linkedData?.brandStrategyId) {
            try {
              const { ModuleData } = getModels();
              const brandStrategyDoc = await ModuleData.findOne({ moduleId: 'brand-strategy', companyId });
              if (brandStrategyDoc?.data) {
                brandStrategyData = brandStrategyDoc.data;
              }
            } catch {}
          }

          // Load visual identity — respect linkedData.visualIdentityId
          let visualIdentityData: any = null;
          if (linkedData?.visualIdentityId) {
            try {
              const { ModuleData } = getModels();
              const visualIdentityDoc = await ModuleData.findOne({ moduleId: 'visual-identity', companyId });
              if (visualIdentityDoc?.data) {
                visualIdentityData = visualIdentityDoc.data;
              }
            } catch {}
          }

          // Load competitors — respect linkedData.competitorIds
          let competitorNames: string[] | undefined;
          let competitorWeaknesses: string[][] | undefined;
          try {
            const { Competitor } = getModels();
            if (Competitor) {
              const compFilter = linkedData?.competitorIds?.length
                ? { companyId, _id: { $in: linkedData.competitorIds } }
                : { companyId };
              const competitors = await Competitor.find(compFilter).limit(10);
              if (competitors.length > 0) {
                competitorNames = competitors.map((c: any) => c.name);
                competitorWeaknesses = competitors.map((c: any) => c.weaknesses || []);
              }
            }
          } catch {}

          // Load personas — respect linkedData.personaIds
          let personaNames: string[] | undefined;
          let personaGoals: string[][] | undefined;
          let personaPainPoints: string[][] | undefined;
          try {
            const { Persona } = getModels();
            if (Persona) {
              const personaFilter = linkedData?.personaIds?.length
                ? { companyId, _id: { $in: linkedData.personaIds } }
                : { companyId };
              const personaDocs = await Persona.find(personaFilter).limit(10);
              if (personaDocs.length > 0) {
                personaNames = personaDocs.map((p: any) => p.name);
                personaGoals = personaDocs.map((p: any) => p.goals || []);
                personaPainPoints = personaDocs.map((p: any) => p.painPoints || []);
              }
            }
          } catch {}

          // Load FAQs — respect linkedData.faqIds
          let faqQuestions: string[] | undefined;
          let faqAnswers: string[] | undefined;
          try {
            const { FAQ } = getModels();
            if (FAQ) {
              const faqFilter = linkedData?.faqIds?.length
                ? { companyId, _id: { $in: linkedData.faqIds } }
                : { companyId };
              const faqDocs = await FAQ.find(faqFilter).limit(20);
              if (faqDocs.length > 0) {
                faqQuestions = faqDocs.map((f: any) => f.question || f.title);
                faqAnswers = faqDocs.map((f: any) => f.answer || '');
              }
            }
          } catch {}

          // Load testimonials — respect linkedData.testimonialIds
          let testimonialQuotes: { author: string; quote: string; company?: string }[] | undefined;
          try {
            const { Testimonial } = getModels();
            if (Testimonial) {
              const tFilter = linkedData?.testimonialIds?.length
                ? { companyId, _id: { $in: linkedData.testimonialIds } }
                : { companyId };
              const tDocs = await Testimonial.find(tFilter).limit(10);
              if (tDocs.length > 0) {
                testimonialQuotes = tDocs.map((t: any) => ({
                  author: t.customerName || t.name || t.author || 'Unknown',
                  quote: t.shortQuote || t.fullTestimonial || '',
                  company: t.customerCompany || undefined,
                }));
              }
            }
          } catch {}

          // Load case studies — respect linkedData.caseStudyIds
          let caseStudyTitles: string[] | undefined;
          let caseStudyResults: string[] | undefined;
          try {
            const { CaseStudy } = getModels();
            if (CaseStudy) {
              const csFilter = linkedData?.caseStudyIds?.length
                ? { companyId, _id: { $in: linkedData.caseStudyIds } }
                : { companyId };
              const csDocs = await CaseStudy.find(csFilter).limit(10);
              if (csDocs.length > 0) {
                caseStudyTitles = csDocs.map((cs: any) => cs.title || cs.clientName || 'Untitled');
                caseStudyResults = csDocs.map((cs: any) => cs.results || '');
              }
            }
          } catch {}

          // Load product categories
          let productCategoryNames: string[] | undefined;
          try {
            const { ProductCategory } = getModels();
            if (ProductCategory) {
              const pcFilter = linkedData?.productCategoryIds?.length
                ? { companyId, _id: { $in: linkedData.productCategoryIds } }
                : { companyId };
              const pcDocs = await ProductCategory.find(pcFilter).limit(20);
              if (pcDocs.length > 0) {
                productCategoryNames = pcDocs.map((pc: any) => pc.name);
              }
            }
          } catch {}

          // Load founders — respect linkedData.founderIds
          let founderNames: string[] | undefined;
          let founderTitles: string[] | undefined;
          let founderBios: string[] | undefined;
          try {
            const { Founder } = getModels();
            if (Founder) {
              const founderFilter = linkedData?.founderIds?.length
                ? { companyId, _id: { $in: linkedData.founderIds } }
                : { companyId };
              const founderDocs = await Founder.find(founderFilter).limit(10);
              if (founderDocs.length > 0) {
                founderNames = founderDocs.map((f: any) => f.name || '');
                founderTitles = founderDocs.map((f: any) => f.title || f.role || '');
                founderBios = founderDocs.map((f: any) => (f.bio || '').substring(0, 200));
              }
            }
          } catch {}

          // Load books — respect linkedData.bookIds
          let bookTitles: string[] | undefined;
          let bookAuthors: string[] | undefined;
          let bookGenres: string[] | undefined;
          let bookDescriptions: string[] | undefined;
          try {
            const { ModuleData } = getModels();
            if (ModuleData && linkedData?.bookIds?.length) {
              const bookDocs = await ModuleData.find({ moduleId: 'books', companyId, _id: { $in: linkedData.bookIds } }).limit(10);
              if (bookDocs.length > 0) {
                bookTitles = bookDocs.map((b: any) => b.data?.title || b.data?.name || '');
                bookAuthors = bookDocs.map((b: any) => b.data?.author || '');
                bookGenres = bookDocs.map((b: any) => b.data?.genre || b.data?.bookGenre || '');
                bookDescriptions = bookDocs.map((b: any) => (b.data?.description || '').substring(0, 150));
              }
            }
          } catch {}

          // Load courses — respect linkedData.courseIds
          let courseTitles: string[] | undefined;
          let courseCategories: string[] | undefined;
          let courseLevels: string[] | undefined;
          try {
            const { ModuleData } = getModels();
            if (ModuleData && linkedData?.courseIds?.length) {
              const courseDocs = await ModuleData.find({ moduleId: 'courses', companyId, _id: { $in: linkedData.courseIds } }).limit(10);
              if (courseDocs.length > 0) {
                courseTitles = courseDocs.map((c: any) => c.data?.title || c.data?.name || '');
                courseCategories = courseDocs.map((c: any) => c.data?.category || c.data?.courseCategory || '');
                courseLevels = courseDocs.map((c: any) => c.data?.level || c.data?.difficultyLevel || '');
              }
            }
          } catch {}

          // Load events — respect linkedData.eventIds
          let eventTitles: string[] | undefined;
          let eventTypes: string[] | undefined;
          let eventDates: string[] | undefined;
          let eventLocations: string[] | undefined;
          try {
            const { ModuleData } = getModels();
            if (ModuleData && linkedData?.eventIds?.length) {
              const eventDocs = await ModuleData.find({ moduleId: 'events', companyId, _id: { $in: linkedData.eventIds } }).limit(10);
              if (eventDocs.length > 0) {
                eventTitles = eventDocs.map((e: any) => e.data?.title || e.data?.name || '');
                eventTypes = eventDocs.map((e: any) => e.data?.eventType || e.data?.type || '');
                eventDates = eventDocs.map((e: any) => e.data?.startDate || e.data?.eventDate || '');
                eventLocations = eventDocs.map((e: any) => e.data?.location || e.data?.venue || '');
              }
            }
          } catch {}

          // Load sales collateral — respect linkedData.salesCollateralIds
          let salesCollateralNames: string[] | undefined;
          let salesCollateralTypes: string[] | undefined;
          try {
            const { ModuleData } = getModels();
            if (ModuleData) {
              const scFilter = linkedData?.salesCollateralIds?.length
                ? { moduleId: 'sales-collateral', companyId, _id: { $in: linkedData.salesCollateralIds } }
                : { moduleId: 'sales-collateral', companyId };
              const scDocs = await ModuleData.find(scFilter).limit(10);
              if (scDocs.length > 0) {
                salesCollateralNames = scDocs.map((s: any) => s.data?.name || s.data?.title || '');
                salesCollateralTypes = scDocs.map((s: any) => s.data?.type || s.data?.scriptType || 'document');
              }
            }
          } catch {}

          // Load brand assets — respect linkedData.brandAssetIds
          let brandAssetNames: string[] | undefined;
          let brandAssetTypes: string[] | undefined;
          try {
            const { BrandAsset } = getModels();
            if (BrandAsset) {
              const baFilter = linkedData?.brandAssetIds?.length
                ? { companyId, _id: { $in: linkedData.brandAssetIds } }
                : { companyId };
              const baDocs = await BrandAsset.find(baFilter).limit(20);
              if (baDocs.length > 0) {
                brandAssetNames = baDocs.map((a: any) => a.name || '');
                brandAssetTypes = baDocs.map((a: any) => a.type || a.assetType || '');
              }
            }
          } catch {}

          // Build pipeline inputs
          const pipelineInputs: LandingPagePipelineInputs = {
            // The user's own brief from the "Generate with AI" popup or the AI Chat
            // generation flow. Stated as the highest-priority instruction in the
            // prompt, so it wins over the derived company context.
            customInstructions:
              typeof customInstructions === 'string' && customInstructions.trim()
                ? customInstructions.trim()
                : undefined,
            companyName: company.name,
            companyDescription: company.description || businessProfile?.description || undefined,
            companyIndustry: businessProfile?.primaryIndustry || undefined,
            companyBusinessModel: businessProfile?.businessModel || undefined,
            companyTargetAudience: undefined,
            companyPrimaryOffering: undefined,
            companyUsps: undefined,
            companyTargetGeography: businessProfile?.targetGeography || businessProfile?.targetMarket || undefined,
            companyCountry: businessProfile?.country || businessProfile?.headquartersCountry || undefined,
            existingLandingPageGoal: primaryGoal || undefined,
            existingLandingPageType: pageType || undefined,
            language: language || undefined,
          };

          if (latestCompanyContext) {
            const analysis = (latestCompanyContext as any).analysis?.toObject?.() || (latestCompanyContext as any).analysis || {};
            if (analysis.industryType && !pipelineInputs.companyIndustry) pipelineInputs.companyIndustry = analysis.industryType;
            if (analysis.businessModel && !pipelineInputs.companyBusinessModel) pipelineInputs.companyBusinessModel = analysis.businessModel;
            if (analysis.targetAudience?.primary) pipelineInputs.companyTargetAudience = analysis.targetAudience.primary;
            if (analysis.primaryOffering) pipelineInputs.companyPrimaryOffering = analysis.primaryOffering;
            if (analysis.uspSuggestions?.length) pipelineInputs.companyUsps = analysis.uspSuggestions;
            if (analysis.targetGeography && !pipelineInputs.companyTargetGeography) pipelineInputs.companyTargetGeography = analysis.targetGeography;
            if (analysis.country && !pipelineInputs.companyCountry) pipelineInputs.companyCountry = analysis.country;
          }

          if (icpData) {
            pipelineInputs.icpName = icpData.name || undefined;
            pipelineInputs.icpIndustry = icpData.industry || undefined;
            pipelineInputs.icpCompanySize = icpData.companySize || undefined;
            pipelineInputs.icpPainPoints = icpData.painPoints || undefined;
            pipelineInputs.icpBusinessGoals = icpData.businessGoals || undefined;
          }

          if (brandStrategyData) {
            pipelineInputs.brandArchetype = brandStrategyData.brandArchetype || undefined;
            pipelineInputs.brandPersonality = brandStrategyData.brandPersonality || undefined;
            pipelineInputs.brandValues = brandStrategyData.brandValues || undefined;
            pipelineInputs.brandPositioning = brandStrategyData.brandPositioning || undefined;
            pipelineInputs.brandVoice = brandStrategyData.brandVoice || undefined;
            pipelineInputs.primaryColor = brandStrategyData.primaryColor || undefined;
          }

          if (productNames?.length) {
            pipelineInputs.productNames = productNames;
            pipelineInputs.productDescriptions = productDescriptions;
          }

          // Data source enriched context
          if (personaNames?.length) {
            pipelineInputs.personaNames = personaNames;
            pipelineInputs.personaGoals = personaGoals;
            pipelineInputs.personaPainPoints = personaPainPoints;
          }
          if (competitorNames?.length) {
            pipelineInputs.competitorNames = competitorNames;
            pipelineInputs.competitorWeaknesses = competitorWeaknesses;
          }
          if (faqQuestions?.length) {
            pipelineInputs.faqQuestions = faqQuestions;
            pipelineInputs.faqAnswers = faqAnswers;
          }
          if (testimonialQuotes?.length) {
            pipelineInputs.testimonialQuotes = testimonialQuotes;
          }
          if (caseStudyTitles?.length) {
            pipelineInputs.caseStudyTitles = caseStudyTitles;
            pipelineInputs.caseStudyResults = caseStudyResults;
          }
          if (productCategoryNames?.length) {
            pipelineInputs.productCategoryNames = productCategoryNames;
          }
          if (visualIdentityData) {
            pipelineInputs.visualIdentityData = {
              primaryColor: visualIdentityData.primaryColor || undefined,
              secondaryColor: visualIdentityData.secondaryColor || undefined,
              accentColor: visualIdentityData.accentColor || undefined,
              backgroundColor: visualIdentityData.backgroundColor || undefined,
              headingFont: visualIdentityData.headingFont || undefined,
              bodyFont: visualIdentityData.bodyFont || undefined,
            };
          }
          if (founderNames?.length) {
            pipelineInputs.founderNames = founderNames;
            pipelineInputs.founderTitles = founderTitles;
            pipelineInputs.founderBios = founderBios;
          }
          if (bookTitles?.length) {
            pipelineInputs.bookTitles = bookTitles;
            pipelineInputs.bookAuthors = bookAuthors;
            pipelineInputs.bookGenres = bookGenres;
            pipelineInputs.bookDescriptions = bookDescriptions;
          }
          if (courseTitles?.length) {
            pipelineInputs.courseTitles = courseTitles;
            pipelineInputs.courseCategories = courseCategories;
            pipelineInputs.courseLevels = courseLevels;
          }
          if (eventTitles?.length) {
            pipelineInputs.eventTitles = eventTitles;
            pipelineInputs.eventTypes = eventTypes;
            pipelineInputs.eventDates = eventDates;
            pipelineInputs.eventLocations = eventLocations;
          }
          if (salesCollateralNames?.length) {
            pipelineInputs.salesCollateralNames = salesCollateralNames;
            pipelineInputs.salesCollateralTypes = salesCollateralTypes;
          }
          if (brandAssetNames?.length) {
            pipelineInputs.brandAssetNames = brandAssetNames;
            pipelineInputs.brandAssetTypes = brandAssetTypes;
          }

          updateJobProgress(job.jobId, 10, 'Preparing context...');
          const pipeline = new LandingPagePipeline(pipelineInputs, (progress, step) => updateJobProgress(job.jobId, progress, step));
          const result = await pipeline.run();

          const context = await aiContextService.create({
            companyId,
            moduleSource: 'landing-page',
            analysisType: 'full-analysis',
            inputs: { companyName: company.name, description: pipelineInputs.companyDescription },
            analysis: result.analysis,
            metadata: {
              pipelineVersion: result.pipelineVersion,
              provider: result.provider,
              model: result.aiModel,
              tokensUsed: result.tokensUsed,
              inputTokens: result.inputTokens,
              outputTokens: result.outputTokens,
              processingTimeMs: result.processingTimeMs,
              latencyMs: result.latencyMs,
              overallConfidence: result.overallConfidence,
              fieldConfidences: result.fieldConfidences,
              finishReason: result.finishReason,
              apiKeyMasked: result.apiKeyMasked,
            },
          });

          await aiContextService.updateStatus(context.id, 'approved');

          const autoFillData = computeLandingPageAutoFillMapping(result.analysis);
          completeJob(job.jobId, autoFillData, 'generated');

          console.log(`[LandingPage-AutoFill] Job ${job.jobId} completed. Source: generated`);
        } catch (err: any) {
          console.error(`[LandingPage-AutoFill] Job ${job.jobId} failed:`, err.message);
          failJob(job.jobId, err.message || 'AI generation failed');
        }
      });
    } catch (error: any) {
      console.error('[LandingPage-AutoFill] Error:', error.message);
      res.status(500).json({ error: 'Failed to generate landing page data', details: error.message });
    }
  }
);

// ============================================
// POST /regenerate
// ============================================

router.post(
  '/regenerate',
  requirePermission('landing-pages', 'ai-generate'),
  [
    body('companyId').notEmpty().withMessage('Company ID is required'),
  ],
  async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { companyId, existingLandingPageData, linkedData, language } = req.body;

    // Create job and return immediately
    const job = createJob('landing-page', companyId, req.body._moduleId);
    res.status(202).json({ jobId: job.jobId, status: 'processing' });

    // Run pipeline in background
    setImmediate(async () => {
      try {
        const { Company, BusinessProfile, ICP, Product } = getModels();
        const company = await Company.findById(companyId);
        if (!company) {
          failJob(job.jobId, 'Company not found');
          return;
        }

        let businessProfile: any = null;
        try { businessProfile = await BusinessProfile.findOne({ companyId }); } catch {}

        const companyContexts = await aiContextService.getByCompany(companyId, 'company-creation');
        const latestCompanyContext = companyContexts.find((c: any) => {
          const a = c.analysis?.toObject?.() || c.analysis || {};
          return c.status === 'approved' || a.industryType || a.businessModel || a.businessSummary;
        });

        let icpData: any = null;
        try { icpData = await ICP.findOne({ companyId, isActive: true }).sort({ createdAt: -1 }); } catch {}

        // Load brand strategy context for enrichment
        let brandStrategyData: any = null;
        try {
          const { ModuleData } = getModels();
          const brandStrategyDoc = await ModuleData.findOne({ moduleId: 'brand-strategy', companyId });
          if (brandStrategyDoc?.data) {
            brandStrategyData = brandStrategyDoc.data;
          }
        } catch {}

        // Load product context — respect linkedData.productIds if provided
        let productNames: string[] | undefined;
        let productDescriptions: string[] | undefined;
        try {
          const productFilter = linkedData?.productIds?.length
            ? { companyId, _id: { $in: linkedData.productIds } }
            : { companyId };
          const products = await Product.find(productFilter).limit(10);
          if (products.length > 0) {
            productNames = products.map((p: any) => p.name);
            productDescriptions = products.map((p: any) => p.description || p.shortDescription || '');
          }
        } catch {}

        // Load ICP — respect linkedData.icpIds if provided
        if (linkedData?.icpIds?.length) {
          try {
            icpData = await ICP.findOne({ companyId, _id: { $in: linkedData.icpIds } });
          } catch {}
        }

        // Load brand strategy — respect linkedData.brandStrategyId
        if (linkedData?.brandStrategyId) {
          try {
            const { ModuleData } = getModels();
            const brandStrategyDoc = await ModuleData.findOne({ moduleId: 'brand-strategy', companyId });
            if (brandStrategyDoc?.data) {
              brandStrategyData = brandStrategyDoc.data;
            }
          } catch {}
        }

        // Load visual identity — respect linkedData.visualIdentityId
        let visualIdentityData: any = null;
        if (linkedData?.visualIdentityId) {
          try {
            const { ModuleData } = getModels();
            const visualIdentityDoc = await ModuleData.findOne({ moduleId: 'visual-identity', companyId });
            if (visualIdentityDoc?.data) {
              visualIdentityData = visualIdentityDoc.data;
            }
          } catch {}
        }

        // Load competitors — respect linkedData.competitorIds
        let competitorNames: string[] | undefined;
        let competitorWeaknesses: string[][] | undefined;
        try {
          const { Competitor } = getModels();
          if (Competitor) {
            const compFilter = linkedData?.competitorIds?.length
              ? { companyId, _id: { $in: linkedData.competitorIds } }
              : { companyId };
            const competitors = await Competitor.find(compFilter).limit(10);
            if (competitors.length > 0) {
              competitorNames = competitors.map((c: any) => c.name);
              competitorWeaknesses = competitors.map((c: any) => c.weaknesses || []);
            }
          }
        } catch {}

        // Load personas — respect linkedData.personaIds
        let personaNames: string[] | undefined;
        let personaGoals: string[][] | undefined;
        let personaPainPoints: string[][] | undefined;
        try {
          const { Persona } = getModels();
          if (Persona) {
            const personaFilter = linkedData?.personaIds?.length
              ? { companyId, _id: { $in: linkedData.personaIds } }
              : { companyId };
            const personaDocs = await Persona.find(personaFilter).limit(10);
            if (personaDocs.length > 0) {
              personaNames = personaDocs.map((p: any) => p.name);
              personaGoals = personaDocs.map((p: any) => p.goals || []);
              personaPainPoints = personaDocs.map((p: any) => p.painPoints || []);
            }
          }
        } catch {}

        // Load FAQs — respect linkedData.faqIds
        let faqQuestions: string[] | undefined;
        let faqAnswers: string[] | undefined;
        try {
          const { FAQ } = getModels();
          if (FAQ) {
            const faqFilter = linkedData?.faqIds?.length
              ? { companyId, _id: { $in: linkedData.faqIds } }
              : { companyId };
            const faqDocs = await FAQ.find(faqFilter).limit(20);
            if (faqDocs.length > 0) {
              faqQuestions = faqDocs.map((f: any) => f.question || f.title);
              faqAnswers = faqDocs.map((f: any) => f.answer || '');
            }
          }
        } catch {}

        // Load testimonials — respect linkedData.testimonialIds
        let testimonialQuotes: { author: string; quote: string; company?: string }[] | undefined;
        try {
          const { Testimonial } = getModels();
          if (Testimonial) {
            const tFilter = linkedData?.testimonialIds?.length
              ? { companyId, _id: { $in: linkedData.testimonialIds } }
              : { companyId };
            const tDocs = await Testimonial.find(tFilter).limit(10);
            if (tDocs.length > 0) {
              testimonialQuotes = tDocs.map((t: any) => ({
                author: t.customerName || t.name || t.author || 'Unknown',
                quote: t.shortQuote || t.fullTestimonial || '',
                company: t.customerCompany || undefined,
              }));
            }
          }
        } catch {}

        // Load case studies — respect linkedData.caseStudyIds
        let caseStudyTitles: string[] | undefined;
        let caseStudyResults: string[] | undefined;
        try {
          const { CaseStudy } = getModels();
          if (CaseStudy) {
            const csFilter = linkedData?.caseStudyIds?.length
              ? { companyId, _id: { $in: linkedData.caseStudyIds } }
              : { companyId };
            const csDocs = await CaseStudy.find(csFilter).limit(10);
            if (csDocs.length > 0) {
              caseStudyTitles = csDocs.map((cs: any) => cs.title || cs.clientName || 'Untitled');
              caseStudyResults = csDocs.map((cs: any) => cs.results || '');
            }
          }
        } catch {}

        // Load product categories
        let productCategoryNames: string[] | undefined;
        try {
          const { ProductCategory } = getModels();
          if (ProductCategory) {
            const pcFilter = linkedData?.productCategoryIds?.length
              ? { companyId, _id: { $in: linkedData.productCategoryIds } }
              : { companyId };
            const pcDocs = await ProductCategory.find(pcFilter).limit(20);
            if (pcDocs.length > 0) {
              productCategoryNames = pcDocs.map((pc: any) => pc.name);
            }
          }
        } catch {}

        // Load founders — respect linkedData.founderIds
        let founderNames: string[] | undefined;
        let founderTitles: string[] | undefined;
        let founderBios: string[] | undefined;
        try {
          const { Founder } = getModels();
          if (Founder) {
            const founderFilter = linkedData?.founderIds?.length
              ? { companyId, _id: { $in: linkedData.founderIds } }
              : { companyId };
            const founderDocs = await Founder.find(founderFilter).limit(10);
            if (founderDocs.length > 0) {
              founderNames = founderDocs.map((f: any) => f.name || '');
              founderTitles = founderDocs.map((f: any) => f.title || f.role || '');
              founderBios = founderDocs.map((f: any) => (f.bio || '').substring(0, 200));
            }
          }
        } catch {}

        // Load books — respect linkedData.bookIds
        let bookTitles: string[] | undefined;
        let bookAuthors: string[] | undefined;
        let bookGenres: string[] | undefined;
        let bookDescriptions: string[] | undefined;
        try {
          const { ModuleData } = getModels();
          if (ModuleData && linkedData?.bookIds?.length) {
            const bookDocs = await ModuleData.find({ moduleId: 'books', companyId, _id: { $in: linkedData.bookIds } }).limit(10);
            if (bookDocs.length > 0) {
              bookTitles = bookDocs.map((b: any) => b.data?.title || b.data?.name || '');
              bookAuthors = bookDocs.map((b: any) => b.data?.author || '');
              bookGenres = bookDocs.map((b: any) => b.data?.genre || b.data?.bookGenre || '');
              bookDescriptions = bookDocs.map((b: any) => (b.data?.description || '').substring(0, 150));
            }
          }
        } catch {}

        // Load courses — respect linkedData.courseIds
        let courseTitles: string[] | undefined;
        let courseCategories: string[] | undefined;
        let courseLevels: string[] | undefined;
        try {
          const { ModuleData } = getModels();
          if (ModuleData && linkedData?.courseIds?.length) {
            const courseDocs = await ModuleData.find({ moduleId: 'courses', companyId, _id: { $in: linkedData.courseIds } }).limit(10);
            if (courseDocs.length > 0) {
              courseTitles = courseDocs.map((c: any) => c.data?.title || c.data?.name || '');
              courseCategories = courseDocs.map((c: any) => c.data?.category || c.data?.courseCategory || '');
              courseLevels = courseDocs.map((c: any) => c.data?.level || c.data?.difficultyLevel || '');
            }
          }
        } catch {}

        // Load events — respect linkedData.eventIds
        let eventTitles: string[] | undefined;
        let eventTypes: string[] | undefined;
        let eventDates: string[] | undefined;
        let eventLocations: string[] | undefined;
        try {
          const { ModuleData } = getModels();
          if (ModuleData && linkedData?.eventIds?.length) {
            const eventDocs = await ModuleData.find({ moduleId: 'events', companyId, _id: { $in: linkedData.eventIds } }).limit(10);
            if (eventDocs.length > 0) {
              eventTitles = eventDocs.map((e: any) => e.data?.title || e.data?.name || '');
              eventTypes = eventDocs.map((e: any) => e.data?.eventType || e.data?.type || '');
              eventDates = eventDocs.map((e: any) => e.data?.startDate || e.data?.eventDate || '');
              eventLocations = eventDocs.map((e: any) => e.data?.location || e.data?.venue || '');
            }
          }
        } catch {}

        // Load sales collateral — respect linkedData.salesCollateralIds
        let salesCollateralNames: string[] | undefined;
        let salesCollateralTypes: string[] | undefined;
        try {
          const { ModuleData } = getModels();
          if (ModuleData) {
            const scFilter = linkedData?.salesCollateralIds?.length
              ? { moduleId: 'sales-collateral', companyId, _id: { $in: linkedData.salesCollateralIds } }
              : { moduleId: 'sales-collateral', companyId };
            const scDocs = await ModuleData.find(scFilter).limit(10);
            if (scDocs.length > 0) {
              salesCollateralNames = scDocs.map((s: any) => s.data?.name || s.data?.title || '');
              salesCollateralTypes = scDocs.map((s: any) => s.data?.type || s.data?.scriptType || 'document');
            }
          }
        } catch {}

        // Load brand assets — respect linkedData.brandAssetIds
        let brandAssetNames: string[] | undefined;
        let brandAssetTypes: string[] | undefined;
        try {
          const { BrandAsset } = getModels();
          if (BrandAsset) {
            const baFilter = linkedData?.brandAssetIds?.length
              ? { companyId, _id: { $in: linkedData.brandAssetIds } }
              : { companyId };
            const baDocs = await BrandAsset.find(baFilter).limit(20);
            if (baDocs.length > 0) {
              brandAssetNames = baDocs.map((a: any) => a.name || '');
              brandAssetTypes = baDocs.map((a: any) => a.type || a.assetType || '');
            }
          }
        } catch {}

        // Build pipeline inputs with existing landing page data for regeneration
        const pipelineInputs: LandingPagePipelineInputs = {
          companyName: company.name,
          companyDescription: company.description || businessProfile?.description || undefined,
          companyIndustry: businessProfile?.primaryIndustry || undefined,
          companyBusinessModel: businessProfile?.businessModel || undefined,
          companyTargetAudience: undefined,
          companyPrimaryOffering: undefined,
          companyUsps: undefined,
          // Existing landing page identity for regeneration
          existingLandingPageName: existingLandingPageData?.name || undefined,
          existingLandingPageType: existingLandingPageData?.pageType || undefined,
          existingLandingPageGoal: existingLandingPageData?.primaryGoal || undefined,
          language: language || undefined,
        };

        if (latestCompanyContext) {
          const analysis = (latestCompanyContext as any).analysis?.toObject?.() || (latestCompanyContext as any).analysis || {};
          if (analysis.industryType && !pipelineInputs.companyIndustry) pipelineInputs.companyIndustry = analysis.industryType;
          if (analysis.businessModel && !pipelineInputs.companyBusinessModel) pipelineInputs.companyBusinessModel = analysis.businessModel;
          if (analysis.targetAudience?.primary) pipelineInputs.companyTargetAudience = analysis.targetAudience.primary;
          if (analysis.primaryOffering) pipelineInputs.companyPrimaryOffering = analysis.primaryOffering;
          if (analysis.uspSuggestions?.length) pipelineInputs.companyUsps = analysis.uspSuggestions;
        }

        if (icpData) {
          pipelineInputs.icpName = icpData.name || undefined;
          pipelineInputs.icpIndustry = icpData.industry || undefined;
          pipelineInputs.icpCompanySize = icpData.companySize || undefined;
          pipelineInputs.icpPainPoints = icpData.painPoints || undefined;
          pipelineInputs.icpBusinessGoals = icpData.businessGoals || undefined;
        }

        if (brandStrategyData) {
          pipelineInputs.brandArchetype = brandStrategyData.brandArchetype || undefined;
          pipelineInputs.brandPersonality = brandStrategyData.brandPersonality || undefined;
          pipelineInputs.brandValues = brandStrategyData.brandValues || undefined;
          pipelineInputs.brandPositioning = brandStrategyData.brandPositioning || undefined;
          pipelineInputs.brandVoice = brandStrategyData.brandVoice || undefined;
          pipelineInputs.primaryColor = brandStrategyData.primaryColor || undefined;
        }

        if (productNames?.length) {
          pipelineInputs.productNames = productNames;
          pipelineInputs.productDescriptions = productDescriptions;
        }

        // Data source enriched context
        if (personaNames?.length) {
          pipelineInputs.personaNames = personaNames;
          pipelineInputs.personaGoals = personaGoals;
          pipelineInputs.personaPainPoints = personaPainPoints;
        }
        if (competitorNames?.length) {
          pipelineInputs.competitorNames = competitorNames;
          pipelineInputs.competitorWeaknesses = competitorWeaknesses;
        }
        if (faqQuestions?.length) {
          pipelineInputs.faqQuestions = faqQuestions;
          pipelineInputs.faqAnswers = faqAnswers;
        }
        if (testimonialQuotes?.length) {
          pipelineInputs.testimonialQuotes = testimonialQuotes;
        }
        if (caseStudyTitles?.length) {
          pipelineInputs.caseStudyTitles = caseStudyTitles;
          pipelineInputs.caseStudyResults = caseStudyResults;
        }
        if (productCategoryNames?.length) {
          pipelineInputs.productCategoryNames = productCategoryNames;
        }
        if (visualIdentityData) {
          pipelineInputs.visualIdentityData = {
            primaryColor: visualIdentityData.primaryColor || undefined,
            secondaryColor: visualIdentityData.secondaryColor || undefined,
            accentColor: visualIdentityData.accentColor || undefined,
            backgroundColor: visualIdentityData.backgroundColor || undefined,
            headingFont: visualIdentityData.headingFont || undefined,
            bodyFont: visualIdentityData.bodyFont || undefined,
          };
        }
        if (founderNames?.length) {
          pipelineInputs.founderNames = founderNames;
          pipelineInputs.founderTitles = founderTitles;
          pipelineInputs.founderBios = founderBios;
        }
        if (bookTitles?.length) {
          pipelineInputs.bookTitles = bookTitles;
          pipelineInputs.bookAuthors = bookAuthors;
          pipelineInputs.bookGenres = bookGenres;
          pipelineInputs.bookDescriptions = bookDescriptions;
        }
        if (courseTitles?.length) {
          pipelineInputs.courseTitles = courseTitles;
          pipelineInputs.courseCategories = courseCategories;
          pipelineInputs.courseLevels = courseLevels;
        }
        if (eventTitles?.length) {
          pipelineInputs.eventTitles = eventTitles;
          pipelineInputs.eventTypes = eventTypes;
          pipelineInputs.eventDates = eventDates;
          pipelineInputs.eventLocations = eventLocations;
        }
        if (salesCollateralNames?.length) {
          pipelineInputs.salesCollateralNames = salesCollateralNames;
          pipelineInputs.salesCollateralTypes = salesCollateralTypes;
        }
        if (brandAssetNames?.length) {
          pipelineInputs.brandAssetNames = brandAssetNames;
          pipelineInputs.brandAssetTypes = brandAssetTypes;
        }

        updateJobProgress(job.jobId, 10, 'Preparing context...');
        const pipeline = new LandingPagePipeline(pipelineInputs, (progress, step) => updateJobProgress(job.jobId, progress, step));
        const result = await pipeline.run();

        const context = await aiContextService.create({
          companyId,
          moduleSource: 'landing-page',
          analysisType: 'full-analysis',
          inputs: { companyName: company.name, description: pipelineInputs.companyDescription },
          analysis: result.analysis,
          metadata: {
            pipelineVersion: result.pipelineVersion,
            provider: result.provider,
            model: result.aiModel,
            tokensUsed: result.tokensUsed,
            inputTokens: result.inputTokens,
            outputTokens: result.outputTokens,
            processingTimeMs: result.processingTimeMs,
            latencyMs: result.latencyMs,
            overallConfidence: result.overallConfidence,
            fieldConfidences: result.fieldConfidences,
            finishReason: result.finishReason,
            apiKeyMasked: result.apiKeyMasked,
          },
        });

        await aiContextService.updateStatus(context.id, 'approved');

        const autoFillData = computeLandingPageAutoFillMapping(result.analysis);
        completeJob(job.jobId, autoFillData, 'regenerated');
        console.log(`[LandingPage-Regenerate] Job ${job.jobId} completed. Source: regenerated`);
      } catch (err: any) {
        console.error(`[LandingPage-Regenerate] Job ${job.jobId} failed:`, err.message);
        failJob(job.jobId, err.message || 'AI regeneration failed');
      }
    });
  }
);

// ============================================
// POST /generate-full — "Generate with AI" one-shot
// Chains the full wizard into a single background job:
//   create draft page → auto-fill pipeline (title/strategy/structure/content/SEO)
//   → apply generated content to the page → generate website (HTML/CSS/JS)
//   → complete with preview + zip URLs.
// Polled by the frontend via the existing GET /status/:jobId above.
// ============================================

// Default section skeleton (mirrors the frontend DEFAULT_SECTIONS in
// src/modules/sales/landing-pages/constants.ts). IDs are assigned per-request.
const GENERATE_FULL_DEFAULT_SECTIONS = [
  { type: 'hero', name: 'Hero Section', enabled: true, order: 1 },
  { type: 'pain-points', name: 'Pain Points', enabled: true, order: 2 },
  { type: 'solution-explanation', name: 'Solution', enabled: true, order: 3 },
  { type: 'features', name: 'Features', enabled: true, order: 4 },
  { type: 'benefits', name: 'Benefits', enabled: true, order: 5 },
  { type: 'how-it-works', name: 'How It Works', enabled: true, order: 6 },
  { type: 'social-proof', name: 'Social Proof', enabled: true, order: 7 },
  { type: 'testimonials', name: 'Testimonials', enabled: true, order: 8 },
  { type: 'guarantee', name: 'Guarantee', enabled: true, order: 11 },
  { type: 'faqs', name: 'FAQs', enabled: true, order: 12 },
  { type: 'cta-section', name: 'CTA Section', enabled: true, order: 13 },
];

/** Build a concise, paste-able AI builder prompt summarising the generated page. */
function buildAiPromptSummary(page: any, brief: string): string {
  const enabledSections = (page.sections || [])
    .filter((s: any) => s.enabled)
    .sort((a: any, b: any) => (a.order || 0) - (b.order || 0))
    .map((s: any) => `- ${s.name}${s.headline ? `: ${s.headline}` : ''}`)
    .join('\n');
  return [
    '# Landing Page Builder Prompt',
    `Page Name: ${page.name || 'Untitled'}`,
    `Type: ${page.pageType || 'custom'} | Goal: ${page.primaryGoal || 'lead-generation'} | Funnel: ${page.funnelStage || 'tofu'} | Framework: ${page.framework || 'custom'}`,
    brief ? `Strategy Brief: ${brief}` : '',
    page.headline ? `Headline: ${page.headline}` : '',
    page.subHeadline ? `Subheadline: ${page.subHeadline}` : '',
    page.ctaText ? `CTA: ${page.ctaText}` : '',
    page.metaTitle ? `Meta Title: ${page.metaTitle}` : '',
    page.metaDescription ? `Meta Description: ${page.metaDescription}` : '',
    enabledSections ? `Sections:\n${enabledSections}` : '',
    '',
    'Build a responsive, conversion-optimised landing page implementing the above structure and copy. Use the company brand colours and fonts, include scroll-reveal animations, a sticky CTA, and a mobile menu.',
  ].filter(Boolean).join('\n');
}

/**
 * Auto-generate a single section image via AI and persist it as a brand asset.
 * Tries OpenAI gpt-image-1 first, falls back to Zhipu CogView-3. Returns the
 * saved asset URL (relative) or null on failure (caller treats as non-fatal).
 */
async function autoGenerateSectionImageUrl(
  section: any,
  businessName?: string,
  industry?: string,
): Promise<string | null> {
  const parts = [section?.headline, section?.subheadline, section?.description]
    .filter(Boolean)
    .map((s: any) => String(s).trim())
    .filter(Boolean);
  const subject = parts.length > 0 ? parts.join('. ') : `${section?.name || section?.type || 'landing page'} section`;
  const prompt =
    `Professional website image for the "${section?.name || section?.type}" section of a` +
    ` ${industry || 'general'} industry landing page${businessName ? ` for ${businessName}` : ''}.` +
    ` Context: ${subject}. High quality, clean modern illustration, on-brand, no text overlay.` +
    ` Wide 16:9 composition.`;

  const stamp = `${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;

  try {
    const r = await generateImageWithOpenAI(prompt, '1536x1024', 'standard', 'vivid', 'gpt-image-1');
    if (r?.base64Data) {
      const { buffer, mimeType } = base64ToBuffer(r.base64Data);
      const saved = await saveBrandAssetFile(buffer, `lp-section-${stamp}.png`, mimeType);
      return saved.url;
    }
  } catch (err: any) {
    console.warn('[LandingPage-GenerateFull] OpenAI image gen failed, trying CogView:', err?.message);
  }

  try {
    const r = await generateImageWithZhipuCogView(prompt, '1024x1024');
    if (r?.base64Data) {
      const { buffer, mimeType } = base64ToBuffer(r.base64Data);
      const saved = await saveBrandAssetFile(buffer, `lp-section-${stamp}.png`, mimeType);
      return saved.url;
    }
  } catch (err: any) {
    console.warn('[LandingPage-GenerateFull] CogView image gen failed:', err?.message);
  }

  return null;
}

router.post(
  '/generate-full',
  requirePermission('landing-pages', 'ai-generate'),
  [body('companyId').notEmpty().withMessage('Company ID is required')],
  async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { companyId, strategy, linkedData, uploadedImageUrls, autoGenerateImages, language } = req.body as {
      companyId: string;
      strategy: { name?: string; brief?: string; description?: string; pageType?: string; primaryGoal?: string; funnelStage?: string; audienceType?: string };
      linkedData?: Record<string, any>;
      uploadedImageUrls?: string[];
      autoGenerateImages?: boolean;
      language?: string;
    };

    const brief = (strategy?.brief || strategy?.description || '').trim();
    if (!strategy?.name || !strategy.name.trim()) {
      res.status(400).json({ error: 'Page name is required' });
      return;
    }
    if (!brief) {
      res.status(400).json({ error: 'Strategy brief is required' });
      return;
    }

    // Create the job and return immediately — all heavy work runs in the background.
    const job = createJob('landing-page', companyId, req.body._moduleId || 'landing-pages');
    res.status(202).json({ jobId: job.jobId, status: 'processing' });

    const origin = req.get('origin') || `${req.protocol}://${req.get('host')}`;
    const pageName = strategy.name.trim();

    setImmediate(async () => {
      let pageId: string | undefined;
      try {
        // ---- Step 1 (5%): create a draft landing page so we have a pageId ----
        updateJobProgress(job.jobId, 5, 'Creating draft landing page...');
        let doc = await LandingPageContentOS.findOne({ companyId });
        if (!doc) doc = new LandingPageContentOS({ companyId });
        pageId = new mongoose.Types.ObjectId().toString();
        const sections = GENERATE_FULL_DEFAULT_SECTIONS.map((s, i) => ({
          ...s,
          id: `section-${Date.now()}-${i}`,
          headline: '', subheadline: '', description: '', cta: '',
          bulletPoints: [], trustStatements: [], uiNotes: '', conversionNotes: '', seoNotes: '',
        }));
        const newPage: Record<string, any> = {
          id: pageId,
          companyId,
          name: pageName,
          description: brief,
          pageType: strategy.pageType || '',
          primaryGoal: strategy.primaryGoal || '',
          secondaryGoal: '',
          funnelStage: strategy.funnelStage || '',
          framework: '',
          trafficSource: '',
          headline: '', subHeadline: '', ctaText: '', content: '',
          sections,
          seoKeywords: [],
          searchIntent: '', metaTitle: '', metaDescription: '',
          linkedData: linkedData || {},
          aiPrompt: '',
          audienceType: strategy.audienceType || '',
          language: language || 'en',
          status: 'draft',
          version: 1,
          createdAt: new Date().toISOString(),
          updatedAt: new Date().toISOString(),
          // Mark the page as generating immediately so the frontend can show a
          // loader even if the user refreshes before website generation starts.
          generatedWebsite: {
            status: 'generating',
            jobId: job.jobId,
            generatedAt: new Date().toISOString(),
          },
        };
        doc.pages.push(newPage);
        await doc.save();

        // ---- Step 2 (10–50%): load context + run the auto-fill pipeline ----
        updateJobProgress(job.jobId, 10, 'Loading company context...');
        const { Company, BusinessProfile, ICP, Product } = getModels();
        const company = await Company.findById(companyId);
        if (!company) {
          // Mark the just-created page as failed so the frontend stops showing a spinner
          await updateGeneratedWebsiteStatus(companyId, pageId!, { status: 'failed', jobId: job.jobId, error: 'Company not found' }).catch(() => {});
          failJob(job.jobId, 'Company not found');
          return;
        }

        let businessProfile: any = null;
        try { businessProfile = await BusinessProfile.findOne({ companyId }); } catch {}

        let latestCompanyContext: any = null;
        try {
          const companyContexts = await aiContextService.getByCompany(companyId, 'company-creation');
          latestCompanyContext = companyContexts.find((c: any) => {
            const a = c.analysis?.toObject?.() || c.analysis || {};
            return c.status === 'approved' || a.industryType || a.businessModel || a.businessSummary;
          });
        } catch {}

        let icpData: any = null;
        try { icpData = await ICP.findOne({ companyId, isActive: true }).sort({ createdAt: -1 }); } catch {}

        let brandStrategyData: any = null;
        let visualIdentityData: any = null;
        try {
          const { ModuleData } = getModels();
          const bsDoc = await ModuleData.findOne({ moduleId: 'brand-strategy', companyId });
          if (bsDoc?.data) brandStrategyData = bsDoc.data;
          const viDoc = await ModuleData.findOne({ moduleId: 'visual-identity', companyId });
          if (viDoc?.data) visualIdentityData = viDoc.data;
        } catch {}

        let productNames: string[] | undefined;
        let productDescriptions: string[] | undefined;
        try {
          const products = await Product.find({ companyId }).limit(10);
          if (products.length > 0) {
            productNames = products.map((p: any) => p.name);
            productDescriptions = products.map((p: any) => p.description || p.shortDescription || '');
          }
        } catch {}

        const pipelineInputs: LandingPagePipelineInputs = {
          companyName: company.name,
          companyDescription: company.description || businessProfile?.description || undefined,
          companyIndustry: businessProfile?.primaryIndustry || undefined,
          companyBusinessModel: businessProfile?.businessModel || undefined,
          companyTargetGeography: businessProfile?.targetGeography || businessProfile?.targetMarket || undefined,
          companyCountry: businessProfile?.country || businessProfile?.headquartersCountry || undefined,
          existingLandingPageName: pageName,
          existingLandingPageType: strategy.pageType || undefined,
          existingLandingPageGoal: strategy.primaryGoal || undefined,
          existingLandingPageFunnelStage: strategy.funnelStage || undefined,
          strategyBrief: brief,
          language: language || undefined,
        };

        if (latestCompanyContext) {
          const analysis = latestCompanyContext.analysis?.toObject?.() || latestCompanyContext.analysis || {};
          if (analysis.industryType && !pipelineInputs.companyIndustry) pipelineInputs.companyIndustry = analysis.industryType;
          if (analysis.businessModel && !pipelineInputs.companyBusinessModel) pipelineInputs.companyBusinessModel = analysis.businessModel;
          if (analysis.targetAudience?.primary) pipelineInputs.companyTargetAudience = analysis.targetAudience.primary;
          if (analysis.primaryOffering) pipelineInputs.companyPrimaryOffering = analysis.primaryOffering;
          if (analysis.uspSuggestions?.length) pipelineInputs.companyUsps = analysis.uspSuggestions;
        }
        if (icpData) {
          pipelineInputs.icpName = icpData.name || undefined;
          pipelineInputs.icpIndustry = icpData.industry || undefined;
          pipelineInputs.icpPainPoints = icpData.painPoints || undefined;
          pipelineInputs.icpBusinessGoals = icpData.businessGoals || undefined;
        }
        if (brandStrategyData) {
          pipelineInputs.brandArchetype = brandStrategyData.brandArchetype || undefined;
          pipelineInputs.brandPersonality = brandStrategyData.brandPersonality || undefined;
          pipelineInputs.brandValues = brandStrategyData.brandValues || undefined;
          pipelineInputs.brandPositioning = brandStrategyData.brandPositioning || undefined;
          pipelineInputs.brandVoice = brandStrategyData.brandVoice || undefined;
          pipelineInputs.primaryColor = brandStrategyData.primaryColor || undefined;
        }
        if (productNames?.length) {
          pipelineInputs.productNames = productNames;
          pipelineInputs.productDescriptions = productDescriptions;
        }
        if (visualIdentityData) {
          pipelineInputs.visualIdentityData = {
            primaryColor: visualIdentityData.primaryColor || undefined,
            secondaryColor: visualIdentityData.secondaryColor || undefined,
            accentColor: visualIdentityData.accentColor || undefined,
            backgroundColor: visualIdentityData.backgroundColor || undefined,
            headingFont: visualIdentityData.headingFont || undefined,
            bodyFont: visualIdentityData.bodyFont || undefined,
          };
        }

        updateJobProgress(job.jobId, 15, 'Generating strategy, structure & content with AI...');
        const pipeline = new LandingPagePipeline(pipelineInputs, (progress, step) =>
          updateJobProgress(job.jobId, 15 + Math.round(progress * 0.35), step)
        );
        const result = await pipeline.run();

        // Record in AI Processing history (same as /auto-fill). Non-fatal if it fails.
        try {
          const context = await aiContextService.create({
            companyId,
            moduleSource: 'landing-page',
            analysisType: 'full-analysis',
            inputs: { companyName: company.name, description: brief },
            analysis: result.analysis,
            metadata: {
              pipelineVersion: result.pipelineVersion,
              provider: result.provider,
              model: result.aiModel,
              tokensUsed: result.tokensUsed,
              inputTokens: result.inputTokens,
              outputTokens: result.outputTokens,
              processingTimeMs: result.processingTimeMs,
              latencyMs: result.latencyMs,
              overallConfidence: result.overallConfidence,
              fieldConfidences: result.fieldConfidences,
              finishReason: result.finishReason,
              apiKeyMasked: result.apiKeyMasked,
            },
          });
          await aiContextService.updateStatus(context.id, 'approved');
        } catch (err: any) {
          console.warn('[LandingPage-GenerateFull] aiContext record failed (non-fatal):', err.message);
        }

        // ---- Step 3 (50–55%): apply generated content onto the page document ----
        updateJobProgress(job.jobId, 52, 'Applying generated content to page...');
        const mapping = computeLandingPageAutoFillMapping(result.analysis);
        const updatedDoc = await LandingPageContentOS.findOne({ companyId });
        const pageIndex = updatedDoc?.pages.findIndex((p: any) => p.id === pageId);
        if (!updatedDoc || pageIndex === -1 || pageIndex === undefined) {
          throw new Error('Created landing page not found while applying AI content');
        }
        const pageObj = updatedDoc.pages[pageIndex] as Record<string, any>;
        for (const [key, value] of Object.entries(mapping)) {
          if (value === undefined || value === null) continue;
          pageObj[key] = value;
        }

        // ---- Guarantee publish-required fields so "Update & Publish" never fails
        // silently. The normalise* helpers default to valid enums, but they only run
        // when the AI actually emitted the field — so fall back to the user's modal
        // selection, then to a safe default.
        if (!pageObj.pageType) pageObj.pageType = strategy.pageType || 'lead-generation';
        if (!pageObj.primaryGoal) pageObj.primaryGoal = strategy.primaryGoal || 'lead-generation';
        if (!pageObj.funnelStage) pageObj.funnelStage = strategy.funnelStage || 'tofu';

        // Every enabled section must have a non-empty headline (publish validation
        // requires this). Fall back to the section name if the AI left it blank.
        if (Array.isArray(pageObj.sections)) {
          for (const sec of pageObj.sections) {
            if (sec.enabled !== false && (!sec.headline || !String(sec.headline).trim())) {
              sec.headline = sec.name || `${sec.type || 'Section'} headline`;
            }
          }
        }

        // Preserve the user's chosen name, brief, and audience; fill the AI builder prompt.
        pageObj.name = pageName;
        pageObj.description = brief;
        if (strategy.audienceType) pageObj.audienceType = strategy.audienceType;
        pageObj.aiPrompt = buildAiPromptSummary(pageObj, brief);

        // ---- Step 3b: section images ----
        // Each landing page uses ONLY its own section images. If the user uploaded
        // images, assign them to the enabled sections (hero first). Otherwise auto-
        // generate one image per section with AI (capped to keep things timely).
        const enabledSections = (pageObj.sections || []).filter((s: any) => s.enabled !== false);
        if (uploadedImageUrls && uploadedImageUrls.length > 0) {
          updateJobProgress(job.jobId, 53, 'Assigning uploaded images to sections...');
          enabledSections.forEach((sec: any, i: number) => {
            sec.media = [uploadedImageUrls[i % uploadedImageUrls.length]];
          });
        } else if (autoGenerateImages !== false) {
          const businessName = businessProfile?.name || company.name;
          const industry = businessProfile?.primaryIndustry;
          const IMAGE_CAP = 4; // cap auto-generated images so the job stays within the poll window
          const toGenerate = enabledSections.slice(0, IMAGE_CAP);
          for (let i = 0; i < toGenerate.length; i++) {
            const sec = toGenerate[i];
            const pct = 53 + Math.round(((i + 1) / toGenerate.length) * 7); // 53 → 60
            updateJobProgress(job.jobId, pct, `Generating section image ${i + 1}/${toGenerate.length}: ${sec.name || sec.type}...`);
            try {
              const url = await autoGenerateSectionImageUrl(sec, businessName, industry);
              if (url) {
                sec.media = [...(sec.media || []), url];
              }
            } catch (err: any) {
              console.warn(`[LandingPage-GenerateFull] image gen skipped for section ${sec.name}:`, err?.message);
            }
          }
        }

        pageObj.updatedAt = new Date().toISOString();
        updatedDoc.markModified('pages');
        await updatedDoc.save();

        // ---- Step 4 (60–95%): generate the website (HTML/CSS/JS) ----
        updateJobProgress(job.jobId, 60, 'Generating landing page website...');
        const finalPage = updatedDoc.pages[pageIndex];
        const genResult = await generateLandingPageWebsiteCore(
          finalPage,
          companyId,
          { framework: 'html', styling: 'css', responsive: true },
          origin,
          (progress, step) => updateJobProgress(job.jobId, 60 + Math.round(progress * 0.35), step),
          job.jobId,
        );

        completeJob(job.jobId, {
          landingPageId: pageId,
          previewUrl: genResult.previewUrl,
          zipUrl: genResult.zipUrl,
          pageData: finalPage,
        }, 'generated');
        console.log(`[LandingPage-GenerateFull] Job ${job.jobId} completed. pageId: ${pageId}`);
      } catch (err: any) {
        console.error(`[LandingPage-GenerateFull] Job ${job.jobId} failed:`, err.message);
        // Mark the page's generatedWebsite as failed so the frontend stops showing
        // a spinner after a refresh — the generation is not coming back.
        if (pageId) {
          try {
            await updateGeneratedWebsiteStatus(companyId, pageId, {
              status: 'failed',
              jobId: job.jobId,
              error: err.message || 'AI generation failed',
            });
          } catch (dbErr: any) {
            console.warn('[LandingPage-GenerateFull] Failed to update generatedWebsite status on failure:', dbErr?.message);
          }
        }
        failJob(job.jobId, err.message || 'AI generation failed');
      }
    });
  }
);

export default router;