/**
 * Stateless AI Image Generation Route
 *
 * POST /api/ai/generate-image-raw
 *
 * Generates an image using any configured provider and returns the base64 string directly.
 * NO Mongoose. NO database. NO .save() calls.
 *
 * This bypasses the stateful ImageGeneration model entirely,
 * avoiding Mongoose enum validation errors for social media images.
 *
 * Supported models/providers: gpt-image-2, gpt-image-1, dall-e-3 (OpenAI),
 * cogview-3 (Zhipu), flux (FLUX), midjourney (Midjourney),
 * ideogram-v3 (Ideogram), nano-v1 (Nano)
 */

import express, { Request, Response } from 'express';
import fs from 'fs';
import path from 'path';
import { body, validationResult } from 'express-validator';
import { authenticate } from '../middleware/auth';
import { requirePermission } from '../middleware/permissions';
import { asyncHandler } from '../middleware/errorHandler';
import { getImageProviderForModel, IMAGE_PROVIDER_NAMES, type ImageProvider } from '../utils/aiProvider';
import {
  generateImageForProvider,
  generateImageWithZhipuCogView,
} from './imageGenerations';
// Shared image-to-image mechanism (OpenAI /images/edits) already used by the
// brand asset flows — reused here so reference-image generation has one owner.
import { generateBrandImageEditWithOpenAI } from './aiContextBrandAsset';

const router = express.Router();

router.use(authenticate);

// ============================================
// Aspect Ratio Mappings
// ============================================

const ASPECT_RATIO_SIZE_MAP: Record<string, string> = {
  '1:1': '1024x1024',
  '16:9': '1536x1024',
  '9:16': '1024x1536',
  '4:3': '1024x768',
  '3:2': '1024x683',
  '3:4': '768x1024',
  '2:3': '683x1024',
};

// ============================================
// Reference image loading
// ============================================

const MIME_BY_EXTENSION: Record<string, string> = {
  '.jpg': 'image/jpeg',
  '.jpeg': 'image/jpeg',
  '.webp': 'image/webp',
  '.gif': 'image/gif',
};

/**
 * Load the bytes of an optional reference image so it can be sent to the
 * image-to-image endpoint. Accepts a data URI, an uploaded path served by this
 * server (/uploads/...), or an absolute http(s) URL. Returns null when the
 * image cannot be read — generation then falls back to text-to-image rather
 * than failing, because the reference is always optional.
 */
