import { v } from "convex/values";
import { mutation, query } from "./_generated/server";
import { Id } from "./_generated/dataModel";
import { getAuthUserId } from "@convex-dev/auth/server";

// ========== USER PLAYBACK STATE ==========

// Get user's playback state for a room
export const getUserPlaybackState = query({
  args: {
    roomId: v.id("rooms"),
  },
  handler: async (ctx, args) => {
    const userId = await getAuthUserId(ctx);
    if (!userId) return null;

    return await ctx.db
      .query("userPlaybackStates")
      .withIndex("by_user_and_room", (q) =>
        q.eq("userId", userId).eq("roomId", args.roomId)
      )
      .unique();
  },
});

// Update user's playback state
export const updateUserPlaybackState = mutation({
  args: {
    roomId: v.id("rooms"),
    currentTrackId: v.optional(v.id("atoms")),
    isPlaying: v.boolean(),
    position: v.number(),
    volume: v.optional(v.number()),
    followMode: v.optional(
      v.union(v.literal("NONE"), v.literal("ROOM"), v.literal("USER"))
    ),
    followingUserId: v.optional(v.id("users")),
  },
  handler: async (ctx, args) => {
    const userId = await getAuthUserId(ctx);
    if (!userId) throw new Error("Not authenticated");

    const existing = await ctx.db
      .query("userPlaybackStates")
      .withIndex("by_user_and_room", (q) =>
        q.eq("userId", userId).eq("roomId", args.roomId)
      )
      .unique();

    const now = Date.now();
    const updates = {
      currentTrackId: args.currentTrackId,
      isPlaying: args.isPlaying,
      position: args.position,
      timestamp: now,
      volume: args.volume ?? existing?.volume ?? 0.7,
      followMode: args.followMode ?? existing?.followMode ?? "NONE",
      followingUserId: args.followingUserId,
      lastUpdated: now,
    };

    if (existing) {
      await ctx.db.patch(existing._id, updates);
      return existing._id;
    } else {
      return await ctx.db.insert("userPlaybackStates", {
        userId,
        roomId: args.roomId,
        ...updates,
      });
    }
  },
});

// Set follow mode
export const setFollowMode = mutation({
  args: {
    roomId: v.id("rooms"),
    followMode: v.union(v.literal("NONE"), v.literal("ROOM"), v.literal("USER")),
    followingUserId: v.optional(v.id("users")),
  },
  handler: async (ctx, args) => {
    const userId = await getAuthUserId(ctx);
    if (!userId) throw new Error("Not authenticated");

    const existing = await ctx.db
      .query("userPlaybackStates")
      .withIndex("by_user_and_room", (q) =>
        q.eq("userId", userId).eq("roomId", args.roomId)
      )
      .unique();

    if (!existing) {
      // Create initial state with follow mode
      return await ctx.db.insert("userPlaybackStates", {
        userId,
        roomId: args.roomId,
        isPlaying: false,
        position: 0,
        timestamp: Date.now(),
        volume: 0.7,
        followMode: args.followMode,
        followingUserId: args.followingUserId,
        lastUpdated: Date.now(),
      });
    }

    await ctx.db.patch(existing._id, {
      followMode: args.followMode,
      followingUserId: args.followingUserId,
      lastUpdated: Date.now(),
    });

    return existing._id;
  },
});

// ========== ROOM PLAYBACK STATE ==========

// Get room playback state
export const getRoomPlaybackState = query({
  args: {
    roomId: v.id("rooms"),
  },
  handler: async (ctx, args) => {
    const userId = await getAuthUserId(ctx);
    if (!userId) throw new Error("Not authenticated");

    const room = await ctx.db.get(args.roomId);
    if (!room) return null;

    // Check if user has access to the room
    const member = await ctx.db
      .query("spaceMembers")
      .withIndex("by_space_and_user", (q) =>
        q.eq("spaceId", room.spaceId).eq("userId", userId)
      )
      .first();

    if (!member || member.isKicked) {
      throw new Error("Not authorized");
    }

    return room.playbackState || null;
  },
});

