import { NextRequest, NextResponse } from 'next/server';
import { prisma } from '@/lib/db';
import { readFile } from 'fs/promises';
import { createReadStream } from 'fs';
import { existsSync, statSync } from 'fs';
import path from 'path';
import { getVideoThumbnail } from '@/lib/video-thumbnail';

// Conditionally import sharp - handle case where libvips is not installed
let sharp: any = null;
try {
  sharp = require('sharp');
} catch (error) {
  console.warn('Sharp library not available. Watermarking and image processing will be disabled.');
  console.warn('To enable image processing, install libvips: sudo apt-get install libvips-dev');
}

const WATERMARK_BUCKET_SIZE = 200;
const watermarkCache = new Map<string, Buffer>();

async function getWatermarkOverlay(baseWidth: number, baseHeight: number) {
  if (!sharp) {
    throw new Error('Sharp library not available');
  }
  
  const bucketWidth = Math.ceil(baseWidth / WATERMARK_BUCKET_SIZE) * WATERMARK_BUCKET_SIZE;
  const bucketHeight = Math.ceil(baseHeight / WATERMARK_BUCKET_SIZE) * WATERMARK_BUCKET_SIZE;
  const cacheKey = `${bucketWidth}x${bucketHeight}`;

  let cached: Buffer | undefined = watermarkCache.get(cacheKey);
  if (!cached) {
    const diagonal = Math.sqrt(bucketWidth * bucketWidth + bucketHeight * bucketHeight);
    const watermarkWidth = Math.round(diagonal * 1.05);
    const watermarkHeight = Math.round((watermarkWidth * 120) / 360);
    const watermarkSvg = `
      <svg width="${watermarkWidth}" height="${watermarkHeight}" viewBox="0 0 360 120" xmlns="http://www.w3.org/2000/svg">
        <defs>
          <linearGradient id="wmGradient" x1="0%" y1="0%" x2="0%" y2="100%">
            <stop offset="0%" stop-color="#1a3c7c" stop-opacity="0.45"/>
            <stop offset="100%" stop-color="#0a1e3f" stop-opacity="0.45"/>
          </linearGradient>
        </defs>
        <rect width="360" height="120" fill="url(#wmGradient)" rx="20" ry="20" />
        <g fill="rgba(255,255,255,0.65)">
          <text x="30" y="82" font-family="'Inter','Segoe UI',sans-serif" font-size="72" font-weight="700">J</text>
          <text x="144" y="82" font-family="'Inter','Segoe UI',sans-serif" font-size="72" font-weight="700">M</text>
          <g transform="translate(80,20) scale(0.7)">
            <polygon points="60,0 80,35 120,35 90,57 100,95 60,75 20,95 30,57 0,35 40,35"
              fill="none" stroke="rgba(255,255,255,0.7)" stroke-width="12" stroke-linejoin="round" />
          </g>
          <text x="214" y="50" font-family="'Inter','Segoe UI',sans-serif" font-size="24" font-weight="600">Jewish</text>
          <text x="214" y="78" font-family="'Inter','Segoe UI',sans-serif" font-size="24" font-weight="600">and Modern</text>
        </g>
      </svg>
    `;

    const rotationAngle = -Math.atan(bucketHeight / bucketWidth) * (180 / Math.PI);
    const newCached = await sharp(Buffer.from(watermarkSvg))
      .resize({
        width: watermarkWidth,
        height: watermarkHeight,
        fit: 'inside',
        withoutEnlargement: true,
      })
      .rotate(rotationAngle, { background: { r: 0, g: 0, b: 0, alpha: 0 } })
      .resize({
        width: bucketWidth,
        height: bucketHeight,
        fit: 'cover',
        position: 'centre',
      })
      .ensureAlpha()
      .toBuffer();

    watermarkCache.set(cacheKey, newCached);
    cached = newCached;
  }

  // cached is guaranteed to be defined here (either from cache or just created)
  return sharp(cached)
    .resize({
      width: baseWidth,
      height: baseHeight,
      fit: 'cover',
      position: 'centre',
    })
    .ensureAlpha()
    .toBuffer();
}

/**
 * Get content type from file extension
 */
