/**
 * GEO Optimization Routes
 *
 * CRUD + AI generation for AI Discoverability content:
 * - Quora Answers
 * - AI Overview Optimisation
 * - People Also Ask SEO
 * - ChatGPT / GEO Submissions
 * - Directory Submissions
 */

import express, { Request, Response } from 'express';
import { body, param, validationResult } from 'express-validator';
import { authenticate } from '../middleware/auth';
import { getModels } from '../models';
import { generateWithAI, getAIConfig } from '../utils/aiProvider';
import { aiContextService } from '../services/aiContext/aiContextService';
import { createJob, completeJob, failJob } from '../services/aiContext/aiJobManager';
import { runReadinessAudit } from '../services/geo/readinessAudit';
import {
  buildGeoAnalytics,
  GeoIntelligenceInput,
  PersistedGeoData,
  GeoAnalyticsBundle,
} from '../services/geo/geoIntelligence';
import { simulateCitation } from '../services/geo/geoCitationSimulator';

const router = express.Router();
router.use(authenticate);

// ─── Validation ──────────────────────────────────────────────────────────────

// Length ceilings mirror the frontend GEO_LIMITS (Add Discoverability form) and
// the Mongoose schema so a direct API request cannot store oversized text.
const validateCreate = [
  body('companyId').isString().notEmpty(),
  body('title').isString().notEmpty().trim().isLength({ max: 200 }).withMessage('Title must be 200 characters or fewer'),
  body('contentType').isIn([
    'quora-answer',
    'ai-overview',
    'people-also-ask',
    'chatgpt-submission',
    'directory-submission',
  ]),
  body('description').optional().isLength({ max: 1000 }).withMessage('Description must be 1000 characters or fewer'),
  body('targetQuestion').optional().isLength({ max: 500 }).withMessage('Target Question must be 500 characters or fewer'),
  body('targetKeyword').optional().isLength({ max: 200 }).withMessage('Target Keyword must be 200 characters or fewer'),
  body('targetDirectory').optional().isLength({ max: 200 }).withMessage('Target Directory must be 200 characters or fewer'),
  body('generatedTitle').optional().isLength({ max: 200 }).withMessage('Generated Title must be 200 characters or fewer'),
  body('generatedSummary').optional().isLength({ max: 300 }).withMessage('Generated Summary must be 300 characters or fewer'),
  body('generatedContent').optional().isString(),
];

const validateUpdate = [
  param('id').isMongoId(),
  body('title').optional().isString().trim().isLength({ max: 200 }).withMessage('Title must be 200 characters or fewer'),
  body('description').optional().isLength({ max: 1000 }).withMessage('Description must be 1000 characters or fewer'),
  body('targetQuestion').optional().isLength({ max: 500 }).withMessage('Target Question must be 500 characters or fewer'),
  body('targetKeyword').optional().isLength({ max: 200 }).withMessage('Target Keyword must be 200 characters or fewer'),
  body('targetDirectory').optional().isLength({ max: 200 }).withMessage('Target Directory must be 200 characters or fewer'),
  body('generatedTitle').optional().isLength({ max: 200 }).withMessage('Generated Title must be 200 characters or fewer'),
  body('generatedSummary').optional().isLength({ max: 300 }).withMessage('Generated Summary must be 300 characters or fewer'),
  body('generatedContent').optional().isString(),
  body('status').optional().isIn(['draft', 'generated', 'reviewed', 'published', 'submitted']),
];

// ─── Helpers ─────────────────────────────────────────────────────────────────

const handleError = (res: Response, error: any) => {
  console.error('[GEOOptimization] Error:', error);
  res.status(500).json({ error: error.message || 'Internal server error' });
};

const authorizeCompany = (req: Request, companyId: string): boolean => {
  return req.user?.companyIds?.includes(companyId) || req.user?.role === 'admin';
};

// ─── CRUD Routes ─────────────────────────────────────────────────────────────

/** GET /api/geo-optimization/:companyId — list all GEO content for a company */
/**
 * GET /readiness/:companyId
 *
 * The AI-Readiness audit: entity completeness, trust signals, content coverage
 * and brand authority, computed from the modules the user has already filled in.
 *
 * Declared BEFORE `/:companyId` — Express matches in order, and the generic
 * route would otherwise swallow "readiness" as a company id.
 *
 * Every count is a `countDocuments`, never a full fetch: the audit only needs
 * how many exist, and pulling every blog post to length-check an array would
 * make this the slowest endpoint in the module.
 */
