import { FastifyInstance } from 'fastify';
import { prisma } from '../../../config/database';
import { authenticate, requireAdminRole, optionalAuth } from '../middleware/auth';
import { z } from 'zod';
import { copyFile, access, unlink } from 'fs/promises';
import { join } from 'path';
import { extractVideoMetadata } from '../services/ffprobe.service';
import { ThumbnailService } from '../services/thumbnail.service';
import { logger } from '../../../utils/logger';
import { eventBus } from '../../../services/event-bus.service';

// List videos endpoint (admin only for now)
interface ListVideosQuery {
  limit?: string;
  offset?: string;
  search?: string;
  orientation?: 'H' | 'V';
  producers?: string;
  isShort?: string;
}

export async function videosRoutes(fastify: FastifyInstance) {
  // List videos (admin only)
  fastify.get<{ Querystring: ListVideosQuery }>(
    '/',
    {
      preHandler: requireAdminRole,
    },
    async (request, reply) => {
      const limit = parseInt(request.query.limit || '50');
      const offset = parseInt(request.query.offset || '0');
      const search = request.query.search;
      const orientation = request.query.orientation;
      const producers = request.query.producers?.split(',').filter(Boolean);
      const isShort = request.query.isShort;

      // Build Prisma WHERE clause
      const where: any = {};

      if (isShort !== undefined) {
        where.isShort = isShort === 'true';
      }

      if (search) {
        where.title = {
          contains: search,
          mode: 'insensitive',
        };
      }

      if (orientation) {
        where.orientation = orientation;
      }

      if (producers && producers.length > 0) {
        where.producer = {
          in: producers,
        };
      }

      const videos = await prisma.video.findMany({
        where,
        select: {
          id: true,
          title: true,
          filename: true,
          durationSeconds: true,
          fileSize: true,
          width: true,
          height: true,
          orientation: true,
          producer: true,
          thumbnailPath: true,
          createdAt: true,
          isPublished: true,
          publishedAt: true,
          scheduledPublishAt: true,
          scheduledUnpublishAt: true,
          category: true,
          isShort: true,
          accessLevel: true,
        },
        orderBy: {
          createdAt: 'desc',
        },
        take: limit,
        skip: offset,
      });

      // Get total count
      const total = await prisma.video.count({ where });

      // Map videos to include thumbnailUrl
      const videosWithThumbnails = videos.map((video) => ({
        ...video,
        duration: video.durationSeconds ?? 0, // Add duration alias for frontend
        thumbnailUrl: video.thumbnailPath ? `/media/videos/${video.id}/thumbnail` : null,
      }));

      return {
        videos: videosWithThumbnails,
        total,
        limit,
        offset,
      };
    }
  );

  // Get single video (admin only for now)
  fastify.get<{ Params: { id: string } }>(
    '/:id',
    {
      preHandler: requireAdminRole,
    },
    async (request, reply) => {
      const videoId = parseInt(request.params.id);

      const video = await prisma.video.findUnique({
        where: { id: videoId },
      });

      if (!video) {
        return reply.code(404).send({ message: 'Video not found' });
      }

      return {
        video: {
          ...video,
          duration: video.durationSeconds ?? 0,
          thumbnailUrl: video.thumbnailPath ? `/media/videos/${video.id}/thumbnail` : null,
        },
      };
    }
  );

  // Get list of producers (admin only)
  fastify.get(
    '/producers',
    {
      preHandler: requireAdminRole,
    },
    async (request, reply) => {
      const videos = await prisma.video.findMany({
        where: {
          producer: { not: null },
        },
        select: {
          producer: true,
        },
        distinct: ['producer'],
      });

      return videos.map((v) => v.producer).filter(Boolean);
    }
  );

  // Health check for videos routes
  fastify.get('/health', async (request, reply) => {
    // Test database connection by counting videos
    const count = await prisma.video.count();

    return {
      status: 'ok',
      videosCount: count,
    };
  });

  // ========================================================================
  // PUBLISHING ROUTES (replaces copy-to-public)
  // ========================================================================

  // Zod schemas for publishing
  const PublishSchema = z.object({
    category: z.enum(['videos', 'curated', 'compilations', 'playback', 'highlights']),
  });

  const BulkPublishSchema = z.object({
    videoIds: z.array(z.number().int().positive()).min(1).max(100),
    category: z.enum(['videos', 'curated', 'compilations', 'playback', 'highlights']),
  });

  const BulkUnpublishSchema = z.object({
    videoIds: z.array(z.number().int().positive()).min(1).max(100),
  });

  // POST /videos/:id/publish - Publish single video
  fastify.post<{ Params: { id: string }; Body: z.infer<typeof PublishSchema> }>(
    '/:id/publish',
    { preHandler: requireAdminRole },
    async (request, reply) => {
      const videoId = parseInt(request.params.id);
      const parseResult = PublishSchema.safeParse(request.body);

      if (!parseResult.success) {
        return reply.code(400).send({ message: 'Invalid category', errors: parseResult.error.errors });
      }

      const { category } = parseResult.data;

      try {
        const video = await prisma.video.update({
          where: { id: videoId },
          data: {
            isPublished: true,
            publishedAt: new Date(),
            category,
          },
        });

        const userId = (request as any).user?.id || 'unknown';
        logger.info(`Video ${videoId} published to ${category}`);

        eventBus.publish('media.video.published', {
          videoId: String(videoId),
          title: video.title || video.filename || `Video #${videoId}`,
          publishedByUserId: userId,
        });

        return { success: true, video };
      } catch (error: any) {
        logger.error(`Error publishing video ${videoId}:`, error);
        return reply.code(500).send({ message: 'Failed to publish video' });
      }
    }
  );

  // POST /videos/:id/unpublish - Unpublish single video
  fastify.post<{ Params: { id: string } }>(
    '/:id/unpublish',
    { preHandler: requireAdminRole },
    async (request, reply) => {
      const videoId = parseInt(request.params.id);

      try {
        const video = await prisma.video.update({
          where: { id: videoId },
          data: {
            isPublished: false,
            publishedAt: null,
            // Keep category for re-publishing
          },
        });

        logger.info(`Video ${videoId} unpublished`);

        eventBus.publish('media.video.unpublished', {
          videoId: String(videoId),
          title: video.title || video.filename || `Video #${videoId}`,
        });

        return { success: true, video };
      } catch (error: any) {
        logger.error(`Error unpublishing video ${videoId}:`, error);
        return reply.code(500).send({ message: 'Failed to unpublish video' });
      }
    }
  );

  // POST /videos/bulk-publish - Publish multiple videos
  fastify.post<{ Body: z.infer<typeof BulkPublishSchema> }>(
    '/bulk-publish',
    { preHandler: requireAdminRole },
    async (request, reply) => {
      const parseResult = BulkPublishSchema.safeParse(request.body);

      if (!parseResult.success) {
        return reply.code(400).send({ message: 'Invalid request', errors: parseResult.error.errors });
      }

      const { videoIds, category } = parseResult.data;

      try {
        const result = await prisma.video.updateMany({
          where: { id: { in: videoIds } },
          data: {
            isPublished: true,
            publishedAt: new Date(),
            category,
          },
        });

        logger.info(`Bulk published ${result.count} videos to ${category}`);
        return { success: true, count: result.count };
      } catch (error: any) {
        logger.error(`Error bulk publishing videos:`, error);
        return reply.code(500).send({ message: 'Failed to publish videos' });
      }
    }
  );

  // POST /videos/bulk-unpublish - Unpublish multiple videos
  fastify.post<{ Body: z.infer<typeof BulkUnpublishSchema> }>(
    '/bulk-unpublish',
    { preHandler: requireAdminRole },
    async (request, reply) => {
      const parseResult = BulkUnpublishSchema.safeParse(request.body);

      if (!parseResult.success) {
        return reply.code(400).send({ message: 'Invalid request', errors: parseResult.error.errors });
      }

      const { videoIds } = parseResult.data;

      try {
        const result = await prisma.video.updateMany({
          where: { id: { in: videoIds } },
          data: {
            isPublished: false,
            publishedAt: null,
          },
        });

        logger.info(`Bulk unpublished ${result.count} videos`);
        return { success: true, count: result.count };
      } catch (error: any) {
        logger.error(`Error bulk unpublishing videos:`, error);
        return reply.code(500).send({ message: 'Failed to unpublish videos' });
      }
    }
  );

  // POST /videos/:id/lock - Lock published video
  fastify.post<{ Params: { id: string } }>(
    '/:id/lock',
    { preHandler: requireAdminRole },
    async (request, reply) => {
      const videoId = parseInt(request.params.id);
      const userId = (request as any).user?.id;

      try {
        const video = await prisma.video.update({
          where: { id: videoId },
          data: {
            isLocked: true,
            lockedAt: new Date(),
            lockedById: userId,
          },
        });

        logger.info(`Video ${videoId} locked by user ${userId}`);
        return { success: true, video };
      } catch (error: any) {
        logger.error(`Error locking video ${videoId}:`, error);
        return reply.code(500).send({ message: 'Failed to lock video' });
      }
    }
  );

  // POST /videos/:id/unlock - Unlock published video
  fastify.post<{ Params: { id: string } }>(
    '/:id/unlock',
    { preHandler: requireAdminRole },
    async (request, reply) => {
      const videoId = parseInt(request.params.id);

      try {
        const video = await prisma.video.update({
          where: { id: videoId },
          data: {
            isLocked: false,
            lockedAt: null,
            lockedById: null,
          },
        });

        logger.info(`Video ${videoId} unlocked`);
        return { success: true, video };
      } catch (error: any) {
        logger.error(`Error unlocking video ${videoId}:`, error);
        return reply.code(500).send({ message: 'Failed to unlock video' });
      }
    }
  );

  // ========================================================================
  // THUMBNAIL GENERATION ROUTES
  // ========================================================================

  // POST /videos/:id/generate-thumbnail - Generate thumbnail for single video
  fastify.post<{ Params: { id: string } }>(
    '/:id/generate-thumbnail',
    { preHandler: requireAdminRole },
    async (request, reply) => {
      const videoId = parseInt(request.params.id);

      try {
        const video = await prisma.video.findUnique({
          where: { id: videoId },
        });

        if (!video) {
          return reply.code(404).send({ message: 'Video not found' });
        }

        // Check if video file exists
        try {
          await access(video.path);
        } catch {
          return reply.code(400).send({ message: 'Video file not found on disk' });
        }

        // Generate thumbnail
        const thumbnailPath = await ThumbnailService.generateThumbnail({
          videoPath: video.path,
          videoId: video.id,
          duration: video.durationSeconds ?? 0,
          orientation: video.orientation ?? '',
        });

        // Update video with thumbnail path
        const updatedVideo = await prisma.video.update({
          where: { id: videoId },
          data: { thumbnailPath },
        });

        logger.info(`Thumbnail generated for video ${videoId}`);
        return { success: true, video: updatedVideo };
      } catch (error: any) {
        logger.error(`Error generating thumbnail for video ${videoId}:`, error);
        return reply.code(500).send({ message: 'Failed to generate thumbnail' });
      }
    }
  );

  // POST /videos/bulk-generate-thumbnails - Generate thumbnails for all videos without them
  fastify.post(
    '/bulk-generate-thumbnails',
    { preHandler: requireAdminRole },
    async (request, reply) => {
      try {
        // Find all videos without thumbnails
        const videos = await prisma.video.findMany({
          where: {
            thumbnailPath: null,
            isValid: true,
          },
          select: {
            id: true,
            path: true,
            durationSeconds: true,
            orientation: true,
          },
        });

        logger.info(`Found ${videos.length} videos without thumbnails`);

        const results = {
          total: videos.length,
          succeeded: 0,
          failed: 0,
          errors: [] as { videoId: number; error: string }[],
        };

        for (const video of videos) {
          try {
            // Check if video file exists
            await access(video.path);

            // Generate thumbnail
            const thumbnailPath = await ThumbnailService.generateThumbnail({
              videoPath: video.path,
              videoId: video.id,
              duration: video.durationSeconds ?? 0,
              orientation: video.orientation ?? '',
            });

            // Update video with thumbnail path
            await prisma.video.update({
              where: { id: video.id },
              data: { thumbnailPath },
            });

            results.succeeded++;
            logger.info(`Thumbnail generated for video ${video.id}`);
          } catch (error: any) {
            results.failed++;
            results.errors.push({
              videoId: video.id,
              error: error.message,
            });
            logger.error(`Failed to generate thumbnail for video ${video.id}:`, error);
          }
        }

        return {
          success: true,
          results,
        };
      } catch (error: any) {
        logger.error('Error bulk generating thumbnails:', error);
        return reply.code(500).send({ message: 'Failed to bulk generate thumbnails' });
      }
    }
  );
}
