/**
 * Competitor AI Context Routes
 *
 * API endpoints for Competitor AI generation and auto-fill.
 * POST /auto-fill — generate competitor from company context + ICP data
 * POST /regenerate/:competitorId — regenerate an existing competitor
 * GET /status/:jobId — poll for async job status
 *
 * All long-running AI generation is processed asynchronously to avoid
 * 504 Gateway Timeout errors from reverse proxies.
 */

import express, { Request, Response } from 'express';
import { body, param, validationResult } from 'express-validator';
import { authenticate } from '../middleware/auth';
import { requirePermission } from '../middleware/permissions';
import { CompetitorPipeline } from '../services/aiContext/competitorPipeline';
import { CompetitorPipelineInputs } from '../services/aiContext/competitorPrompts';
import { aiContextService, computeCompetitorAutoFillMapping } from '../services/aiContext/aiContextService';
import { createJob, updateJobProgress, completeJob, failJob, getJob } from '../services/aiContext/aiJobManager';
import { getModels } from '../models';

const router = express.Router();
router.use(authenticate);

// ============================================
// GET /status/:jobId — poll for async job result
// ============================================

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
// Generate competitor data from company context + ICP
// ============================================

router.post(
  '/auto-fill',
  requirePermission('competitors', '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 } = req.body;

    try {
      // Start async generation
      const job = createJob('competitor', 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 {
          const { Company, BusinessProfile, ICP } = 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 {}

          const pipelineInputs: CompetitorPipelineInputs = {
            companyName: company.name,
            companyDescription: company.description || businessProfile?.description || undefined,
            companyIndustry: businessProfile?.primaryIndustry || undefined,
            companyBusinessModel: businessProfile?.businessModel || undefined,
            companyTargetAudience: undefined,
            companyPrimaryOffering: undefined,
            companyUsps: 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;
          }

          updateJobProgress(job.jobId, 5, 'Researching competitors on the web...');
          const pipeline = new CompetitorPipeline(pipelineInputs);
          updateJobProgress(job.jobId, 10, 'Gathering company and ICP context...');
          updateJobProgress(job.jobId, 20, 'Generating competitor profile with AI...');
          const result = await pipeline.run();
          updateJobProgress(job.jobId, 80, 'Saving competitor data...');

          // Tracking record only — a failure here must not discard a generation
          // the AI has already completed (an out-of-range confidence score used
          // to throw right here and fail the whole job).
          try {
            const context = await aiContextService.create({
              companyId,
              moduleSource: 'competitor',
              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,
                webResearchPerformed: !!result.webResearchData,
                webResearchWebsite: result.webResearchData?.detectedWebsite || null,
              },
            });
            await aiContextService.updateStatus(context.id, 'approved');
          } catch (contextErr: any) {
            console.warn(`[Competitor-AutoFill] Job ${job.jobId}: AiContext tracking record failed (generation kept):`, contextErr.message);
          }

          const autoFillData = computeCompetitorAutoFillMapping(result.analysis);
          if (result.webResearchData?.detectedWebsite) {
            (autoFillData as any)._meta = {
              websiteSource: 'web_research',
              websiteDetected: result.webResearchData.detectedWebsite,
            };
          }
          completeJob(job.jobId, autoFillData, 'generated');

          console.log(`[Competitor-AutoFill] Job ${job.jobId} completed. Source: generated`);
        } catch (err: any) {
          console.error(`[Competitor-AutoFill] Job ${job.jobId} failed:`, err.message);
          failJob(job.jobId, err.message || 'AI generation failed');
        }
      });
    } catch (error: any) {
      console.error('[AI-Context/Competitor] Auto-fill error:', error);
      res.status(500).json({ error: 'Competitor auto-fill failed', message: error.message });
    }
  }
);

// ============================================
// POST /quick-generate
// Generate competitor data from a short description seed (async)
// ============================================

