import { Response, Router } from 'express';
import type { Router as RouterType } from 'express';
import { PromptCacheService, GenerationService, ImageService } from '@/services/core';
import { StorageFactory } from '@/services/StorageFactory';
import { asyncHandler } from '@/middleware/errorHandler';
import { validateApiKey } from '@/middleware/auth/validateApiKey';
import { requireProjectKey } from '@/middleware/auth/requireProjectKey';
import { rateLimitByApiKey } from '@/middleware/auth/rateLimiter';
import { GENERATION_LIMITS } from '@/utils/constants';

export const liveRouter: RouterType = Router();

let promptCacheService: PromptCacheService;
let generationService: GenerationService;
let imageService: ImageService;

const getPromptCacheService = (): PromptCacheService => {
  if (!promptCacheService) {
    promptCacheService = new PromptCacheService();
  }
  return promptCacheService;
};

const getGenerationService = (): GenerationService => {
  if (!generationService) {
    generationService = new GenerationService();
  }
  return generationService;
};

const getImageService = (): ImageService => {
  if (!imageService) {
    imageService = new ImageService();
  }
  return imageService;
};

/**
 * GET /api/v1/live
 * Generate image with prompt caching
 * Returns image bytes directly with cache headers
 */
liveRouter.get(
  '/',
  validateApiKey,
  requireProjectKey,
  rateLimitByApiKey,
  asyncHandler(async (req: any, res: Response) => {
    const cacheService = getPromptCacheService();
    const genService = getGenerationService();
    const imgService = getImageService();
    const { prompt, aspectRatio } = req.query;

    // Validate prompt
    if (!prompt || typeof prompt !== 'string') {
      res.status(400).json({
        success: false,
        error: {
          message: 'Prompt is required and must be a string',
          code: 'VALIDATION_ERROR',
        },
      });
      return;
    }

    const projectId = req.apiKey.projectId;
    const apiKeyId = req.apiKey.id;
    const organizationSlug = req.apiKey.organizationSlug || process.env['DEFAULT_ORG_SLUG'] || 'default';
    const projectSlug = req.apiKey.projectSlug || process.env['DEFAULT_PROJECT_SLUG'] || 'main';

    try {
      // Compute prompt hash for cache lookup
      const promptHash = cacheService.computePromptHash(prompt);

      // Check cache
      const cachedEntry = await cacheService.getCachedEntry(promptHash, projectId);

      if (cachedEntry) {
        // Cache HIT - fetch and stream existing image
        await cacheService.recordCacheHit(cachedEntry.id);

        // Get image from database
        const image = await imgService.getById(cachedEntry.imageId);
        if (!image) {
          throw new Error('Cached image not found in database');
        }

        const storageService = await StorageFactory.getInstance();

        // Parse storage key to get components
        // Format: {orgSlug}/{projectSlug}/img/{imageId}
        const keyParts = image.storageKey.split('/');
        if (keyParts.length < 4 || keyParts[2] !== 'img') {
          throw new Error('Invalid storage key format');
        }

        const storedOrgSlug = keyParts[0]!;
        const storedProjectSlug = keyParts[1]!;
        const imageId = keyParts[3]!;

        // Download image from storage
        const buffer = await storageService.downloadFile(
          storedOrgSlug,
          storedProjectSlug,
          imageId
        );

        // Set cache headers
        res.setHeader('Content-Type', image.mimeType);
        res.setHeader('Content-Length', buffer.length);
        res.setHeader('Cache-Control', 'public, max-age=31536000'); // 1 year
        res.setHeader('X-Cache-Status', 'HIT');
        res.setHeader('X-Cache-Hit-Count', cachedEntry.hitCount.toString());
        res.setHeader('X-Image-Id', image.id);

        // Stream image bytes
        res.send(buffer);
        return;
      }

      // Cache MISS - generate new image
      const generation = await genService.create({
        projectId,
        apiKeyId,
        organizationSlug,
        projectSlug,
        prompt,
        aspectRatio: (aspectRatio as string) || GENERATION_LIMITS.DEFAULT_ASPECT_RATIO,
        requestId: req.requestId,
      });

      // Get the output image
      if (!generation.outputImage) {
        throw new Error('Generation succeeded but no output image was created');
      }

      // Create cache entry
      const queryParamsHash = cacheService.computePromptHash(
        JSON.stringify({ aspectRatio: aspectRatio || GENERATION_LIMITS.DEFAULT_ASPECT_RATIO })
      );

      await cacheService.createCacheEntry({
        projectId,
        generationId: generation.id,
        imageId: generation.outputImage.id,
        promptHash,
        queryParamsHash,
        originalPrompt: prompt,
        requestParams: {
          aspectRatio: aspectRatio || GENERATION_LIMITS.DEFAULT_ASPECT_RATIO,
        },
        hitCount: 0,
      });

      // Download newly generated image
      const storageService = await StorageFactory.getInstance();

      // Format: {orgSlug}/{projectSlug}/img/{imageId}
      const keyParts = generation.outputImage.storageKey.split('/');
      if (keyParts.length < 4 || keyParts[2] !== 'img') {
        throw new Error('Invalid storage key format');
      }

      const storedOrgSlug = keyParts[0]!;
      const storedProjectSlug = keyParts[1]!;
      const imageId = keyParts[3]!;

      const buffer = await storageService.downloadFile(
        storedOrgSlug,
        storedProjectSlug,
        imageId
      );

      // Set cache headers
      res.setHeader('Content-Type', generation.outputImage.mimeType);
      res.setHeader('Content-Length', buffer.length);
      res.setHeader('Cache-Control', 'public, max-age=31536000'); // 1 year
      res.setHeader('X-Cache-Status', 'MISS');
      res.setHeader('X-Generation-Id', generation.id);
      res.setHeader('X-Image-Id', generation.outputImage.id);

      // Stream image bytes
      res.send(buffer);
      return;
    } catch (error) {
      console.error('Live generation error:', error);
      res.status(500).json({
        success: false,
        error: {
          message: error instanceof Error ? error.message : 'Generation failed',
          code: 'GENERATION_ERROR',
        },
      });
      return;
    }
  })
);