// Update room playback state
export const updateRoomPlaybackState = mutation({
  args: {
    roomId: v.id("rooms"),
    currentTrackId: v.optional(v.id("atoms")),
    isPlaying: v.boolean(),
    position: v.number(),
    queue: v.optional(v.array(v.id("atoms"))),
    queueIndex: v.optional(v.number()),
    volume: v.optional(v.number()),
    repeat: v.optional(
      v.union(v.literal("none"), v.literal("one"), v.literal("all"))
    ),
    shuffle: v.optional(v.boolean()),
    radioMode: v.optional(
      v.union(v.literal("OFF"), v.literal("PROMPT"), v.literal("AUTO"))
    ),
    radioPrompt: v.optional(v.string()),
  },
  handler: async (ctx, args) => {
    const userId = await getAuthUserId(ctx);
    if (!userId) throw new Error("Not authenticated");

    const room = await ctx.db.get(args.roomId);
    if (!room) throw new Error("Room not found");

    const now = Date.now();
    const currentState = room.playbackState;

    const newState = {
      currentTrackId: args.currentTrackId ?? currentState?.currentTrackId,
      isPlaying: args.isPlaying,
      position: args.position,
      timestamp: now,
      queue: args.queue ?? currentState?.queue ?? [],
      queueIndex: args.queueIndex ?? currentState?.queueIndex ?? 0,
      volume: args.volume ?? currentState?.volume ?? 0.7,
      repeat: args.repeat ?? currentState?.repeat ?? "none",
      shuffle: args.shuffle ?? currentState?.shuffle ?? false,
      radioMode: args.radioMode ?? currentState?.radioMode ?? "OFF",
      radioPrompt: args.radioPrompt ?? currentState?.radioPrompt,
    };

    await ctx.db.patch(args.roomId, {
      playbackState: newState,
    });
  },
});

// Add track to room queue
export const addToRoomQueue = mutation({
  args: {
    roomId: v.id("rooms"),
    atomId: v.id("atoms"),
  },
  handler: async (ctx, args) => {
    const userId = await getAuthUserId(ctx);
    if (!userId) throw new Error("Not authenticated");

    const room = await ctx.db.get(args.roomId);
    if (!room) throw new Error("Room not found");

    const currentState = room.playbackState;
    const queue = currentState?.queue ?? [];

    await ctx.db.patch(args.roomId, {
      playbackState: {
        ...currentState,
        currentTrackId: currentState?.currentTrackId,
        isPlaying: currentState?.isPlaying ?? false,
        position: currentState?.position ?? 0,
        timestamp: currentState?.timestamp ?? Date.now(),
        queue: [...queue, args.atomId],
        queueIndex: currentState?.queueIndex ?? 0,
        volume: currentState?.volume ?? 0.7,
        repeat: currentState?.repeat ?? "none",
        shuffle: currentState?.shuffle ?? false,
        radioMode: currentState?.radioMode ?? "OFF",
        radioPrompt: currentState?.radioPrompt,
      },
    });
  },
});

// ========== ROOM LEADER ==========

// Set user as room leader
export const setRoomLeader = mutation({
  args: {
    roomId: v.id("rooms"),
    isLeader: v.boolean(),
  },
  handler: async (ctx, args) => {
    const userId = await getAuthUserId(ctx);
    if (!userId) throw new Error("Not authenticated");

    const room = await ctx.db.get(args.roomId);
    if (!room) throw new Error("Room not found");

    if (args.isLeader) {
      // Set current user as leader
      await ctx.db.patch(args.roomId, {
        roomLeaderId: userId,
      });
    } else {
      // Only remove if current user is the leader
      if (room.roomLeaderId === userId) {
        await ctx.db.patch(args.roomId, {
          roomLeaderId: undefined,
        });
      }
    }
  },
});

// Get room leader info
export const getRoomLeader = query({
  args: {
    roomId: v.id("rooms"),
  },
  handler: async (ctx, args) => {
    const userId = await getAuthUserId(ctx);
    if (!userId) throw new Error("Not authenticated");

    const room = await ctx.db.get(args.roomId);
    if (!room?.roomLeaderId) return null;

    // Check if user has access to the room
    const member = await ctx.db
      .query("spaceMembers")
      .withIndex("by_space_and_user", (q) =>
        q.eq("spaceId", room.spaceId).eq("userId", userId)
      )
      .first();

    if (!member || member.isKicked) {
      throw new Error("Not authorized");
    }

    const user = await ctx.db.get(room.roomLeaderId);
    return user;
  },
});

// Sync room playback to leader's playback (called when leader's playback changes)
export const syncRoomToLeader = mutation({
  args: {
    roomId: v.id("rooms"),
  },
  handler: async (ctx, args) => {
    const room = await ctx.db.get(args.roomId);
    if (!room?.roomLeaderId) return;

    // Get leader's playback state
    const leaderState = await ctx.db
      .query("userPlaybackStates")
      .withIndex("by_user_and_room", (q) =>
        q.eq("userId", room.roomLeaderId!).eq("roomId", args.roomId)
      )
      .unique();

    if (!leaderState) return;

    // Update room playback to match leader's state
    const currentState = room.playbackState;
    await ctx.db.patch(args.roomId, {
      playbackState: {
        currentTrackId: leaderState.currentTrackId,
        isPlaying: leaderState.isPlaying,
        position: leaderState.position,
        timestamp: leaderState.timestamp,
        queue: currentState?.queue ?? [],
        queueIndex: currentState?.queueIndex ?? 0,
        volume: currentState?.volume ?? 0.7,
        repeat: currentState?.repeat ?? "none",
        shuffle: currentState?.shuffle ?? false,
        radioMode: currentState?.radioMode ?? "OFF",
        radioPrompt: currentState?.radioPrompt,
      },
    });
  },
});
