import { v } from "convex/values";
import { mutation, MutationCtx, query, QueryCtx } from "./_generated/server";

// MARK: - Helper Functions

/**
 * Get current user or throw an error if not authenticated
 */
export async function getCurrentUserOrThrow(ctx: QueryCtx | MutationCtx) {
  const userRecord = await getCurrentUser(ctx);
  if (!userRecord) throw new Error("Can't get current user");
  return userRecord;
}

/**
 * Get current user from auth context
 */
export async function getCurrentUser(ctx: QueryCtx | MutationCtx) {
  const identity = await ctx.auth.getUserIdentity();
  if (identity === null) {
    return null;
  }
  return await userByTokenIdentifier(ctx, identity.tokenIdentifier);
}

/**
 * Get user by tokenIdentifier
 */
async function userByTokenIdentifier(
  ctx: QueryCtx | MutationCtx,
  tokenIdentifier: string
) {
  return await ctx.db
    .query("users")
    .withIndex("by_token", (q) => q.eq("tokenIdentifier", tokenIdentifier))
    .unique();
}

// MARK: - Mutations

/**
 * Get or create a user based on the authenticated token
 * This mutation will be called whenever a user authenticates
 */
export const upsertUser = mutation({
  args: {},
  handler: async (ctx) => {
    const identity = await ctx.auth.getUserIdentity();

    if (!identity) {
      throw new Error("Not authenticated");
    }

    // Extract email from identity
    const email = identity.email || "";
    const name = identity.name;

    // Check if user already exists
    const existingUser = await userByTokenIdentifier(
      ctx,
      identity.tokenIdentifier
    );

    if (existingUser) {
      // Update user if any information has changed
      await ctx.db.patch(existingUser._id, {
        email: email,
        username: name,
      });
      return existingUser._id;
    }

    // Create new user
    const userId = await ctx.db.insert("users", {
      email: email,
      tokenIdentifier: identity.tokenIdentifier,
      username: name,
    });

    return userId;
  },
});

// MARK: - Queries

/**
 * Get the current authenticated user
 */
export const current = query({
  args: {},
  handler: async (ctx) => {
    return await getCurrentUser(ctx);
  },
});

/**
 * Get user by ID
 */
export const getUser = query({
  args: { userId: v.id("users") },
  handler: async (ctx, args) => {
    return await ctx.db.get(args.userId);
  },
});

/**
 * Update username for the current user
 */
export const updateUsername = mutation({
  args: {
    username: v.string(),
  },
  handler: async (ctx, args) => {
    const identity = await ctx.auth.getUserIdentity();

    if (!identity) {
      throw new Error("Not authenticated");
    }

    // Check if user exists
    const existingUser = await userByTokenIdentifier(
      ctx,
      identity.tokenIdentifier
    );

    if (existingUser) {
      // Update username
      await ctx.db.patch(existingUser._id, {
        username: args.username,
      });
      return existingUser._id;
    }

    // If user doesn't exist, create with username
    // Get email from identity
    const email = identity.email || "";

    const userId = await ctx.db.insert("users", {
      email: email,
      tokenIdentifier: identity.tokenIdentifier,
      username: args.username,
    });

    return userId;
  },
});
