import { Router, Request, Response } 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 = Router();

/**
 * GET /api/images/:orgId/:projectId/:category/:filename
 * Serves images via presigned URLs (redirect approach)
 */
imagesRouter.get(
  '/images/:orgId/:projectId/:category/:filename',
  asyncHandler(async (req: Request, res: Response) => {
    const { orgId, projectId, category, filename } = req.params;

    // Validate category
    if (!['uploads', 'generated', 'references'].includes(category)) {
      return res.status(400).json({
        success: false,
        message: 'Invalid category',
      });
    }

    const storageService = await StorageFactory.getInstance();

    try {
      // Check if file exists first (fast check)
      const exists = await storageService.fileExists(
        orgId,
        projectId,
        category as 'uploads' | 'generated' | 'references',
        filename,
      );

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

      // Determine content type from filename
      const ext = filename.toLowerCase().split('.').pop();
      const contentType =
        {
          png: 'image/png',
          jpg: 'image/jpeg',
          jpeg: 'image/jpeg',
          gif: 'image/gif',
          webp: 'image/webp',
          svg: 'image/svg+xml',
        }[ext || ''] || 'application/octet-stream';

      // Set headers for optimal caching and performance
      res.setHeader('Content-Type', contentType);
      res.setHeader('Cache-Control', 'public, max-age=86400, immutable'); // 24 hours + immutable
      res.setHeader('ETag', `"${orgId}-${projectId}-${filename}"`); // Simple ETag

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

      // Stream the file directly through our API (memory efficient)
      const fileStream = await storageService.streamFile(
        orgId,
        projectId,
        category as 'uploads' | 'generated' | 'references',
        filename,
      );

      // 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);
      return res.status(404).json({
        success: false,
        message: 'File not found',
      });
    }
  }),
);

/**
 * GET /api/images/url/:orgId/:projectId/:category/:filename
 * Returns a presigned URL instead of redirecting
 */
imagesRouter.get(
  '/images/url/:orgId/:projectId/:category/:filename',
  asyncHandler(async (req: Request, res: Response) => {
    const { orgId, projectId, category, filename } = req.params;
    const { expiry = '3600' } = req.query; // Default 1 hour

    if (!['uploads', 'generated', 'references'].includes(category)) {
      return res.status(400).json({
        success: false,
        message: 'Invalid category',
      });
    }

    const storageService = await StorageFactory.getInstance();

    try {
      const presignedUrl = await storageService.getPresignedDownloadUrl(
        orgId,
        projectId,
        category as 'uploads' | 'generated' | 'references',
        filename,
        parseInt(expiry as string, 10),
      );

      return res.json({
        success: true,
        url: presignedUrl,
        expiresIn: parseInt(expiry as string, 10),
      });
    } catch (error) {
      console.error('Failed to generate presigned URL:', error);
      return 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)) {
      return res.status(400).json({
        success: false,
        message: 'Invalid query parameters',
        error: 'limit and offset must be valid numbers',
      });
    }

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

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

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

      // List files in generated category
      const allFiles = await storageService.listFiles(orgId, projectId, 'generated', 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) => ({
        filename: file.filename,
        url: storageService.getPublicUrl(orgId, projectId, 'generated', 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} generated images`,
      );

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

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