import { FastifyInstance } from 'fastify';
import { prisma } from '../../../config/database';
import { authenticate } from '../middleware/auth';
import { logger } from '../../../utils/logger';
import { randomUUID } from 'crypto';

interface PlaylistParams {
  id: string;
}

interface VideoParams {
  id: string;
  mediaId: string;
}

interface CreatePlaylistBody {
  name: string;
  description?: string;
  isPublic?: boolean;
}

interface UpdatePlaylistBody {
  name?: string;
  description?: string;
  isPublic?: boolean;
}

interface AddVideoBody {
  mediaId: number;
}

interface ReorderBody {
  items: Array<{ mediaId: number; position: number }>;
}

async function updatePlaylistCounters(playlistId: number) {
  const stats = await prisma.playlistVideo.aggregate({
    where: { playlistId },
    _count: { id: true },
  });

  // Sum duration from joined videos
  const videos = await prisma.playlistVideo.findMany({
    where: { playlistId },
    select: {
      media: {
        select: { durationSeconds: true },
      },
    },
  });

  const totalDuration = videos.reduce(
    (sum, v) => sum + (v.media.durationSeconds ?? 0),
    0
  );

  await prisma.playlist.update({
    where: { id: playlistId },
    data: {
      videoCount: stats._count.id,
      totalDurationSeconds: totalDuration,
      updatedAt: new Date(),
    },
  });
}

async function checkOwnership(playlistId: number, userId: string) {
  const playlist = await prisma.playlist.findUnique({
    where: { id: playlistId },
    select: { userId: true },
  });

  if (!playlist) return null;
  if (playlist.userId !== userId) return false;
  return true;
}