function getContentType(filePath: string, mediaType?: string): string {
  const ext = path.extname(filePath).toLowerCase();
  
  // If media_type is video, prioritize video MIME types
  if (mediaType === 'video') {
    if (ext === '.mp4') return 'video/mp4';
    if (ext === '.webm') return 'video/webm';
    if (ext === '.mov') return 'video/quicktime';
    if (ext === '.avi') return 'video/x-msvideo';
    if (ext === '.mkv') return 'video/x-matroska';
    return 'video/mp4'; // default
  }
  
  // Image types
  if (ext === '.jpg' || ext === '.jpeg') return 'image/jpeg';
  if (ext === '.png') return 'image/png';
  if (ext === '.gif') return 'image/gif';
  if (ext === '.webp') return 'image/webp';
  if (ext === '.heic') return 'image/heic';
  if (ext === '.heif') return 'image/heif';
  return 'image/jpeg'; // default
}

/**
 * Handle HTTP range requests for video streaming
 */
function parseRange(range: string | null, fileSize: number): { start: number; end: number } | null {
  if (!range) return null;
  
  const parts = range.replace(/bytes=/, '').split('-');
  const start = parseInt(parts[0], 10);
  const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
  
  if (isNaN(start) || isNaN(end) || start > end || start < 0) {
    return null;
  }
  
  return { start, end };
}

