import { Router, Request, Response } from 'express';
import type { Router as RouterType } from 'express';
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';

export const imagesRouter: RouterType = Router();

/**
 * GET /api/images/:orgSlug/:projectSlug/img/:imageId
 * Serves images directly (streaming approach)
 * New format: {orgSlug}/{projectSlug}/img/{imageId}
 */
imagesRouter.get(
  '/images/:orgSlug/:projectSlug/img/:imageId',
  asyncHandler(async (req: Request, res: Response): Promise<void> => {
    const { orgSlug, projectSlug, imageId } = req.params;

    // Validate required params (these are guaranteed by route pattern)
    if (!orgSlug || !projectSlug || !imageId) {
      res.status(400).json({
        success: false,
        message: 'Missing required parameters',
      });
      return;
    }

    const storageService = await StorageFactory.getInstance();

    try {
      // Check if file exists first (fast check)
      const exists = await storageService.fileExists(orgSlug, projectSlug, imageId);

      if (!exists) {
        res.status(404).json({
          success: false,
          message: 'File not found',
        });
        return;
      }

      // Set headers for optimal caching and performance
      // Note: Content-Type will be set from MinIO metadata
      res.setHeader('Cache-Control', 'public, max-age=31536000, immutable'); // 1 year + immutable
      res.setHeader('ETag', `"${imageId}"`); // UUID as ETag

      // Handle conditional requests (304 Not Modified)
      const ifNoneMatch = req.headers['if-none-match'];
      if (ifNoneMatch === `"${imageId}"`) {
        res.status(304).end(); // Not Modified
        return;
      }

      // Stream the file directly through our API (memory efficient)
      const fileStream = await storageService.streamFile(orgSlug, projectSlug, imageId);

      // Handle stream errors
      fileStream.on('error', (streamError) => {
        console.error('Stream error:', streamError);
        if (!res.headersSent) {
          res.status(500).json({
            success: false,
            message: 'Error streaming file',
          });
        }
      });

      // Stream the file without loading into memory
      fileStream.pipe(res);
    } catch (error) {
      console.error('Failed to stream file:', error);
      res.status(404).json({
        success: false,
        message: 'File not found',
      });
    }
  }),
);

/**
 * GET /api/images/url/:orgSlug/:projectSlug/img/:imageId
 * Returns a presigned URL instead of redirecting
 */
imagesRouter.get(
  '/images/url/:orgSlug/:projectSlug/img/:imageId',
  asyncHandler(async (req: Request, res: Response): Promise<void> => {
    const { orgSlug, projectSlug, imageId } = req.params;
    const { expiry = '3600' } = req.query; // Default 1 hour

    // Validate required params (these are guaranteed by route pattern)
    if (!orgSlug || !projectSlug || !imageId) {
      res.status(400).json({
        success: false,
        message: 'Missing required parameters',
      });
      return;
    }

    const storageService = await StorageFactory.getInstance();

    try {
      const presignedUrl = await storageService.getPresignedDownloadUrl(
        orgSlug,
        projectSlug,
        imageId,
        parseInt(expiry as string, 10),
      );

      res.json({
        success: true,
        url: presignedUrl,
        expiresIn: parseInt(expiry as string, 10),
      });
    } catch (error) {
      console.error('Failed to generate presigned URL:', error);
      res.status(404).json({
        success: false,
        message: 'File not found or access denied',
      });
    }
  }),
);

/**
 * GET /api/images/generated
 * List generated images for the authenticated project
 */
imagesRouter.get(
  '/images/generated',
  validateApiKey,
  requireProjectKey,
  rateLimitByApiKey,
  asyncHandler(async (req: any, res: Response) => {
    const timestamp = new Date().toISOString();
    const requestId = req.requestId;

    // Parse and validate query parameters
    const limit = Math.min(Math.max(parseInt(req.query.limit as string, 10) || 30, 1), 100);
    const offset = Math.max(parseInt(req.query.offset as string, 10) || 0, 0);
    const prefix = (req.query.prefix as string) || undefined;

    // Validate query parameters
    if (isNaN(limit) || isNaN(offset)) {
      res.status(400).json({
        success: false,
        message: 'Invalid query parameters',
        error: 'limit and offset must be valid numbers',
      });
      return;
    }

    // Extract org/project from validated API key
    const orgSlug = req.apiKey?.organizationSlug || 'default';
    const projectSlug = req.apiKey?.projectSlug!;

    console.log(
      `[${timestamp}] [${requestId}] Listing images for org:${orgSlug}, project:${projectSlug}, limit:${limit}, offset:${offset}, prefix:${prefix || 'none'}`,
    );

    try {
      // Get storage service instance
      const storageService = await StorageFactory.getInstance();

      // List files in img folder
      const allFiles = await storageService.listFiles(orgSlug, projectSlug, prefix);

      // Sort by lastModified descending (newest first)
      allFiles.sort((a, b) => {
        const dateA = a.lastModified ? new Date(a.lastModified).getTime() : 0;
        const dateB = b.lastModified ? new Date(b.lastModified).getTime() : 0;
        return dateB - dateA;
      });

      // Apply pagination
      const total = allFiles.length;
      const paginatedFiles = allFiles.slice(offset, offset + limit);

      // Map to response format with public URLs
      const images = paginatedFiles.map((file) => ({
        imageId: file.filename,
        url: storageService.getPublicUrl(orgSlug, projectSlug, file.filename),
        size: file.size,
        contentType: file.contentType,
        lastModified: file.lastModified ? file.lastModified.toISOString() : new Date().toISOString(),
      }));

      const hasMore = offset + limit < total;

      console.log(
        `[${timestamp}] [${requestId}] Successfully listed ${images.length} of ${total} images`,
      );

      return res.status(200).json({
        success: true,
        data: {
          images,
          total,
          offset,
          limit,
          hasMore,
        },
      });
    } catch (error) {
      console.error(`[${timestamp}] [${requestId}] Failed to list images:`, error);

      return res.status(500).json({
        success: false,
        message: 'Failed to list images',
        error: error instanceof Error ? error.message : 'Unknown error occurred',
      });
    }
  }),
);
