import { GraphQLError } from "graphql";
import { builder } from "../builder";
import { VideoType, VideoLikeResponseType, VideoPlayResponseType, VideoLikeStatusType } from "../types/index";
import { videos, video_models, video_likes, video_plays, users, files } from "../../db/schema/index";
import { eq, and, lte, desc, inArray, count } from "drizzle-orm";

async function enrichVideo(db: any, video: any) {
  // Fetch models
  const modelRows = await db
    .select({
      id: users.id,
      artist_name: users.artist_name,
      slug: users.slug,
      avatar: users.avatar,
    })
    .from(video_models)
    .leftJoin(users, eq(video_models.user_id, users.id))
    .where(eq(video_models.video_id, video.id));

  // Fetch movie file
  let movieFile = null;
  if (video.movie) {
    const mf = await db.select().from(files).where(eq(files.id, video.movie)).limit(1);
    movieFile = mf[0] || null;
  }

  // Count likes
  const likesCount = await db.select({ count: count() }).from(video_likes).where(eq(video_likes.video_id, video.id));
  const playsCount = await db.select({ count: count() }).from(video_plays).where(eq(video_plays.video_id, video.id));

  return {
    ...video,
    models: modelRows,
    movie_file: movieFile,
    likes_count: likesCount[0]?.count || 0,
    plays_count: playsCount[0]?.count || 0,
  };
}

builder.queryField("videos", (t) =>
  t.field({
    type: [VideoType],
    args: {
      modelId: t.arg.string(),
      featured: t.arg.boolean(),
      limit: t.arg.int(),
    },
    resolve: async (_root, args, ctx) => {
      let query = ctx.db
        .select({ v: videos })
        .from(videos)
        .where(lte(videos.upload_date, new Date()))
        .orderBy(desc(videos.upload_date));

      if (args.modelId) {
        const videoIds = await ctx.db
          .select({ video_id: video_models.video_id })
          .from(video_models)
          .where(eq(video_models.user_id, args.modelId));

        if (videoIds.length === 0) return [];

        query = ctx.db
          .select({ v: videos })
          .from(videos)
          .where(and(
            lte(videos.upload_date, new Date()),
            inArray(videos.id, videoIds.map((v: any) => v.video_id)),
          ))
          .orderBy(desc(videos.upload_date));
      }

      if (args.featured !== null && args.featured !== undefined) {
        query = ctx.db
          .select({ v: videos })
          .from(videos)
          .where(and(
            lte(videos.upload_date, new Date()),
            eq(videos.featured, args.featured),
          ))
          .orderBy(desc(videos.upload_date));
      }

      if (args.limit) {
        query = (query as any).limit(args.limit);
      }

      const rows = await query;
      const videoList = rows.map((r: any) => r.v || r);
      return Promise.all(videoList.map((v: any) => enrichVideo(ctx.db, v)));
    },
  }),
);

builder.queryField("video", (t) =>
  t.field({
    type: VideoType,
    nullable: true,
    args: {
      slug: t.arg.string({ required: true }),
    },
    resolve: async (_root, args, ctx) => {
      const video = await ctx.db
        .select()
        .from(videos)
        .where(and(eq(videos.slug, args.slug), lte(videos.upload_date, new Date())))
        .limit(1);

      if (!video[0]) return null;
      return enrichVideo(ctx.db, video[0]);
    },
  }),
);

builder.queryField("videoLikeStatus", (t) =>
  t.field({
    type: VideoLikeStatusType,
    args: {
      videoId: t.arg.string({ required: true }),
    },
    resolve: async (_root, args, ctx) => {
      if (!ctx.currentUser) return { liked: false };
      const existing = await ctx.db
        .select()
        .from(video_likes)
        .where(and(eq(video_likes.video_id, args.videoId), eq(video_likes.user_id, ctx.currentUser.id)))
        .limit(1);
      return { liked: existing.length > 0 };
    },
  }),
);

builder.mutationField("likeVideo", (t) =>
  t.field({
    type: VideoLikeResponseType,
    args: {
      videoId: t.arg.string({ required: true }),
    },
    resolve: async (_root, args, ctx) => {
      if (!ctx.currentUser) throw new GraphQLError("Unauthorized");

      const existing = await ctx.db
        .select()
        .from(video_likes)
        .where(and(eq(video_likes.video_id, args.videoId), eq(video_likes.user_id, ctx.currentUser.id)))
        .limit(1);

      if (existing.length > 0) throw new GraphQLError("Already liked");

      await ctx.db.insert(video_likes).values({
        video_id: args.videoId,
        user_id: ctx.currentUser.id,
      });

      await ctx.db
        .update(videos)
        .set({ likes_count: (await ctx.db.select({ c: videos.likes_count }).from(videos).where(eq(videos.id, args.videoId)).limit(1))[0]?.c as number + 1 || 1 })
        .where(eq(videos.id, args.videoId));

      const likesCount = await ctx.db.select({ count: count() }).from(video_likes).where(eq(video_likes.video_id, args.videoId));
      return { liked: true, likes_count: likesCount[0]?.count || 1 };
    },
  }),
);