export async function playlistsUserRoutes(fastify: FastifyInstance) {
  // GET /api/playlists/my - Current user's playlists
  fastify.get(
    '/my',
    { preHandler: authenticate },
    async (request, reply) => {
      const userId = request.user!.id;

      const playlists = await prisma.playlist.findMany({
        where: { userId },
        select: {
          id: true,
          name: true,
          description: true,
          isPublic: true,
          shareToken: true,
          videoCount: true,
          totalDurationSeconds: true,
          viewCount: true,
          createdAt: true,
          updatedAt: true,
          featured: {
            select: { id: true, position: true },
          },
          videos: {
            select: {
              media: {
                select: { thumbnailPath: true, id: true },
              },
            },
            orderBy: { position: 'asc' as const },
            take: 1,
          },
        },
        orderBy: { updatedAt: 'desc' },
      });

      return {
        data: playlists.map((p) => {
          const firstVideo = p.videos?.[0]?.media;
          return {
            id: p.id,
            name: p.name,
            description: p.description,
            isPublic: p.isPublic,
            shareToken: p.shareToken,
            videoCount: p.videoCount ?? 0,
            totalDurationSeconds: p.totalDurationSeconds ?? 0,
            viewCount: p.viewCount ?? 0,
            thumbnailUrl: firstVideo?.thumbnailPath
              ? `/media/videos/${firstVideo.id}/thumbnail`
              : null,
            createdAt: p.createdAt,
            updatedAt: p.updatedAt,
            isFeatured: !!p.featured,
            featuredPosition: p.featured?.position ?? null,
            isOwner: true,
          };
        }),
      };
    }
  );

  // POST /api/playlists/ - Create playlist
  fastify.post<{ Body: CreatePlaylistBody }>(
    '/',
    { preHandler: authenticate },
    async (request, reply) => {
      const userId = request.user!.id;
      const { name, description, isPublic } = request.body;

      if (!name || name.trim().length === 0 || name.length > 100) {
        return reply.code(400).send({ message: 'Name is required (1-100 characters)' });
      }

      try {
        const playlist = await prisma.playlist.create({
          data: {
            userId,
            name: name.trim(),
            description: description?.trim() || null,
            isPublic: isPublic ?? false,
            updatedAt: new Date(),
          },
        });

        return reply.code(201).send(playlist);
      } catch (error: any) {
        if (error.code === 'P2002') {
          return reply
            .code(409)
            .send({ message: 'You already have a playlist with this name' });
        }
        throw error;
      }
    }
  );

  // PUT /api/playlists/:id - Update playlist
  fastify.put<{ Params: PlaylistParams; Body: UpdatePlaylistBody }>(
    '/:id',
    { preHandler: authenticate },
    async (request, reply) => {
      const playlistId = parseInt(request.params.id);
      if (isNaN(playlistId)) {
        return reply.code(400).send({ message: 'Invalid playlist ID' });
      }

      const ownership = await checkOwnership(playlistId, request.user!.id);
      if (ownership === null) {
        return reply.code(404).send({ message: 'Playlist not found' });
      }
      if (ownership === false) {
        return reply.code(403).send({ message: 'Not authorized' });
      }

      const { name, description, isPublic } = request.body;
      const data: any = { updatedAt: new Date() };

      if (name !== undefined) {
        if (name.trim().length === 0 || name.length > 100) {
          return reply
            .code(400)
            .send({ message: 'Name must be 1-100 characters' });
        }
        data.name = name.trim();
      }
      if (description !== undefined) data.description = description?.trim() || null;
      if (isPublic !== undefined) data.isPublic = isPublic;

      try {
        const updated = await prisma.playlist.update({
          where: { id: playlistId },
          data,
        });

        return updated;
      } catch (error: any) {
        if (error.code === 'P2002') {
          return reply
            .code(409)
            .send({ message: 'You already have a playlist with this name' });
        }
        throw error;
      }
    }
  );

  // DELETE /api/playlists/:id - Delete playlist
  fastify.delete<{ Params: PlaylistParams }>(
    '/:id',
    { preHandler: authenticate },
    async (request, reply) => {
      const playlistId = parseInt(request.params.id);
      if (isNaN(playlistId)) {
        return reply.code(400).send({ message: 'Invalid playlist ID' });
      }

      const ownership = await checkOwnership(playlistId, request.user!.id);
      if (ownership === null) {
        return reply.code(404).send({ message: 'Playlist not found' });
      }
      if (ownership === false) {
        return reply.code(403).send({ message: 'Not authorized' });
      }

      await prisma.playlist.delete({ where: { id: playlistId } });
      return { success: true };
    }
  );

  // POST /api/playlists/:id/videos - Add video to playlist
  fastify.post<{ Params: PlaylistParams; Body: AddVideoBody }>(
    '/:id/videos',
    { preHandler: authenticate },
    async (request, reply) => {
      const playlistId = parseInt(request.params.id);
      if (isNaN(playlistId)) {
        return reply.code(400).send({ message: 'Invalid playlist ID' });
      }

      const ownership = await checkOwnership(playlistId, request.user!.id);
      if (ownership === null) {
        return reply.code(404).send({ message: 'Playlist not found' });
      }
      if (ownership === false) {
        return reply.code(403).send({ message: 'Not authorized' });
      }

      const { mediaId } = request.body;
      if (!mediaId) {
        return reply.code(400).send({ message: 'mediaId is required' });
      }

      // Check video exists and is published
      const video = await prisma.video.findUnique({
        where: { id: mediaId },
        select: { id: true, isPublished: true },
      });

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

      // Check for duplicate
      const existing = await prisma.playlistVideo.findFirst({
        where: { playlistId, mediaId },
      });

      if (existing) {
        return reply.code(409).send({ message: 'Video already in playlist' });
      }

      // Get next position
      const maxPos = await prisma.playlistVideo.aggregate({
        where: { playlistId },
        _max: { position: true },
      });

      const nextPosition = (maxPos._max.position ?? -1) + 1;

      await prisma.playlistVideo.create({
        data: {
          playlistId,
          mediaId,
          position: nextPosition,
        },
      });

      await updatePlaylistCounters(playlistId);
      return { success: true, position: nextPosition };
    }
  );

  // DELETE /api/playlists/:id/videos/:mediaId - Remove video from playlist
  fastify.delete<{ Params: VideoParams }>(
    '/:id/videos/:mediaId',
    { preHandler: authenticate },
    async (request, reply) => {
      const playlistId = parseInt(request.params.id);
      const mediaId = parseInt(request.params.mediaId);

      if (isNaN(playlistId) || isNaN(mediaId)) {
        return reply.code(400).send({ message: 'Invalid IDs' });
      }

      const ownership = await checkOwnership(playlistId, request.user!.id);
      if (ownership === null) {
        return reply.code(404).send({ message: 'Playlist not found' });
      }
      if (ownership === false) {
        return reply.code(403).send({ message: 'Not authorized' });
      }

      const pv = await prisma.playlistVideo.findFirst({
        where: { playlistId, mediaId },
      });

      if (!pv) {
        return reply.code(404).send({ message: 'Video not in playlist' });
      }

      await prisma.playlistVideo.delete({ where: { id: pv.id } });
      await updatePlaylistCounters(playlistId);
      return { success: true };
    }
  );

  // PUT /api/playlists/:id/videos/reorder - Reorder videos
  fastify.put<{ Params: PlaylistParams; Body: ReorderBody }>(
    '/:id/videos/reorder',
    { preHandler: authenticate },
    async (request, reply) => {
      const playlistId = parseInt(request.params.id);
      if (isNaN(playlistId)) {
        return reply.code(400).send({ message: 'Invalid playlist ID' });
      }

      const ownership = await checkOwnership(playlistId, request.user!.id);
      if (ownership === null) {
        return reply.code(404).send({ message: 'Playlist not found' });
      }
      if (ownership === false) {
        return reply.code(403).send({ message: 'Not authorized' });
      }

      const { items } = request.body;
      if (!items || !Array.isArray(items)) {
        return reply.code(400).send({ message: 'items array is required' });
      }

      await prisma.$transaction(
        items.map((item) =>
          prisma.playlistVideo.updateMany({
            where: { playlistId, mediaId: item.mediaId },
            data: { position: item.position },
          })
        )
      );

      await prisma.playlist.update({
        where: { id: playlistId },
        data: { updatedAt: new Date() },
      });

      return { success: true };
    }
  );

  // POST /api/playlists/:id/share - Generate share token
  fastify.post<{ Params: PlaylistParams }>(
    '/:id/share',
    { preHandler: authenticate },
    async (request, reply) => {
      const playlistId = parseInt(request.params.id);
      if (isNaN(playlistId)) {
        return reply.code(400).send({ message: 'Invalid playlist ID' });
      }

      const ownership = await checkOwnership(playlistId, request.user!.id);
      if (ownership === null) {
        return reply.code(404).send({ message: 'Playlist not found' });
      }
      if (ownership === false) {
        return reply.code(403).send({ message: 'Not authorized' });
      }

      const shareToken = randomUUID();
      await prisma.playlist.update({
        where: { id: playlistId },
        data: { shareToken, updatedAt: new Date() },
      });

      return { shareToken };
    }
  );

  // DELETE /api/playlists/:id/share - Revoke share token
  fastify.delete<{ Params: PlaylistParams }>(
    '/:id/share',
    { preHandler: authenticate },
    async (request, reply) => {
      const playlistId = parseInt(request.params.id);
      if (isNaN(playlistId)) {
        return reply.code(400).send({ message: 'Invalid playlist ID' });
      }

      const ownership = await checkOwnership(playlistId, request.user!.id);
      if (ownership === null) {
        return reply.code(404).send({ message: 'Playlist not found' });
      }
      if (ownership === false) {
        return reply.code(403).send({ message: 'Not authorized' });
      }

      await prisma.playlist.update({
        where: { id: playlistId },
        data: { shareToken: null, updatedAt: new Date() },
      });

      return { success: true };
    }
  );
}