router.post(
  '/quick-generate',
  requirePermission('competitors', 'ai-generate'),
  [
    body('companyId').notEmpty().withMessage('Company ID is required'),
    body('shortDescription').notEmpty().withMessage('Short description is required'),
    // Targeting filters from the Generate with AI dialog. All optional, so a
    // request that sends none behaves exactly as it did before.
    body('generateBasedOn').optional().isIn(['business', 'product', 'both']).withMessage('Invalid generation basis'),
    body('productIds').optional().isArray().withMessage('productIds must be an array'),
    body('countries').optional().isArray().withMessage('countries must be an array'),
    body('states').optional().isArray().withMessage('states must be an array'),
    body('cities').optional().isArray().withMessage('cities must be an array'),
    body('industries').optional().isArray().withMessage('industries must be an array'),
    body('competitorCount').optional().isInt({ min: 1, max: 10 }).withMessage('Competitor count must be between 1 and 10'),
  ],
  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, shortDescription } = req.body;
    // Targeting filters. Normalised here so the pipeline only ever sees clean,
    // non-empty arrays and `undefined` when the user picked nothing.
    const asNames = (v: unknown): string[] | undefined => {
      if (!Array.isArray(v)) return undefined;
      const out = v.map((x) => String(x ?? '').trim()).filter(Boolean);
      return out.length ? out : undefined;
    };
    const generateBasedOn: 'business' | 'product' | 'both' | undefined = req.body.generateBasedOn;
    const productIds = asNames(req.body.productIds);
    const targetCountries = asNames(req.body.countries);
    const targetStates = asNames(req.body.states);
    const targetCities = asNames(req.body.cities);
    const targetIndustries = asNames(req.body.industries);
    const competitorCount = Math.min(Math.max(parseInt(String(req.body.competitorCount ?? 1), 10) || 1, 1), 10);

    try {
      const job = createJob('competitor', companyId, req.body._moduleId);
      res.status(202).json({ jobId: job.jobId, status: 'processing' });

      setImmediate(async () => {
        try {
          const { Company, BusinessProfile, ICP } = 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 {}

          const pipelineInputs: CompetitorPipelineInputs = {
            companyName: company.name,
            companyDescription: company.description || businessProfile?.description || undefined,
            companyIndustry: businessProfile?.primaryIndustry || undefined,
            companyBusinessModel: businessProfile?.businessModel || undefined,
            companyTargetAudience: undefined,
            companyPrimaryOffering: undefined,
            companyUsps: undefined,
            shortDescription,
          };

          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;
          }

          // Products the user picked in the dialog, resolved to the context the
          // prompt actually needs. Only loaded when the generation is product-based.
          if (productIds?.length && (generateBasedOn === 'product' || generateBasedOn === 'both')) {
            try {
              const { Product } = getModels();
              const picked = await Product.find({ _id: { $in: productIds }, companyId })
                .select('name description usp')
                .lean();
              const targetProducts = (picked as any[])
                .map((p) => ({ name: p?.name, description: p?.description, usp: p?.usp }))
                .filter((p) => p.name);
              if (targetProducts.length) pipelineInputs.targetProducts = targetProducts;
            } catch (productErr: any) {
              // A lookup failure must not abort the generation — it just runs
              // without product context.
              console.warn(`[Competitor-QuickGenerate] Job ${job.jobId}: product lookup failed:`, productErr.message);
            }
          }
          pipelineInputs.generateBasedOn = generateBasedOn;
          pipelineInputs.targetCountries = targetCountries;
          pipelineInputs.targetStates = targetStates;
          pipelineInputs.targetCities = targetCities;
          pipelineInputs.targetIndustries = targetIndustries;

          // One job may now produce several competitors. Runs sequentially and
          // feeds the names produced so far back into the prompt so each pass
          // returns a distinct company.
          const generated: any[] = [];
          const generatedNames: string[] = [];
          let result!: Awaited<ReturnType<CompetitorPipeline['run']>>;

          for (let n = 0; n < competitorCount; n++) {
            const base = Math.round((n / competitorCount) * 75);
            updateJobProgress(
              job.jobId,
              base + 5,
              competitorCount > 1
                ? `Researching competitor ${n + 1} of ${competitorCount}...`
                : 'Researching competitors on the web...',
            );
            const pipeline = new CompetitorPipeline({
              ...pipelineInputs,
              alreadyGeneratedNames: generatedNames.length ? [...generatedNames] : undefined,
            });
            updateJobProgress(
              job.jobId,
              base + 15,
              competitorCount > 1
                ? `Generating competitor ${n + 1} of ${competitorCount} with AI...`
                : 'Generating competitor profile with AI...',
            );
            result = await pipeline.run();

            const mapped = computeCompetitorAutoFillMapping(result.analysis);
            if (result.webResearchData?.detectedWebsite) {
              (mapped as any)._meta = {
                websiteSource: 'web_research',
                websiteDetected: result.webResearchData.detectedWebsite,
              };
            }
            generated.push(mapped);
            const producedName = (mapped as any)?.name;
            if (typeof producedName === 'string' && producedName.trim()) {
              generatedNames.push(producedName.trim());
            }
          }

          updateJobProgress(job.jobId, 80, 'Saving competitor data...');

          // Tracking record only — see the note in /auto-fill above.
          try {
            const context = await aiContextService.create({
              companyId,
              moduleSource: 'competitor',
              analysisType: 'full-analysis',
              inputs: { companyName: company.name, description: shortDescription },
              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,
                webResearchPerformed: !!result.webResearchData,
                webResearchWebsite: result.webResearchData?.detectedWebsite || null,
              },
            });
            await aiContextService.updateStatus(context.id, 'approved');
          } catch (contextErr: any) {
            console.warn(`[Competitor-QuickGenerate] Job ${job.jobId}: AiContext tracking record failed (generation kept):`, contextErr.message);
          }

          // A single competitor still completes with the bare object, so the
          // existing apply path is byte-identical for the unfiltered case. Only a
          // multi-competitor run returns the array, which the frontend routes to
          // its list handler.
          const autoFillData = competitorCount > 1 ? generated : generated[0];
          completeJob(job.jobId, autoFillData as any, 'generated');

          console.log(`[Competitor-QuickGenerate] Job ${job.jobId} completed. Generated: ${generated.length}`);
        } catch (err: any) {
          console.error(`[Competitor-QuickGenerate] Job ${job.jobId} failed:`, err.message);
          failJob(job.jobId, err.message || 'AI generation failed');
        }
      });
    } catch (error: any) {
      console.error('[AI-Context/Competitor] Quick-generate error:', error);
      res.status(500).json({ error: 'Competitor quick-generate failed', message: error.message });
    }
  }
);