router.get('/readiness/:companyId', async (req: Request, res: Response) => {
  try {
    const { companyId } = req.params;
    if (!authorizeCompany(req, companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    const m = getModels() as Record<string, any>;
    // Each lookup is individually guarded: a module that is absent or empty for
    // this company must degrade to zero, never fail the whole audit.
    const safeCount = async (model: any, filter: Record<string, unknown>): Promise<number> => {
      try { return model ? await model.countDocuments(filter) : 0; } catch { return 0; }
    };
    const safeOne = async (model: any, filter: Record<string, unknown>) => {
      try { return model ? await model.findOne(filter).lean() : null; } catch { return null; }
    };

    const [
      businessProfile, knowledgePanel, wikipediaArticle,
      faqs, products, blogPosts, caseStudies, testimonials, founders, landingPages, geoContent,
    ] = await Promise.all([
      safeOne(m.BusinessProfile, { companyId }),
      safeOne(m.KnowledgePanel, { companyId }),
      safeOne(m.WikipediaArticle, { companyId }),
      safeCount(m.FAQ, { companyId }),
      safeCount(m.Product, { companyId }),
      safeCount(m.Blog, { companyId }),
      safeCount(m.CaseStudy, { companyId }),
      safeCount(m.Testimonial, { companyId }),
      safeCount(m.Founder, { companyId }),
      safeCount(m.LandingPageContentOS, { companyId }),
      safeCount(m.GEOOptimization, { companyId }),
    ]);

    const report = runReadinessAudit({
      businessProfile: businessProfile as Record<string, any> | null,
      knowledgePanel: knowledgePanel as Record<string, any> | null,
      wikipediaArticle: wikipediaArticle as Record<string, any> | null,
      counts: { faqs, products, blogPosts, caseStudies, testimonials, founders, landingPages, geoContent },
    });

    res.json(report);
  } catch (error: any) {
    console.error('[GEO Readiness] Audit failed:', error.message);
    res.status(500).json({ error: 'Failed to run the readiness audit' });
  }
});

// ─── AI Discoverability & Entity Intelligence ────────────────────────────────
//
// The GEO module is more than a content generator: it is an intelligence
// platform. Everything below is computed from the company's OWN module data
// (Cross-Module Automation) — no external scraping, no re-entering data.

/** Collect every module signal the intelligence engine needs. */
async function fetchGeoInput(companyId: string): Promise<GeoIntelligenceInput> {
  const m = getModels() as Record<string, any>;
  const safeOne = async (model: any, filter: Record<string, unknown>) => {
    try { return model ? await model.findOne(filter).lean() : null; } catch { return null; }
  };
  const safeCount = async (model: any, filter: Record<string, unknown>): Promise<number> => {
    try { return model ? await model.countDocuments(filter) : 0; } catch { return 0; }
  };
  const safeFind = async (model: any, filter: Record<string, unknown>, select?: string) => {
    try {
      if (!model) return [];
      const q = model.find(filter);
      if (select) q.select(select);
      return await q.lean();
    } catch { return []; }
  };

  const [businessProfile, brand, knowledgePanel, wikipediaArticle, competitors, geoItems, founders, legalDocs] =
    await Promise.all([
      safeOne(m.BusinessProfile, { companyId }),
      safeOne(m.Brand, { companyId }),
      safeOne(m.KnowledgePanel, { companyId }),
      safeOne(m.WikipediaArticle, { companyId }),
      safeFind(m.Competitor, { companyId, isActive: true }),
      safeFind(m.GEOOptimization, { companyId }),
      safeFind(m.Founder, { companyId }, 'name designation bio'),
      safeFind(m.LegalDocument, { companyId, status: { $ne: 'archived' } }, 'type category status'),
    ]);

  const counts = {
    faqs: await safeCount(m.FAQ, { companyId }),
    products: await safeCount(m.Product, { companyId }),
    blogPosts: await safeCount(m.Blog, { companyId }),
    caseStudies: await safeCount(m.CaseStudy, { companyId }),
    testimonials: await safeCount(m.Testimonial, { companyId }),
    founders: await safeCount(m.Founder, { companyId }),
    landingPages: await safeCount(m.LandingPageContentOS, { companyId }),
    geoContent: geoItems.length,
    awards: await safeCount(m.Award, { companyId }),
    reviews: await safeCount(m.ReputationReview, { companyId }),
    gmbLocations: await safeCount(m.GmbLocation, { companyId }),
    mediaMentions: await safeCount(m.MediaMention, { companyId }),
  };

  return { businessProfile, brand, knowledgePanel, wikipediaArticle, competitors, geoItems, founders, legalDocs, counts };
}

/** Load persisted GEO intelligence state (history, platform metrics, user statuses). */
async function fetchPersistedGeoData(companyId: string): Promise<PersistedGeoData> {
  const m = getModels() as Record<string, any>;
  const safeFind = async (model: any, filter: Record<string, unknown>, sort?: string, limit?: number) => {
    try {
      if (!model) return [];
      const q = model.find(filter);
      if (sort) q.sort(sort);
      if (limit) q.limit(limit);
      return await q.lean();
    } catch { return []; }
  };
  const [snapshots, platformMetrics, opportunities, recommendations, settings] = await Promise.all([
    safeFind(m.GeoAuditSnapshot, { companyId }, '-generatedAt', 12),
    safeFind(m.GeoPlatformMetric, { companyId }, '-recordedAt', 100),
    safeFind(m.GeoCitationOpportunity, { companyId }),
    safeFind(m.GeoRecommendation, { companyId }),
    (async () => {
      try { return m.GeoAuditSettings ? await m.GeoAuditSettings.findOne({ companyId }).lean() : null; } catch { return null; }
    })(),
  ]);
  return { snapshots, platformMetrics, opportunities, recommendations, settings };
}

/** Upsert computed opportunities/recommendations so user statuses persist. */
async function savePersistedInsights(companyId: string, bundle: GeoAnalyticsBundle): Promise<void> {
  const m = getModels() as Record<string, any>;
  try {
    for (const opp of bundle.citationOpportunities) {
      await m.GeoCitationOpportunity?.findOneAndUpdate(
        { companyId, sourceKey: opp.id },
        {
          $set: {
            opportunityType: opp.opportunityType,
            title: opp.title,
            gap: opp.gap,
            priority: opp.priority,
            suggestedContent: opp.suggestedContent,
          },
        },
        { upsert: true, new: true }
      );
    }
    for (const rec of bundle.recommendations) {
      await m.GeoRecommendation?.findOneAndUpdate(
        { companyId, sourceKey: rec.id },
        {
          $set: {
            source: rec.source,
            title: rec.title,
            reasoning: rec.reasoning,
            priority: rec.priority,
            targetModule: rec.targetModule || '',
          },
        },
        { upsert: true, new: true }
      );
    }
  } catch (err: any) {
    console.warn(`[GEO Intelligence] Persisting insights failed (non-fatal): ${err.message}`);
  }
}

/** Run an audit, save the snapshot and roll audit timestamps forward. */
async function runAndSaveAudit(
  companyId: string,
  input: GeoIntelligenceInput,
  auditType: 'manual' | 'scheduled' | 'baseline'
): Promise<Record<string, any>> {
  const m = getModels() as Record<string, any>;
  let settings: any = null;
  try { settings = m.GeoAuditSettings ? await m.GeoAuditSettings.findOne({ companyId }).lean() : null; } catch { /* ignore */ }

  const persisted = await fetchPersistedGeoData(companyId);
  const bundle = buildGeoAnalytics(input, persisted);

  const snapshot = await m.GeoAuditSnapshot.create({
    companyId,
    auditType,
    scores: bundle.scores,
    inventory: bundle.inventory,
    recommendationsCount: bundle.actionCenter.length,
    generatedAt: new Date(),
  });

  if (m.GeoAuditSettings) {
    const frequency = settings?.frequency === 'monthly' ? 'monthly' : settings?.frequency === 'biweekly' ? 'biweekly' : 'weekly';
    const frequencyDays = frequency === 'monthly' ? 30 : frequency === 'biweekly' ? 14 : 7;
    await m.GeoAuditSettings.findOneAndUpdate(
      { companyId },
      {
        $set: {
          autoAuditEnabled: !!settings?.autoAuditEnabled,
          frequency,
          lastAuditAt: new Date(),
          nextAuditAt: new Date(Date.now() + frequencyDays * 86400000),
        },
      },
      { upsert: true, new: true }
    );
  }

  await savePersistedInsights(companyId, bundle);
  return snapshot;
}

/** GET /api/geo-optimization/analytics/:companyId — the full intelligence bundle. */
router.get('/analytics/:companyId', async (req: Request, res: Response) => {
  try {
    const { companyId } = req.params;
    if (!authorizeCompany(req, companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    const input = await fetchGeoInput(companyId);
    let persisted = await fetchPersistedGeoData(companyId);

    // Lazy scheduled audit — if auto-audit is on and the next run is due, take
    // a snapshot now so the trends/history stay current without a job queue.
    if (persisted.settings?.autoAuditEnabled) {
      const now = new Date();
      const next = persisted.settings.nextAuditAt ? new Date(persisted.settings.nextAuditAt) : null;
      if (!next || next <= now) {
        await runAndSaveAudit(companyId, input, 'scheduled');
        persisted = await fetchPersistedGeoData(companyId);
      }
    }

    const bundle = buildGeoAnalytics(input, persisted);
    await savePersistedInsights(companyId, bundle);
    res.json(bundle);
  } catch (error: any) {
    handleError(res, error);
  }
});

/** GET /api/geo-optimization/audits/:companyId — audit snapshot history. */
router.get('/audits/:companyId', async (req: Request, res: Response) => {
  try {
    const { companyId } = req.params;
    if (!authorizeCompany(req, companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }
    const { GeoAuditSnapshot } = getModels();
    const snapshots = await GeoAuditSnapshot.find({ companyId }).sort({ generatedAt: -1 }).limit(24);
    res.json(snapshots);
  } catch (error: any) {
    handleError(res, error);
  }
});

/** POST /api/geo-optimization/audit/run/:companyId — run + persist an audit now. */
router.post('/audit/run/:companyId', async (req: Request, res: Response) => {
  try {
    const { companyId } = req.params;
    if (!authorizeCompany(req, companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }
    const input = await fetchGeoInput(companyId);
    const snapshot = await runAndSaveAudit(companyId, input, 'manual');
    res.status(201).json({ success: true, snapshot });
  } catch (error: any) {
    handleError(res, error);
  }
});

/** POST /api/geo-optimization/audit/settings/:companyId — configure auto-audits. */
router.post('/audit/settings/:companyId', async (req: Request, res: Response) => {
  try {
    const { companyId } = req.params;
    if (!authorizeCompany(req, companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }
    const { autoAuditEnabled, frequency } = req.body || {};
    const freq = frequency === 'monthly' || frequency === 'biweekly' ? frequency : 'weekly';
    const frequencyDays = freq === 'monthly' ? 30 : freq === 'biweekly' ? 14 : 7;
    const { GeoAuditSettings } = getModels();
    const now = new Date();
    const settings = await GeoAuditSettings.findOneAndUpdate(
      { companyId },
      {
        $set: {
          autoAuditEnabled: !!autoAuditEnabled,
          frequency: freq,
          nextAuditAt: !!autoAuditEnabled ? new Date(now.getTime() + frequencyDays * 86400000) : null,
        },
      },
      { upsert: true, new: true }
    );
    res.json(settings);
  } catch (error: any) {
    handleError(res, error);
  }
});

/** POST /api/geo-optimization/platforms/:companyId — record a platform metric. */
router.post('/platforms/:companyId', async (req: Request, res: Response) => {
  try {
    const { companyId } = req.params;
    if (!authorizeCompany(req, companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }
    const { platform, visibility, citations, readiness, notes } = req.body || {};
    if (!platform || typeof visibility !== 'number') {
      res.status(400).json({ error: 'platform and visibility (number) are required' });
      return;
    }
    const { GeoPlatformMetric } = getModels();
    const clamp = (v: number) => Math.max(0, Math.min(100, Math.round(Number(v) || 0)));
    const metric = await GeoPlatformMetric.create({
      companyId,
      platform,
      visibility: clamp(visibility),
      citations: Math.max(0, Math.round(Number(citations) || 0)),
      readiness: clamp(readiness),
      notes: typeof notes === 'string' ? notes.slice(0, 500) : '',
      recordedAt: new Date(),
    });
    res.status(201).json(metric);
  } catch (error: any) {
    handleError(res, error);
  }
});

/** POST /api/geo-optimization/citations/:companyId/status — update opportunity status. */
router.post('/citations/:companyId/status', async (req: Request, res: Response) => {
  try {
    const { companyId } = req.params;
    if (!authorizeCompany(req, companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }
    const { id, status } = req.body || {};
    const valid = ['open', 'planned', 'in-progress', 'done', 'dismissed'];
    if (!id || !valid.includes(status)) {
      res.status(400).json({ error: 'id and a valid status are required' });
      return;
    }
    const { GeoCitationOpportunity } = getModels();
    const updated = await GeoCitationOpportunity.findOneAndUpdate(
      { companyId, sourceKey: id },
      {
        $set: {
          status,
          opportunityType: req.body?.opportunityType || id.replace('opportunity-', ''),
          title: req.body?.title || 'Citation opportunity',
          gap: req.body?.gap || '',
          suggestedContent: req.body?.suggestedContent || '',
        },
      },
      { upsert: true, new: true }
    );
    res.json(updated);
  } catch (error: any) {
    handleError(res, error);
  }
});

/** POST /api/geo-optimization/recommendations/:companyId/status — update recommendation status. */
router.post('/recommendations/:companyId/status', async (req: Request, res: Response) => {
  try {
    const { companyId } = req.params;
    if (!authorizeCompany(req, companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }
    const { id, status } = req.body || {};
    const valid = ['open', 'approved', 'ignored', 'done'];
    if (!id || !valid.includes(status)) {
      res.status(400).json({ error: 'id and a valid status are required' });
      return;
    }
    const { GeoRecommendation } = getModels();
    const updated = await GeoRecommendation.findOneAndUpdate(
      { companyId, sourceKey: id },
      {
        $set: {
          status,
          title: req.body?.title || 'Recommendation',
          reasoning: req.body?.reasoning || '',
        },
      },
      { upsert: true, new: true }
    );
    res.json(updated);
  } catch (error: any) {
    handleError(res, error);
  }
});

/** POST /api/geo-optimization/simulate — predict citation for an AI search prompt. */
router.post('/simulate', async (req: Request, res: Response) => {
  try {
    const { companyId, prompt } = req.body || {};
    if (!companyId || !prompt || typeof prompt !== 'string' || !prompt.trim()) {
      res.status(400).json({ error: 'companyId and prompt are required' });
      return;
    }
    if (!authorizeCompany(req, companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }
    const input = await fetchGeoInput(companyId);
    const result = simulateCitation({
      prompt: prompt.trim().slice(0, 300),
      businessProfile: input.businessProfile,
      geoItems: input.geoItems,
      counts: input.counts,
      competitors: input.competitors.map((c: any) => c.name),
    });

    // Keep a run history (non-fatal if it fails).
    try {
      const { GeoSimulationRun } = getModels();
      await GeoSimulationRun.create({
        companyId,
        prompt: result.prompt,
        predictedCitation: result.predictedCitation,
        visibilityProbability: result.visibilityProbability,
        weakAreas: result.weakAreas,
        missingContent: result.missingContent,
        recommendedImprovements: result.recommendedImprovements,
      });
    } catch (err: any) {
      console.warn(`[GEO Simulator] Saving run failed (non-fatal): ${err.message}`);
    }

    res.json(result);
  } catch (error: any) {
    handleError(res, error);
  }
});

router.get('/:companyId', async (req: Request, res: Response) => {
  try {
    const { companyId } = req.params;
    if (!authorizeCompany(req, companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    const { GEOOptimization } = getModels();
    const { contentType, status } = req.query;

    const filter: any = { companyId };
    if (contentType) filter.contentType = contentType;
    if (status) filter.status = status;

    const items = await GEOOptimization.find(filter).sort({ createdAt: -1 });
    // Return the raw array (app-wide convention: apiRequest exposes the body as
    // `res.data`). The previous `{ success, data }` envelope made the frontend's
    // `Array.isArray(res.data)` check fail, so the list never rendered.
    res.json(items);
  } catch (error) {
    handleError(res, error);
  }
});

/** GET /api/geo-optimization/detail/:id — get single GEO content */
router.get('/detail/:id', async (req: Request, res: Response) => {
  try {
    const { GEOOptimization } = getModels();
    const item = await GEOOptimization.findById(req.params.id);
    if (!item) {
      res.status(404).json({ error: 'Not found' });
      return;
    }
    if (!authorizeCompany(req, item.companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }
    res.json(item);
  } catch (error) {
    handleError(res, error);
  }
});

/** POST /api/geo-optimization — create new GEO content */
router.post('/', validateCreate, async (req: Request, res: Response) => {
  try {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { companyId } = req.body;
    if (!authorizeCompany(req, companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    const { GEOOptimization } = getModels();
    const item = await GEOOptimization.create({
      ...req.body,
      userId: req.user?._id,
      status: req.body.generatedContent ? 'generated' : 'draft',
    });

    res.status(201).json(item);
  } catch (error) {
    handleError(res, error);
  }
});

/** PUT /api/geo-optimization/:id — update GEO content */
router.put('/:id', validateUpdate, async (req: Request, res: Response) => {
  try {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { GEOOptimization } = getModels();
    const item = await GEOOptimization.findById(req.params.id);
    if (!item) {
      res.status(404).json({ error: 'Not found' });
      return;
    }
    if (!authorizeCompany(req, item.companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    const updated = await GEOOptimization.findByIdAndUpdate(
      req.params.id,
      { ...req.body, updatedAt: new Date() },
      { new: true }
    );

    res.json(updated);
  } catch (error) {
    handleError(res, error);
  }
});

/** DELETE /api/geo-optimization/:id */
router.delete('/:id', async (req: Request, res: Response) => {
  try {
    const { GEOOptimization } = getModels();
    const item = await GEOOptimization.findById(req.params.id);
    if (!item) {
      res.status(404).json({ error: 'Not found' });
      return;
    }
    if (!authorizeCompany(req, item.companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    await GEOOptimization.findByIdAndDelete(req.params.id);
    res.json({ success: true, message: 'Deleted' });
  } catch (error) {
    handleError(res, error);
  }
});

// ─── AI Generation ───────────────────────────────────────────────────────────

/** POST /api/geo-optimization/:id/generate — AI generate content */
router.post('/:id/generate', async (req: Request, res: Response) => {
  // This module generated without ever opening a job, so it reached neither the
  // completion notification nor the completion email that every job-based module
  // gets. Declared out here so the catch below can fail the job.
  let aiJob: { jobId: string } | null = null;
  try {
    const startTime = Date.now();
    const { GEOOptimization } = getModels();
    const item = await GEOOptimization.findById(req.params.id);
    if (!item) {
      res.status(404).json({ error: 'Not found' });
      return;
    }
    if (!authorizeCompany(req, item.companyId)) {
      res.status(403).json({ error: 'Access denied' });
      return;
    }

    // Build prompt based on content type
    const prompt = buildGeoPrompt(item, req.body);

    const aiConfig = await getAIConfig();
    const systemPrompt = `You are an expert AI Discoverability (GEO) content strategist. Create content optimised for visibility in AI-driven search and discovery platforms. Follow the format and requirements specified in the user prompt exactly.`;
    aiJob = createJob('geo-optimization', item.companyId);
    const aiResponse = await generateWithAI(prompt, systemPrompt, 4000, 0.7, 'text', aiConfig.AI_PROVIDER);

    const generatedContent = aiResponse.content || '';

    const updated = await GEOOptimization.findByIdAndUpdate(
      req.params.id,
      {
        generatedContent,
        generatedTitle: extractTitle(generatedContent),
        generatedSummary: extractSummary(generatedContent),
        promptUsed: prompt,
        aiProvider: aiConfig.AI_PROVIDER,
        status: 'generated',
        updatedAt: new Date(),
      },
      { new: true }
    );

    // Track this generation in AiContext so it appears in the AI Jobs report
    try {
      const ctx = await aiContextService.create({
        companyId: item.companyId,
        moduleSource: 'geo-optimization',
        analysisType: 'full-analysis',
        inputs: { companyName: req.body?.brandName || '', description: item.title || item.contentType || 'GEO optimization' } as any,
        analysis: { generatedContent, contentType: item.contentType } as any,
        metadata: {
          pipelineVersion: 'v1',
          provider: aiResponse.provider || aiConfig.AI_PROVIDER,
          model: aiResponse.model,
          tokensUsed: aiResponse.tokenUsage?.totalTokens || 0,
          inputTokens: aiResponse.tokenUsage?.inputTokens || 0,
          outputTokens: aiResponse.tokenUsage?.outputTokens || 0,
          processingTimeMs: Date.now() - startTime,
          latencyMs: aiResponse.latencyMs ?? (Date.now() - startTime),
          overallConfidence: 85,
          fieldConfidences: {},
          finishReason: aiResponse.finishReason,
          apiKeyMasked: aiResponse.keyUsed,
        },
      });
      await aiContextService.updateStatus(ctx.id, 'approved');
    } catch (ctxErr: any) {
      console.warn(`[GEO-Optimization] AiContext save failed (non-fatal): ${ctxErr.message}`);
    }

    // Only once the generated content is stored: completeJob raises the in-app
    // notification and the completion email, both subject to the user's AI
    // generation preferences.
    completeJob(aiJob.jobId, { title: updated?.generatedTitle || item.title || '' }, 'geo-optimization');

    res.json(updated);
  } catch (error) {
    // Failing the job keeps a broken generation off the success path — failJob
    // never sends a completion notification or email.
    if (aiJob) failJob(aiJob.jobId, error instanceof Error ? error.message : String(error));
    handleError(res, error);
  }
});

// ─── Prompt Builders ─────────────────────────────────────────────────────────

function buildGeoPrompt(item: any, overrides: any = {}): string {
  const { contentType, targetQuestion, targetKeyword, targetDirectory, title, description } = item;
  const { brandName, brandTone, additionalContext } = overrides;

  const baseContext = `Brand: ${brandName || 'Our brand'}. Tone: ${brandTone || 'professional and helpful'}.`;

  switch (contentType) {
    case 'quora-answer':
      return `${baseContext}
Write a comprehensive, helpful Quora answer to the following question:
"${targetQuestion || title}"

Requirements:
- Provide genuine value — not promotional
- Include specific examples or data where possible
- 300–600 words
- End with a subtle, relevant mention of the brand only if it truly adds value
- Use paragraphs, bullet points, and clear structure

${additionalContext || ''}`;

    case 'ai-overview':
      return `${baseContext}
Write content optimised for Google's AI Overview feature for the keyword: "${targetKeyword || title}"

Requirements:
- Provide a clear, direct answer in the first 2–3 sentences
- Expand with supporting details, facts, and context
- Use structured data-friendly formatting (headings, lists, tables)
- Include FAQ-style questions and answers
- 400–800 words
- Optimise for featured snippet capture

${additionalContext || ''}`;

    case 'people-also-ask':
      return `${baseContext}
Generate People Also Ask (PAA) content for the keyword: "${targetKeyword || title}"

Requirements:
- Create 5–8 related questions that users commonly ask
- Provide concise, direct answers (2–4 sentences each)
- Follow with a brief elaboration paragraph
- Include a short summary paragraph that ties all answers together
- Format as Q&A pairs with clear headings

${additionalContext || ''}`;

    case 'chatgpt-submission':
      return `${baseContext}
Create content formatted for ChatGPT / AI citation optimisation:
"${title}"

Requirements:
- Clear, factual, well-sourced content
- Include entity definitions and structured data
- Use schema.org-friendly language
- Provide both a short summary (50 words) and full article (300–500 words)
- Include key facts, statistics, and authoritative sources
- Format with clear headings and bullet points

${additionalContext || ''}`;

    case 'directory-submission':
      return `${baseContext}
Write a directory listing / business profile submission for: ${targetDirectory || 'general business directories'}

Requirements:
- Short business description (50–100 words)
- Longer detailed description (200–300 words)
- Key services or products offered
- Contact information and location details
- Unique selling propositions
- Call to action
- SEO-friendly with natural keyword usage

${additionalContext || ''}`;

    default:
      return `${baseContext}\nGenerate GEO-optimised content for: ${title}\n${description || ''}`;
  }
}

function extractTitle(content: string): string {
  const lines = content.split('\n').filter((l) => l.trim());
  const firstHeading = lines.find((l) => l.startsWith('#'));
  if (firstHeading) {
    return firstHeading.replace(/^#+\s*/, '').trim();
  }
  return lines[0]?.substring(0, 100) || '';
}

function extractSummary(content: string): string {
  const paragraphs = content.split('\n\n').filter((p) => p.trim());
  const firstPara = paragraphs.find((p) => !p.startsWith('#') && p.length > 50);
  return firstPara ? firstPara.substring(0, 300) : '';
}

export default router;