async function readReferenceImage(
  source: string
): Promise<{ buffer: Buffer; mimeType: string } | null> {
  try {
    const dataUriMatch = source.match(/^data:([^;]+);base64,(.+)$/s);
    if (dataUriMatch) {
      return { buffer: Buffer.from(dataUriMatch[2], 'base64'), mimeType: dataUriMatch[1] };
    }

    if (/^https?:\/\//i.test(source)) {
      const response = await fetch(source);
      if (!response.ok) throw new Error(`HTTP ${response.status}`);
      const arrayBuffer = await response.arrayBuffer();
      const mimeType = response.headers.get('content-type') || 'image/png';
      return { buffer: Buffer.from(arrayBuffer), mimeType };
    }

    // Server-relative upload path, e.g. /uploads/<file>.png — resolve from cwd.
    const filePath = path.resolve(process.cwd(), source.replace(/^\//, ''));
    if (!filePath.startsWith(path.resolve(process.cwd(), 'uploads'))) {
      console.warn('[ImageGen-Raw] Reference image path outside uploads, ignoring:', source);
      return null;
    }
    if (!fs.existsSync(filePath)) {
      console.warn('[ImageGen-Raw] Reference image not found on disk:', filePath);
      return null;
    }
    const buffer = await fs.promises.readFile(filePath);
    const ext = path.extname(filePath).toLowerCase();
    return { buffer, mimeType: MIME_BY_EXTENSION[ext] || 'image/png' };
  } catch (e: any) {
    console.warn('[ImageGen-Raw] Could not load reference image (non-fatal):', e.message);
    return null;
  }
}

// ============================================
// POST /generate-image-raw — Stateless image generation
// ============================================

router.post(
  '/generate-image-raw',
  requirePermission('ai-processing', 'ai-generate'),
  [
    body('prompt').trim().notEmpty().withMessage('Prompt is required'),
    body('companyId').optional().isString(),
    body('enrichPrompt').optional().isBoolean(),
    body('referenceImageUrl').optional().isString(),
  ],
  asyncHandler(async (req: Request, res: Response) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
      res.status(400).json({ success: false, error: 'Validation failed', details: errors.array() });
      return;
    }

    const {
      prompt,
      size,
      quality = 'standard',
      style = 'vivid',
      model = 'gpt-image-1',
      aspectRatio = '1:1',
      companyId,
      enrichPrompt,
      referenceImageUrl,
    } = req.body;

    // Determine which provider to use based on model name
    const provider: ImageProvider = getImageProviderForModel(model);

    // Map aspect ratio to size string
    const imageSize = size || ASPECT_RATIO_SIZE_MAP[aspectRatio] || '1024x1024';

    // Harmony: Enrich image prompt with brand visual context when companyId is provided
    let enrichedPrompt = prompt;
    if (companyId && enrichPrompt) {
      try {
        const { buildHarmonyContext, buildHarmonyImageContextBlock } = require('../services/aiContext/harmonyContextService');
        const harmonyCtx = await buildHarmonyContext(companyId);
        const imageCtx = buildHarmonyImageContextBlock(harmonyCtx);
        if (imageCtx) {
          enrichedPrompt = `${imageCtx}\n\n${prompt}`;
          console.log(`[ImageGen-Raw] Harmony image context enrichment applied for company ${companyId}`);
        }
      } catch (e: any) {
        console.warn(`[ImageGen-Raw] Harmony context enrichment failed (non-fatal): ${e.message}`);
      }
    }

    console.log(`[ImageGen-Raw] Generating image | provider=${IMAGE_PROVIDER_NAMES[provider] || provider} | model=${model} | prompt length=${enrichedPrompt.length} | size=${imageSize}${companyId ? ' | companyId=' + companyId : ''}`);

    // Generate image via the correct provider
    let result: { base64Data: string; revisedPrompt: string; model: string; provider: string; tokenUsage?: any; latencyMs?: number } | null = null;
    let lastError: string = '';

    // When a reference image is supplied, generate image-to-image so the result
    // stays visually tied to it (e.g. a founder's uploaded photo). A failed or
    // unreadable reference falls through to normal text-to-image generation
    // below — the reference is an enhancement, never a hard requirement.
    let referenceImage: { buffer: Buffer; mimeType: string } | null = null;
    if (typeof referenceImageUrl === 'string' && referenceImageUrl.trim()) {
      referenceImage = await readReferenceImage(referenceImageUrl.trim());
      if (referenceImage) {
        try {
          console.log(`[ImageGen-Raw] Generating with reference image | mime=${referenceImage.mimeType}`);
          result = await generateBrandImageEditWithOpenAI(
            enrichedPrompt,
            referenceImage.buffer,
            referenceImage.mimeType,
            'ImageGen-Raw',
            req.user?._id?.toString()
          );
        } catch (editError: any) {
          console.warn(`[ImageGen-Raw] Reference-image edit failed, falling back to from-scratch: ${editError.message}`);
        }
      }
    }

    try {
      if (!result) {
        result = await generateImageForProvider(provider, model, enrichedPrompt, imageSize, quality, style);
      }
    } catch (primaryError: any) {
      lastError = primaryError.message;
      console.warn(`[ImageGen-Raw] ${IMAGE_PROVIDER_NAMES[provider] || provider} failed: ${primaryError.message}`);

      // Only attempt CogView-3 fallback when the primary provider is OpenAI
      if (provider === 'openai') {
        try {
          console.log('[ImageGen-Raw] Attempting Zhipu CogView-3 fallback...');
          result = await generateImageWithZhipuCogView(enrichedPrompt, imageSize);
          console.log(`[ImageGen-Raw] Zhipu CogView-3 fallback succeeded (model: ${result.model})`);
        } catch (cogviewError: any) {
          console.error(`[ImageGen-Raw] Zhipu CogView-3 fallback also failed: ${cogviewError.message}`);
          lastError = `${primaryError.message} | CogView fallback: ${cogviewError.message}`;
        }
      }
    }

    // Return the result — base64 only, no database, no Mongoose
    if (result) {
      res.json({
        success: true,
        base64Data: result.base64Data,
        revisedPrompt: result.revisedPrompt,
        model: result.model,
        provider: result.provider,
      });
    } else {
      res.status(502).json({
        success: false,
        error: 'Image generation failed',
        message: lastError || 'All providers failed to generate an image. Please try again.',
      });
    }
  })
);

export default router;