import { FastifyInstance } from 'fastify';
import { prisma } from '../../../config/database';
import { requireAdminRole } from '../middleware/auth';
import { videoScheduleQueueService } from '../../../services/video-schedule-queue.service';
import { logger } from '../../../utils/logger';
import { z } from 'zod';

// Validation schemas
const schedulePublishSchema = z.object({
  publishAt: z.string().datetime(),
  timezone: z.string().optional().default('UTC'),
});

const scheduleUnpublishSchema = z.object({
  unpublishAt: z.string().datetime(),
  timezone: z.string().optional().default('UTC'),
});

export async function videoScheduleRoutes(fastify: FastifyInstance) {
  /**
   * POST /videos/:id/schedule-publish
   * Schedule a video to be published at a specific time
   */
  fastify.post<{
    Params: { id: string };
    Body: { publishAt: string; timezone?: string };
  }>(
    '/:id/schedule-publish',
    {
      preHandler: requireAdminRole,
    },
    async (request, reply) => {
      const videoId = parseInt(request.params.id);
      const { publishAt, timezone } = request.body;

      // Validate input
      try {
        schedulePublishSchema.parse(request.body);
      } catch (error) {
        return reply.code(400).send({ message: 'Invalid input', error });
      }

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

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

        // Parse publish time
        const publishDate = new Date(publishAt);
        const now = new Date();

        if (publishDate <= now) {
          return reply.code(400).send({ message: 'Publish time must be in the future' });
        }

        // Get user ID from request (set by auth middleware)
        const userId = (request as any).user?.id;
        if (!userId) {
          return reply.code(401).send({ message: 'Unauthorized' });
        }

        // Schedule the publish
        const result = await videoScheduleQueueService.schedulePublish(
          videoId,
          publishDate,
          userId
        );

        logger.info(`Scheduled video ${videoId} to publish at ${publishDate.toISOString()}`);

        return {
          success: true,
          message: 'Video scheduled for publish',
          scheduledFor: result.scheduledFor,
          jobId: result.jobId,
        };
      } catch (error) {
        logger.error('Failed to schedule video publish', { error, videoId });
        return reply.code(500).send({
          message: error instanceof Error ? error.message : 'Failed to schedule video',
        });
      }
    }
  );

  /**
   * POST /videos/:id/schedule-unpublish
   * Schedule a video to be unpublished at a specific time
   */
  fastify.post<{
    Params: { id: string };
    Body: { unpublishAt: string; timezone?: string };
  }>(
    '/:id/schedule-unpublish',
    {
      preHandler: requireAdminRole,
    },
    async (request, reply) => {
      const videoId = parseInt(request.params.id);
      const { unpublishAt, timezone } = request.body;

      // Validate input
      try {
        scheduleUnpublishSchema.parse(request.body);
      } catch (error) {
        return reply.code(400).send({ message: 'Invalid input', error });
      }

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

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

        // Parse unpublish time
        const unpublishDate = new Date(unpublishAt);
        const now = new Date();

        if (unpublishDate <= now) {
          return reply.code(400).send({ message: 'Unpublish time must be in the future' });
        }

        // Get user ID from request
        const userId = (request as any).user?.id;
        if (!userId) {
          return reply.code(401).send({ message: 'Unauthorized' });
        }

        // Schedule the unpublish
        const result = await videoScheduleQueueService.scheduleUnpublish(
          videoId,
          unpublishDate,
          userId
        );

        logger.info(`Scheduled video ${videoId} to unpublish at ${unpublishDate.toISOString()}`);

        return {
          success: true,
          message: 'Video scheduled for unpublish',
          scheduledFor: result.scheduledFor,
          jobId: result.jobId,
        };
      } catch (error) {
        logger.error('Failed to schedule video unpublish', { error, videoId });
        return reply.code(500).send({
          message: error instanceof Error ? error.message : 'Failed to schedule video',
        });
      }
    }
  );

  /**
   * DELETE /videos/:id/schedule/:action
   * Cancel a scheduled publish or unpublish
   */
  fastify.delete<{
    Params: { id: string; action: string };
  }>(
    '/:id/schedule/:action',
    {
      preHandler: requireAdminRole,
    },
    async (request, reply) => {
      const videoId = parseInt(request.params.id);
      const action = request.params.action as 'publish' | 'unpublish';

      if (!['publish', 'unpublish'].includes(action)) {
        return reply.code(400).send({ message: 'Invalid action. Must be "publish" or "unpublish"' });
      }

      try {
        await videoScheduleQueueService.cancelSchedule(videoId, action);

        logger.info(`Cancelled ${action} schedule for video ${videoId}`);

        return {
          success: true,
          message: `${action} schedule cancelled`,
        };
      } catch (error) {
        logger.error(`Failed to cancel ${action} schedule`, { error, videoId });
        return reply.code(500).send({
          message: `Failed to cancel ${action} schedule`,
        });
      }
    }
  );

  /**
   * GET /videos/schedules/upcoming
   * Get all upcoming scheduled publish/unpublish operations
   */
  fastify.get<{
    Querystring: { limit?: string };
  }>(
    '/schedules/upcoming',
    {
      preHandler: requireAdminRole,
    },
    async (request, reply) => {
      const limit = parseInt(request.query.limit || '50');

      try {
        const schedules = await videoScheduleQueueService.getUpcomingSchedules(limit);

        return {
          schedules,
          total: schedules.length,
        };
      } catch (error) {
        logger.error('Failed to get upcoming schedules', { error });
        return reply.code(500).send({ message: 'Failed to fetch schedules' });
      }
    }
  );

  /**
   * GET /videos/:id/schedule-history
   * Get schedule history for a specific video
   */
  fastify.get<{
    Params: { id: string };
    Querystring: { limit?: string };
  }>(
    '/:id/schedule-history',
    {
      preHandler: requireAdminRole,
    },
    async (request, reply) => {
      const videoId = parseInt(request.params.id);
      const limit = parseInt(request.query.limit || '10');

      try {
        const history = await videoScheduleQueueService.getScheduleHistory(videoId, limit);

        return {
          videoId,
          history,
        };
      } catch (error) {
        logger.error('Failed to get schedule history', { error, videoId });
        return reply.code(500).send({ message: 'Failed to fetch schedule history' });
      }
    }
  );

  /**
   * GET /videos/schedules/stats
   * Get queue statistics
   */
  fastify.get(
    '/schedules/stats',
    {
      preHandler: requireAdminRole,
    },
    async (request, reply) => {
      try {
        const stats = await videoScheduleQueueService.getStats();

        return stats;
      } catch (error) {
        logger.error('Failed to get schedule stats', { error });
        return reply.code(500).send({ message: 'Failed to fetch stats' });
      }
    }
  );

  /**
   * POST /videos/schedules/pause
   * Pause the schedule queue
   */
  fastify.post(
    '/schedules/pause',
    {
      preHandler: requireAdminRole,
    },
    async (request, reply) => {
      try {
        await videoScheduleQueueService.pause();

        return {
          success: true,
          message: 'Schedule queue paused',
        };
      } catch (error) {
        logger.error('Failed to pause schedule queue', { error });
        return reply.code(500).send({ message: 'Failed to pause queue' });
      }
    }
  );

  /**
   * POST /videos/schedules/resume
   * Resume the schedule queue
   */
  fastify.post(
    '/schedules/resume',
    {
      preHandler: requireAdminRole,
    },
    async (request, reply) => {
      try {
        await videoScheduleQueueService.resume();

        return {
          success: true,
          message: 'Schedule queue resumed',
        };
      } catch (error) {
        logger.error('Failed to resume schedule queue', { error });
        return reply.code(500).send({ message: 'Failed to resume queue' });
      }
    }
  );

  /**
   * POST /videos/schedules/cleanup
   * Clean up old completed jobs
   */
  fastify.post(
    '/schedules/cleanup',
    {
      preHandler: requireAdminRole,
    },
    async (request, reply) => {
      try {
        const cleaned = await videoScheduleQueueService.cleanup();

        return {
          success: true,
          message: `Cleaned ${cleaned} old jobs`,
          cleaned,
        };
      } catch (error) {
        logger.error('Failed to cleanup schedule queue', { error });
        return reply.code(500).send({ message: 'Failed to cleanup queue' });
      }
    }
  );
}