builder.mutationField("unlikeVideo", (t) =>
  t.field({
    type: VideoLikeResponseType,
    args: {
      videoId: t.arg.string({ required: true }),
    },
    resolve: async (_root, args, ctx) => {
      if (!ctx.currentUser) throw new GraphQLError("Unauthorized");

      const existing = await ctx.db
        .select()
        .from(video_likes)
        .where(and(eq(video_likes.video_id, args.videoId), eq(video_likes.user_id, ctx.currentUser.id)))
        .limit(1);

      if (existing.length === 0) throw new GraphQLError("Not liked");

      await ctx.db
        .delete(video_likes)
        .where(and(eq(video_likes.video_id, args.videoId), eq(video_likes.user_id, ctx.currentUser.id)));

      await ctx.db
        .update(videos)
        .set({ likes_count: Math.max(((await ctx.db.select({ c: videos.likes_count }).from(videos).where(eq(videos.id, args.videoId)).limit(1))[0]?.c as number || 1) - 1, 0) })
        .where(eq(videos.id, args.videoId));

      const likesCount = await ctx.db.select({ count: count() }).from(video_likes).where(eq(video_likes.video_id, args.videoId));
      return { liked: false, likes_count: likesCount[0]?.count || 0 };
    },
  }),
);

builder.mutationField("recordVideoPlay", (t) =>
  t.field({
    type: VideoPlayResponseType,
    args: {
      videoId: t.arg.string({ required: true }),
      sessionId: t.arg.string(),
    },
    resolve: async (_root, args, ctx) => {
      const play = await ctx.db.insert(video_plays).values({
        video_id: args.videoId,
        user_id: ctx.currentUser?.id || null,
        session_id: args.sessionId || null,
      }).returning({ id: video_plays.id });

      const playsCount = await ctx.db.select({ count: count() }).from(video_plays).where(eq(video_plays.video_id, args.videoId));

      await ctx.db
        .update(videos)
        .set({ plays_count: playsCount[0]?.count || 0 })
        .where(eq(videos.id, args.videoId));

      return {
        success: true,
        play_id: play[0].id,
        plays_count: playsCount[0]?.count || 0,
      };
    },
  }),
);

builder.mutationField("updateVideoPlay", (t) =>
  t.field({
    type: "Boolean",
    args: {
      videoId: t.arg.string({ required: true }),
      playId: t.arg.string({ required: true }),
      durationWatched: t.arg.int({ required: true }),
      completed: t.arg.boolean({ required: true }),
    },
    resolve: async (_root, args, ctx) => {
      await ctx.db
        .update(video_plays)
        .set({ duration_watched: args.durationWatched, completed: args.completed, date_updated: new Date() })
        .where(eq(video_plays.id, args.playId));
      return true;
    },
  }),
);

builder.queryField("analytics", (t) =>
  t.field({
    type: "JSON",
    nullable: true,
    resolve: async (_root, _args, ctx) => {
      if (!ctx.currentUser || ctx.currentUser.role !== "model") {
        throw new GraphQLError("Unauthorized");
      }

      const userId = ctx.currentUser.id;

      // Get all videos by this user (via video_models)
      const modelVideoIds = await ctx.db
        .select({ video_id: video_models.video_id })
        .from(video_models)
        .where(eq(video_models.user_id, userId));

      if (modelVideoIds.length === 0) {
        return { total_videos: 0, total_likes: 0, total_plays: 0, plays_by_date: {}, likes_by_date: {}, videos: [] };
      }

      const videoIds = modelVideoIds.map((v: any) => v.video_id);
      const videoList = await ctx.db.select().from(videos).where(inArray(videos.id, videoIds));
      const plays = await ctx.db.select().from(video_plays).where(inArray(video_plays.video_id, videoIds));
      const likes = await ctx.db.select().from(video_likes).where(inArray(video_likes.video_id, videoIds));

      const totalLikes = videoList.reduce((sum, v) => sum + (v.likes_count || 0), 0);
      const totalPlays = videoList.reduce((sum, v) => sum + (v.plays_count || 0), 0);

      const playsByDate = plays.reduce((acc: any, play) => {
        const date = new Date(play.date_created).toISOString().split("T")[0];
        if (!acc[date]) acc[date] = 0;
        acc[date]++;
        return acc;
      }, {});

      const likesByDate = likes.reduce((acc: any, like) => {
        const date = new Date(like.date_created).toISOString().split("T")[0];
        if (!acc[date]) acc[date] = 0;
        acc[date]++;
        return acc;
      }, {});

      const videoAnalytics = videoList.map((video) => {
        const vPlays = plays.filter((p) => p.video_id === video.id);
        const completedPlays = vPlays.filter((p) => p.completed).length;
        const avgWatchTime = vPlays.length > 0
          ? vPlays.reduce((sum, p) => sum + (p.duration_watched || 0), 0) / vPlays.length
          : 0;

        return {
          id: video.id,
          title: video.title,
          slug: video.slug,
          upload_date: video.upload_date,
          likes: video.likes_count || 0,
          plays: video.plays_count || 0,
          completed_plays: completedPlays,
          completion_rate: video.plays_count ? (completedPlays / video.plays_count) * 100 : 0,
          avg_watch_time: Math.round(avgWatchTime),
        };
      });

      return {
        total_videos: videoList.length,
        total_likes: totalLikes,
        total_plays: totalPlays,
        plays_by_date: playsByDate,
        likes_by_date: likesByDate,
        videos: videoAnalytics,
      };
    },
  }),
);