// ============================================
// POST /regenerate/:competitorId
// Re-run competitor pipeline for an existing competitor (async)
// ============================================

router.post(
  '/regenerate/:competitorId',
  requirePermission('competitors', 'ai-generate'),
  [
    param('competitorId').notEmpty().withMessage('Competitor ID is required'),
    body('companyId').notEmpty().withMessage('Company ID is required'),
    // Optional free-text instructions from the Regenerate dialog. Bounded to
    // match the ICP module's 2000-character prompt box.
    body('customInstructions')
      .optional()
      .isLength({ max: 2000 })
      .withMessage('Additional instructions must be 2000 characters or fewer'),
  ],
  async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ error: 'Validation failed', details: errors.array() });
      return;
    }

    const { competitorId } = req.params;
    const { companyId } = req.body;
    const customInstructions = req.body.customInstructions || undefined;

    // Create job and return immediately
    const job = createJob('competitor', companyId, req.body._moduleId);
    res.status(202).json({ jobId: job.jobId, status: 'processing' });

    // Run pipeline in background
    setImmediate(async () => {
      try {
        const { Competitor, Company, BusinessProfile, ICP } = getModels();
        const existingCompetitor = await Competitor.findById(competitorId);
        if (!existingCompetitor) {
          failJob(job.jobId, 'Competitor not found');
          return;
        }

        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 {}

        // Same selection as /auto-fill and /quick-generate. Matching on
        // `status === 'approved'` alone meant a company whose context had never
        // been approved regenerated with no industry, audience, offering or USP
        // context at all — the regenerated profile was correspondingly generic.
        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 {}

        const pipelineInputs: CompetitorPipelineInputs = {
          companyName: company.name,
          companyDescription: company.description || businessProfile?.description || undefined,
          companyIndustry: businessProfile?.primaryIndustry || undefined,
          companyBusinessModel: businessProfile?.businessModel || undefined,
          companyTargetAudience: undefined,
          companyPrimaryOffering: undefined,
          companyUsps: undefined,
          existingCompetitorName: (existingCompetitor as any).name || undefined,
          existingCompetitorType: (existingCompetitor as any).competitorType || undefined,
          existingCompetitorWebsite: (existingCompetitor as any).website || undefined,
          customInstructions,
        };

        if (latestCompanyContext) {
          const analysis = (latestCompanyContext as any).analysis?.toObject?.() || (latestCompanyContext as any).analysis || {};
          // industry/business model were missing here but present in the other two
          // routes, so regenerate sent a weaker prompt than the original generation.
          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;
        }

        updateJobProgress(job.jobId, 5, 'Researching competitors on the web...');
        const pipeline = new CompetitorPipeline(pipelineInputs);
        updateJobProgress(job.jobId, 10, 'Gathering company and ICP context...');
        updateJobProgress(job.jobId, 20, 'Regenerating competitor profile with AI...');
        const result = await pipeline.run();
        updateJobProgress(job.jobId, 80, 'Saving regenerated data...');

        const autoFillData = computeCompetitorAutoFillMapping(result.analysis);
        if (result.webResearchData?.detectedWebsite) {
          (autoFillData as any)._meta = {
            websiteSource: 'web_research',
            websiteDetected: result.webResearchData.detectedWebsite,
          };
        }

        // Tracking record only — see the note in /auto-fill above.
        try {
          const context = await aiContextService.create({
            companyId,
            moduleSource: 'competitor',
            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,
              webResearchPerformed: !!result.webResearchData,
              webResearchWebsite: result.webResearchData?.detectedWebsite || null,
            },
          });
          (context as any).entityId = competitorId;
          await context.save();
          await aiContextService.updateStatus(context.id, 'approved');
        } catch (contextErr: any) {
          console.warn(`[Competitor-Regenerate] Job ${job.jobId}: AiContext tracking record failed (regeneration kept):`, contextErr.message);
        }

        completeJob(job.jobId, autoFillData, 'regenerated');
        console.log(`[Competitor-Regenerate] Job ${job.jobId} completed. Source: regenerated`);
      } catch (err: any) {
        console.error(`[Competitor-Regenerate] Job ${job.jobId} failed:`, err.message);
        failJob(job.jobId, err.message || 'AI regeneration failed');
      }
    });
  }
);

export default router;