export async function GET(
  request: NextRequest,
  { params }: { params: Promise<{ id: string }> }
) {
  try {
    const { id } = await params;
    const photoId = parseInt(id);
    
    if (isNaN(photoId)) {
      return new NextResponse('Invalid photo ID', { status: 400 });
    }

    const { searchParams } = new URL(request.url);
    const thumbnail = searchParams.get('thumbnail') === 'true';
    const watermark = searchParams.get('watermark') === 'true';

    // Fetch photo from database
    const photo = await prisma.photo.findUnique({
      where: { id: photoId },
      select: { path: true, filename: true, media_type: true },
    });

    if (!photo?.path) {
      return new NextResponse('Photo not found', { status: 404 });
    }

    const filePath = photo.path;
    const mediaType = photo.media_type || 'image';

    // Check if file exists
    if (!existsSync(filePath)) {
      console.error(`File not found: ${filePath}`);
      return new NextResponse('File not found on server', { status: 404 });
    }

    // Handle video thumbnail request
    if (thumbnail && mediaType === 'video') {
      // Check if video path is a URL (can't generate thumbnail for remote videos)
      const isVideoUrl = filePath.startsWith('http://') || filePath.startsWith('https://');
      
      if (isVideoUrl) {
        // For remote videos, we can't generate thumbnails locally
        // Return a placeholder
        console.warn(`Cannot generate thumbnail for remote video URL: ${filePath}`);
        try {
          if (!sharp) throw new Error('Sharp not available');
          const placeholderSvg = `
            <svg width="400" height="400" xmlns="http://www.w3.org/2000/svg">
              <rect width="400" height="400" fill="#e5e7eb"/>
              <circle cx="200" cy="200" r="40" fill="#6b7280" opacity="0.8"/>
              <polygon points="190,185 190,215 215,200" fill="white"/>
            </svg>
          `.trim();
          
          const placeholderBuffer = await sharp(Buffer.from(placeholderSvg))
            .png()
            .toBuffer();
          
          return new NextResponse(placeholderBuffer as unknown as BodyInit, {
            headers: {
              'Content-Type': 'image/png',
              'Cache-Control': 'public, max-age=60',
            },
          });
        } catch (error) {
          console.error('Error generating placeholder for remote video:', error);
          const minimalPng = Buffer.from(
            'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
            'base64'
          );
          return new NextResponse(minimalPng as unknown as BodyInit, {
            headers: {
              'Content-Type': 'image/png',
              'Cache-Control': 'public, max-age=60',
            },
          });
        }
      }
      
      try {
        const thumbnailBuffer = await getVideoThumbnail(filePath);
        if (thumbnailBuffer && thumbnailBuffer.length > 0) {
          return new NextResponse(thumbnailBuffer as unknown as BodyInit, {
            headers: {
              'Content-Type': 'image/jpeg',
              'Cache-Control': 'public, max-age=31536000, immutable',
            },
          });
        }
        console.error(`Thumbnail generation returned empty buffer for video: ${filePath}`);
      } catch (error) {
        console.error(`Error generating thumbnail for video ${filePath}:`, error);
      }
      
      // Fallback: return a placeholder image (gray box with play icon)
      // Generate a PNG placeholder using sharp for better compatibility
      try {
        if (!sharp) throw new Error('Sharp not available');
        const placeholderSvg = `
          <svg width="400" height="400" xmlns="http://www.w3.org/2000/svg">
            <rect width="400" height="400" fill="#e5e7eb"/>
            <circle cx="200" cy="200" r="40" fill="#6b7280" opacity="0.8"/>
            <polygon points="190,185 190,215 215,200" fill="white"/>
          </svg>
        `.trim();
        
        const placeholderBuffer = await sharp(Buffer.from(placeholderSvg))
          .png()
          .toBuffer();
        
        return new NextResponse(placeholderBuffer as unknown as BodyInit, {
          headers: {
            'Content-Type': 'image/png',
            'Cache-Control': 'public, max-age=60', // Short cache for placeholder
          },
        });
      } catch (error) {
        console.error('Error generating placeholder:', error);
        // Ultimate fallback: return a minimal 1x1 PNG
        const minimalPng = Buffer.from(
          'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
          'base64'
        );
        return new NextResponse(minimalPng as unknown as BodyInit, {
          headers: {
            'Content-Type': 'image/png',
            'Cache-Control': 'public, max-age=60',
          },
        });
      }
    }

    // Handle video streaming
    if (mediaType === 'video') {
      const fileStats = statSync(filePath);
      const fileSize = fileStats.size;
      const range = request.headers.get('range');
      
      const contentType = getContentType(filePath, mediaType);
      
      // Handle range requests for video seeking
      if (range) {
        const rangeData = parseRange(range, fileSize);
        if (!rangeData) {
          return new NextResponse('Range Not Satisfiable', { 
            status: 416,
            headers: {
              'Content-Range': `bytes */${fileSize}`,
            },
          });
        }
        
        const { start, end } = rangeData;
        const chunkSize = end - start + 1;
        
        const fileBuffer = await readFile(filePath);
        const chunk = fileBuffer.slice(start, end + 1);
        
        return new NextResponse(chunk, {
          status: 206,
          headers: {
            'Content-Range': `bytes ${start}-${end}/${fileSize}`,
            'Accept-Ranges': 'bytes',
            'Content-Length': chunkSize.toString(),
            'Content-Type': contentType,
            'Cache-Control': 'public, max-age=3600',
          },
        });
      }
      
      // Full video file (no range request)
      const videoBuffer = await readFile(filePath);
      return new NextResponse(videoBuffer, {
        headers: {
          'Content-Type': contentType,
          'Content-Length': fileSize.toString(),
          'Accept-Ranges': 'bytes',
          'Cache-Control': 'public, max-age=3600',
        },
      });
    }

    // Handle images (existing logic)
    const imageBuffer = await readFile(filePath);
    const contentType = getContentType(filePath, mediaType);

    const createOriginalResponse = (cacheControl = 'public, max-age=31536000, immutable') =>
      new NextResponse(imageBuffer, {
        headers: {
          'Content-Type': contentType,
          'Cache-Control': cacheControl,
        },
      });

    if (!watermark) {
      return createOriginalResponse();
    }

    // If sharp is not available, return original image without watermark
    if (!sharp) {
      console.warn('Sharp library not available, returning original image without watermark');
      return createOriginalResponse('public, max-age=60');
    }

    try {
      const metadata = await sharp(imageBuffer).metadata();
      const baseWidth = metadata.width ?? 2000;
      const baseHeight = metadata.height ?? 2000;
      const overlayBuffer = await getWatermarkOverlay(baseWidth, baseHeight);
      const ext = path.extname(filePath).toLowerCase();
      const outputFormat =
        ext === '.png' ? 'png' :
        ext === '.webp' ? 'webp' :
        'jpeg';
      const pipeline = sharp(imageBuffer)
        .composite([
          {
            input: overlayBuffer,
            gravity: 'center',
            blend: 'hard-light',
          },
        ])
        .toColorspace('srgb');

      const result = await pipeline
        .toFormat(
          outputFormat,
          outputFormat === 'png'
            ? { compressionLevel: 5 }
            : outputFormat === 'webp'
            ? { quality: 90 }
            : { quality: 90 }
        )
        .toBuffer();

      const responseContentType =
        outputFormat === 'png'
          ? 'image/png'
          : outputFormat === 'webp'
          ? 'image/webp'
          : 'image/jpeg';

      return new NextResponse(result as unknown as BodyInit, {
        headers: {
          'Content-Type': responseContentType,
          'Cache-Control': 'public, max-age=3600',
        },
      });
    } catch (error) {
      console.error('Error applying watermark:', error);
      return createOriginalResponse('public, max-age=60');
    }
  } catch (error) {
    console.error('Error serving media:', error);
    return new NextResponse('Internal server error', { status: 500 });
  }
}

