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

// Helper to check if user is a member of a space
async function isMember(ctx: any, spaceId: string, userId: string) {
  const member = await ctx.db
    .query("spaceMembers")
    .withIndex("by_space_and_user", (q: any) =>
      q.eq("spaceId", spaceId).eq("userId", userId)
    )
    .first();
  return member && !member.isKicked;
}

// Update user presence
export const update = mutation({
  args: {
    spaceId: v.id("spaces"),
    roomId: v.optional(v.id("rooms")),
    status: v.union(
      v.literal("online"),
      v.literal("idle"),
      v.literal("offline")
    ),
  },
  handler: async (ctx, args) => {
    const userId = await getAuthUserId(ctx);
    if (!userId) throw new Error("Not authenticated");

    // Check if user is a member of the space
    if (!(await isMember(ctx, args.spaceId, userId))) {
      throw new Error("Not authorized");
    }

    // Find existing presence record
    const existing = await ctx.db
      .query("userPresence")
      .withIndex("by_user", (q) => q.eq("userId", userId))
      .first();

    const previousRoomId = existing?.roomId;
    const newRoomId = args.roomId;

    if (existing) {
      await ctx.db.patch(existing._id, {
        spaceId: args.spaceId,
        roomId: args.roomId,
        status: args.status,
        lastSeen: Date.now(),
      });
    } else {
      await ctx.db.insert("userPresence", {
        userId,
        spaceId: args.spaceId,
        roomId: args.roomId,
        status: args.status,
        lastSeen: Date.now(),
      });
    }

    // Handle radio room presence changes
    if (previousRoomId !== newRoomId && args.status === "online") {
      // User left a room
      if (previousRoomId) {
        const previousRoom = await ctx.db.get(previousRoomId);
        if (previousRoom?.type === "radio") {
          await ctx.scheduler.runAfter(
            0,
            internal.radioRooms.updateRadioRoomPresence,
            { roomId: previousRoomId, isEntering: false, userId }
          );
        }
      }

      // User entered a new room
      if (newRoomId) {
        const newRoom = await ctx.db.get(newRoomId);
        if (newRoom?.type === "radio") {
          await ctx.scheduler.runAfter(
            0,
            internal.radioRooms.updateRadioRoomPresence,
            { roomId: newRoomId, isEntering: true, userId }
          );
        }
      }
    }
  },
});

// Track active users in space
export const getActiveInSpace = query({
  args: { spaceId: v.id("spaces") },
  handler: async (ctx, args) => {
    const userId = await getAuthUserId(ctx);
    if (!userId) throw new Error("Not authenticated");

    // Check if user is a member
    if (!(await isMember(ctx, args.spaceId, userId))) {
      throw new Error("Not authorized");
    }

    const presences = await ctx.db
      .query("userPresence")
      .withIndex("by_space", (q) => q.eq("spaceId", args.spaceId))
      .collect();

    // Filter to online/idle users (within last 5 minutes)
    const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;
    const activePresences = presences.filter(
      (p) => p.lastSeen > fiveMinutesAgo && p.status !== "offline"
    );

    // Get user details
    const presencesWithUsers = await Promise.all(
      activePresences.map(async (p) => {
        const user = await ctx.db.get(p.userId);
        return { ...p, user };
      })
    );

    return presencesWithUsers;
  },
});

// Track active users in room
export const getActiveInRoom = 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) throw new Error("Room not found");

    // Check if user is a member
    if (!(await isMember(ctx, room.spaceId, userId))) {
      throw new Error("Not authorized");
    }

    const presences = await ctx.db
      .query("userPresence")
      .withIndex("by_room", (q) => q.eq("roomId", args.roomId))
      .collect();

    // Filter to online/idle users (within last 5 minutes)
    const fiveMinutesAgo = Date.now() - 5 * 60 * 1000;
    const activePresences = presences.filter(
      (p) => p.lastSeen > fiveMinutesAgo && p.status !== "offline"
    );

    // Get user details with profiles
    const presencesWithUsers = await Promise.all(
      activePresences.map(async (p) => {
        const user = await ctx.db.get(p.userId);
        const profile = await ctx.db
          .query("userProfiles")
          .withIndex("by_user", (q) => q.eq("userId", p.userId))
          .first();
        return { ...p, user, profile };
      })
    );

    return presencesWithUsers;
  },
});

// Cleanup stale presence (called periodically)
export const cleanup = internalMutation({
  args: {},
  handler: async (ctx) => {
    // Mark users as offline if they haven't been seen in 10 minutes
    const tenMinutesAgo = Date.now() - 10 * 60 * 1000;

    const stalePresences = await ctx.db
      .query("userPresence")
      .collect();

    const updates = stalePresences
      .filter((p) => p.lastSeen < tenMinutesAgo && p.status !== "offline")
      .map((p) => ctx.db.patch(p._id, { status: "offline" as const }));

    await Promise.all(updates);
  },
});
