import { NextRequest, NextResponse } from 'next/server';
import { prisma, prismaAuth } from '@/lib/db';
import { serializePhotos } from '@/lib/serialize';
import { auth } from '@/app/api/auth/[...nextauth]/route';

export async function GET(request: NextRequest) {
  try {
    const searchParams = request.nextUrl.searchParams;
    
    // Parse query parameters
    const people = searchParams.get('people')?.split(',').filter(Boolean).map(Number) || [];
    const peopleMode = (searchParams.get('peopleMode') || 'any') as 'any' | 'all';
    const tags = searchParams.get('tags')?.split(',').filter(Boolean).map(Number) || [];
    const tagsMode = (searchParams.get('tagsMode') || 'any') as 'any' | 'all';
    const dateFrom = searchParams.get('dateFrom');
    const dateTo = searchParams.get('dateTo');
    const mediaType = (searchParams.get('mediaType') || 'all') as 'all' | 'photos' | 'videos';
    const favoritesOnly = searchParams.get('favoritesOnly') === 'true';
    const page = parseInt(searchParams.get('page') || '1', 10);
    const pageSize = parseInt(searchParams.get('pageSize') || '30', 10);
    const skip = (page - 1) * pageSize;

    // Get user session for favorites filter
    const session = await auth();
    let favoritePhotoIds: number[] = [];
    
    if (favoritesOnly && session?.user?.id) {
      const userId = parseInt(session.user.id, 10);
      if (!isNaN(userId)) {
        try {
          const favorites = await prismaAuth.photoFavorite.findMany({
            where: { userId },
            select: { photoId: true },
          });
          favoritePhotoIds = favorites.map((f: { photoId: number }) => f.photoId);
          
          // If user has no favorites, return empty result
          if (favoritePhotoIds.length === 0) {
            return NextResponse.json({
              photos: [],
              total: 0,
              page,
              pageSize,
              totalPages: 0,
            });
          }
        } catch (error: any) {
          // Handle case where table doesn't exist yet (P2021 = table does not exist)
          if (error.code === 'P2021') {
            console.warn('photo_favorites table does not exist yet. Run migration: migrations/add-photo-favorites-table.sql');
          } else {
            console.error('Error fetching favorites:', error);
          }
          // If favorites table doesn't exist or error, treat as no favorites
          if (favoritesOnly) {
            return NextResponse.json({
              photos: [],
              total: 0,
              page,
              pageSize,
              totalPages: 0,
            });
          }
        }
      }
    }

    // Build where clause
    const where: any = {
      processed: true,
    };

    // Media type filter
    if (mediaType !== 'all') {
      if (mediaType === 'photos') {
        where.media_type = 'image';
      } else if (mediaType === 'videos') {
        where.media_type = 'video';
      }
    }

    // Date filter
    if (dateFrom || dateTo) {
      where.date_taken = {};
      if (dateFrom) {
        where.date_taken.gte = new Date(dateFrom);
      }
      if (dateTo) {
        where.date_taken.lte = new Date(dateTo);
      }
    }

    // People filter
    if (people.length > 0) {
      if (peopleMode === 'all') {
        // Photo must have ALL selected people
        where.AND = where.AND || [];
        people.forEach((personId) => {
          where.AND.push({
            Face: {
              some: {
                person_id: personId,
              },
            },
          });
        });
      } else {
        // Photo has ANY of the selected people (default)
        where.Face = {
          some: {
            person_id: { in: people },
          },
        };
      }
    }

    // Tags filter
    if (tags.length > 0) {
      if (tagsMode === 'all') {
        // Photo must have ALL selected tags
        where.AND = where.AND || [];
        tags.forEach((tagId) => {
          where.AND.push({
            PhotoTagLinkage: {
              some: {
                tag_id: tagId,
              },
            },
          });
        });
      } else {
        // Photo has ANY of the selected tags (default)
        where.PhotoTagLinkage = {
          some: {
            tag_id: { in: tags },
          },
        };
      }
    }

    // Favorites filter
    if (favoritesOnly && favoritePhotoIds.length > 0) {
      where.id = { in: favoritePhotoIds };
    } else if (favoritesOnly && favoritePhotoIds.length === 0) {
      // User has no favorites, return empty (already handled above, but keep for safety)
      where.id = { in: [] };
    }

    // Execute query - load photos and relations separately
    // Use raw query to read dates as strings and convert manually to avoid Prisma conversion issues
    let photosBase: any[];
    let total: number;
    
    try {
      // Build WHERE clause for raw SQL
      const whereConditions: string[] = ['processed = true'];
      const params: any[] = [];
      let paramIndex = 1; // PostgreSQL uses $1, $2, etc.
      
      if (mediaType !== 'all') {
        if (mediaType === 'photos') {
          whereConditions.push(`media_type = $${paramIndex}`);
          params.push('image');
          paramIndex++;
        } else if (mediaType === 'videos') {
          whereConditions.push(`media_type = $${paramIndex}`);
          params.push('video');
          paramIndex++;
        }
      }
      
      if (dateFrom || dateTo) {
        if (dateFrom) {
          whereConditions.push(`date_taken >= $${paramIndex}`);
          params.push(dateFrom);
          paramIndex++;
        }
        if (dateTo) {
          whereConditions.push(`date_taken <= $${paramIndex}`);
          params.push(dateTo);
          paramIndex++;
        }
      }
      
      // Handle people filter - embed IDs directly since they're safe integers
      if (people.length > 0) {
        const peopleIds = people.join(',');
        whereConditions.push(`id IN (
          SELECT DISTINCT photo_id FROM faces WHERE person_id IN (${peopleIds})
        )`);
      }
      
      // Handle tags filter - embed IDs directly since they're safe integers
      if (tags.length > 0) {
        const tagIds = tags.join(',');
        whereConditions.push(`id IN (
          SELECT DISTINCT photo_id FROM phototaglinkage WHERE tag_id IN (${tagIds})
        )`);
      }
      
      // Handle favorites filter - embed IDs directly since they're safe integers
      if (favoritesOnly && favoritePhotoIds.length > 0) {
        const favIds = favoritePhotoIds.join(',');
        whereConditions.push(`id IN (${favIds})`);
      } else if (favoritesOnly && favoritePhotoIds.length === 0) {
        whereConditions.push('1 = 0'); // No favorites, return empty
      }
      
      const whereClause = whereConditions.join(' AND ');
      
      // Build query parameters (LIMIT and OFFSET are embedded directly as they're safe integers)
      const queryParams = [...params];
      const countParams = [...params];
      
      // Use raw query to read dates as strings
      // Note: LIMIT and OFFSET are embedded directly since they're integers and safe
      const [photosRaw, totalResult] = await Promise.all([
        prisma.$queryRawUnsafe<Array<{
          id: number;
          path: string;
          filename: string;
          date_added: string;
          date_taken: string | null;
          processed: boolean;
          media_type: string | null;
        }>>(
          `SELECT 
            id, 
            path, 
            filename, 
            date_added, 
            date_taken, 
            processed, 
            media_type
          FROM photos
          WHERE ${whereClause}
          ORDER BY date_taken DESC, id DESC
          LIMIT ${pageSize} OFFSET ${skip}`,
          ...queryParams
        ),
        prisma.$queryRawUnsafe<Array<{ count: bigint }>>(
          `SELECT COUNT(*) as count FROM photos WHERE ${whereClause}`,
          ...countParams
        ),
      ]);
      
      // Convert date strings to Date objects
      photosBase = photosRaw.map(photo => ({
        id: photo.id,
        path: photo.path,
        filename: photo.filename,
        date_added: new Date(photo.date_added),
        date_taken: photo.date_taken ? new Date(photo.date_taken) : null,
        processed: photo.processed,
        media_type: photo.media_type,
      }));
      
      total = Number(totalResult[0].count);
    } catch (error: any) {
      console.error('Error loading photos:', error);
      throw error;
    }

    // Load faces and tags separately
    const photoIds = photosBase.map(p => p.id);
    
    // Fetch faces
    let faces: any[] = [];
    try {
      faces = await prisma.face.findMany({
        where: { photo_id: { in: photoIds } },
        select: {
          id: true,
          photo_id: true,
          person_id: true,
          location: true,
          confidence: true,
          quality_score: true,
          is_primary_encoding: true,
          detector_backend: true,
          model_name: true,
          face_confidence: true,
          exif_orientation: true,
          pose_mode: true,
          yaw_angle: true,
          pitch_angle: true,
          roll_angle: true,
          landmarks: true,
          identified_by_user_id: true,
          excluded: true,
          Person: {
            select: {
              id: true,
              first_name: true,
              last_name: true,
              middle_name: true,
              maiden_name: true,
              date_of_birth: true,
              created_date: true,
            },
          },
          // Exclude encoding field (Bytes) to avoid P2023 conversion errors
        },
      });
    } catch (faceError: any) {
      if (faceError?.code === 'P2023' || faceError?.message?.includes('Conversion failed')) {
        console.warn('Corrupted face data detected in search, skipping faces');
        faces = [];
      } else {
        throw faceError;
      }
    }

    // Fetch photo tag linkages with error handling
    let photoTagLinkages: any[] = [];
    try {
      photoTagLinkages = await prisma.photoTagLinkage.findMany({
        where: { photo_id: { in: photoIds } },
        select: {
          linkage_id: true,
          photo_id: true,
          tag_id: true,
          linkage_type: true,
          created_date: true,
          Tag: {
            select: {
              id: true,
              tag_name: true,
              created_date: true,
            },
          },
        },
      });
    } catch (linkageError: any) {
      if (linkageError?.code === 'P2023' || linkageError?.message?.includes('Conversion failed')) {
        console.warn('Corrupted photo tag linkage data detected, attempting fallback query');
        try {
          // Try with minimal fields
          photoTagLinkages = await prisma.photoTagLinkage.findMany({
            where: { photo_id: { in: photoIds } },
            select: {
              linkage_id: true,
              photo_id: true,
              tag_id: true,
              // Exclude potentially corrupted fields
              Tag: {
                select: {
                  id: true,
                  tag_name: true,
                  // Exclude created_date if it's corrupted
                },
              },
            },
          });
        } catch (fallbackError: any) {
          console.error('Fallback photo tag linkage query also failed:', fallbackError);
          // Return empty array as last resort to prevent API crash
          photoTagLinkages = [];
        }
      } else {
        throw linkageError;
      }
    }

    // Combine the data manually
    const photos = photosBase.map(photo => ({
      ...photo,
      Face: faces.filter(face => face.photo_id === photo.id),
      PhotoTagLinkage: photoTagLinkages.filter(link => link.photo_id === photo.id),
    }));

    return NextResponse.json({
      photos: serializePhotos(photos),
      total,
      page,
      pageSize,
      totalPages: Math.ceil(total / pageSize),
    });
  } catch (error) {
    console.error('Search error:', error);
    const errorMessage = error instanceof Error ? error.message : 'Unknown error';
    const errorStack = error instanceof Error ? error.stack : undefined;
    console.error('Error details:', { errorMessage, errorStack, error });
    return NextResponse.json(
      { 
        error: 'Failed to search photos',
        details: errorMessage,
        ...(process.env.NODE_ENV === 'development' && { stack: errorStack })
      },
      { status: 500 }
    );
  }
}

