import { FastifyInstance, FastifyRequest, FastifyReply } from 'fastify';
import { pipeline } from 'stream/promises';
import { createWriteStream } from 'fs';
import { unlink, mkdir } from 'fs/promises';
import { join, extname } from 'path';
import { randomUUID } from 'crypto';
import { prisma } from '../../../config/database';
import {
  ALLOWED_IMAGE_EXTENSIONS,
  validateImage,
  extractPhotoMetadata,
  generateVariants,
  getPhotoInboxDir,
} from '../services/photo-processing.service';
import { requireAdminRole } from '../middleware/auth';
import { logger } from '../../../utils/logger';

/**
 * Photo upload routes (prefix: /api/photos)
 */
export async function photoUploadRoutes(fastify: FastifyInstance) {
  // POST /api/photos/upload - Single photo upload
  fastify.post(
    '/upload',
    { preHandler: requireAdminRole },
    async (request: FastifyRequest, reply: FastifyReply) => {
      let tempFilePath: string | null = null;

      try {
        const data = await request.file();
        if (!data) {
          return reply.code(400).send({ message: 'No file uploaded' });
        }

        // Validate file extension
        const ext = extname(data.filename).toLowerCase();
        if (!ALLOWED_IMAGE_EXTENSIONS.includes(ext)) {
          return reply.code(400).send({
            message: `Invalid file type. Allowed: ${ALLOWED_IMAGE_EXTENSIONS.join(', ')}`,
          });
        }

        // Generate unique filename and ensure inbox dir exists
        const filename = `${randomUUID()}${ext}`;
        const inboxDir = getPhotoInboxDir();
        await mkdir(inboxDir, { recursive: true });

        const filePath = join(inboxDir, filename);
        tempFilePath = filePath;

        // Stream file to disk
        logger.info(`Uploading photo to ${filePath}`);
        await pipeline(data.file, createWriteStream(filePath));

        // Extract metadata fields from form data
        const metadataFields = data.fields as Record<string, { value: string }>;
        const title = metadataFields.title?.value;
        const producer = metadataFields.producer?.value;
        const creator = metadataFields.creator?.value;
        const albumIdStr = metadataFields.albumId?.value;
        const albumId = albumIdStr ? parseInt(albumIdStr) : null;

        // Validate image
        logger.info(`Validating image: ${filePath}`);
        await validateImage(filePath);

        // Extract metadata
        logger.info(`Extracting photo metadata: ${filePath}`);
        const metadata = await extractPhotoMetadata(filePath);

        // Determine album position if adding to album
        let albumPosition: number | null = null;
        if (albumId) {
          const maxPos = await prisma.photo.aggregate({
            where: { albumId },
            _max: { albumPosition: true },
          });
          albumPosition = (maxPos._max.albumPosition ?? -1) + 1;
        }

        // Insert into database
        const photo = await prisma.photo.create({
          data: {
            path: filePath,
            filename,
            originalFilename: data.filename,
            title: title || data.filename,
            producer: producer || null,
            creator: creator || null,
            width: metadata.width,
            height: metadata.height,
            orientation: metadata.orientation,
            fileSize: BigInt(metadata.fileSize),
            format: metadata.format,
            colorSpace: metadata.colorSpace,
            hasAlpha: metadata.hasAlpha,
            dpi: metadata.dpi,
            cameraMake: metadata.cameraMake,
            cameraModel: metadata.cameraModel,
            focalLength: metadata.focalLength,
            aperture: metadata.aperture,
            shutterSpeed: metadata.shutterSpeed,
            iso: metadata.iso,
            takenAt: metadata.takenAt,
            gpsLatitude: metadata.gpsLatitude,
            gpsLongitude: metadata.gpsLongitude,
            albumId,
            albumPosition,
            uploaderId: request.user?.id || null,
          },
        });

        logger.info(`Photo uploaded: ${photo.id}`);

        // Generate image variants (thumbnail, medium, large, webp)
        try {
          const variants = await generateVariants(filePath, photo.id);
          await prisma.photo.update({
            where: { id: photo.id },
            data: {
              thumbnailPath: variants.thumbnailPath,
              mediumPath: variants.mediumPath,
              largePath: variants.largePath,
              webpPath: variants.webpPath,
            },
          });
          logger.info(`Generated variants for photo ${photo.id}`);
        } catch (variantError) {
          logger.error(`Failed to generate variants for photo ${photo.id}:`, variantError);
        }

        // Update album photo count if applicable
        if (albumId) {
          const count = await prisma.photo.count({ where: { albumId } });
          await prisma.photoAlbum.update({
            where: { id: albumId },
            data: { photoCount: count },
          });
        }

        return reply.code(201).send({
          message: 'Photo uploaded successfully',
          photo: { ...photo, fileSize: photo.fileSize?.toString() ?? null },
        });
      } catch (error) {
        if (tempFilePath) {
          try { await unlink(tempFilePath); } catch { /* ignore */ }
        }
        logger.error('Photo upload failed:', error);
        return reply.code(500).send({
          message: error instanceof Error ? error.message : 'Upload failed',
        });
      }
    }
  );

  // POST /api/photos/upload/batch - Batch photo upload
  fastify.post(
    '/upload/batch',
    { preHandler: requireAdminRole },
    async (request: FastifyRequest, reply: FastifyReply) => {
      try {
        const files = request.files();
        const results: Array<{ filename: string; success: boolean; error?: string; photo?: any }> = [];

        for await (const file of files) {
          let tempFilePath: string | null = null;

          try {
            const ext = extname(file.filename).toLowerCase();
            if (!ALLOWED_IMAGE_EXTENSIONS.includes(ext)) {
              results.push({
                filename: file.filename,
                success: false,
                error: `Invalid file type. Allowed: ${ALLOWED_IMAGE_EXTENSIONS.join(', ')}`,
              });
              continue;
            }

            const filename = `${randomUUID()}${ext}`;
            const inboxDir = getPhotoInboxDir();
            await mkdir(inboxDir, { recursive: true });

            const filePath = join(inboxDir, filename);
            tempFilePath = filePath;

            await pipeline(file.file, createWriteStream(filePath));
            await validateImage(filePath);
            const metadata = await extractPhotoMetadata(filePath);

            const photo = await prisma.photo.create({
              data: {
                path: filePath,
                filename,
                originalFilename: file.filename,
                title: file.filename,
                width: metadata.width,
                height: metadata.height,
                orientation: metadata.orientation,
                fileSize: BigInt(metadata.fileSize),
                format: metadata.format,
                colorSpace: metadata.colorSpace,
                hasAlpha: metadata.hasAlpha,
                dpi: metadata.dpi,
                cameraMake: metadata.cameraMake,
                cameraModel: metadata.cameraModel,
                focalLength: metadata.focalLength,
                aperture: metadata.aperture,
                shutterSpeed: metadata.shutterSpeed,
                iso: metadata.iso,
                takenAt: metadata.takenAt,
                gpsLatitude: metadata.gpsLatitude,
                gpsLongitude: metadata.gpsLongitude,
                uploaderId: request.user?.id || null,
              },
            });

            // Generate variants
            try {
              const variants = await generateVariants(filePath, photo.id);
              await prisma.photo.update({
                where: { id: photo.id },
                data: {
                  thumbnailPath: variants.thumbnailPath,
                  mediumPath: variants.mediumPath,
                  largePath: variants.largePath,
                  webpPath: variants.webpPath,
                },
              });
            } catch (variantError) {
              logger.error(`Failed to generate variants for photo ${photo.id}:`, variantError);
            }

            results.push({
              filename: file.filename,
              success: true,
              photo: { ...photo, fileSize: photo.fileSize?.toString() ?? null },
            });

            logger.info(`Batch upload: ${file.filename} -> photo ${photo.id}`);
          } catch (error) {
            if (tempFilePath) {
              try { await unlink(tempFilePath); } catch { /* ignore */ }
            }
            logger.error(`Batch upload failed for ${file.filename}:`, error);
            results.push({
              filename: file.filename,
              success: false,
              error: error instanceof Error ? error.message : 'Upload failed',
            });
          }
        }

        const successCount = results.filter(r => r.success).length;
        const failCount = results.length - successCount;

        return reply.code(207).send({
          message: `Batch upload: ${successCount} succeeded, ${failCount} failed`,
          results,
        });
      } catch (error) {
        logger.error('Batch photo upload failed:', error);
        return reply.code(500).send({
          message: error instanceof Error ? error.message : 'Batch upload failed',
        });
      }
    }
  );
}
