import {
  internalMutation,
  internalQuery,
  query,
  QueryCtx,
  action,
  internalAction,
} from "../_generated/server";

import { v } from "convex/values";
import { Doc, Id } from "../_generated/dataModel";
import { createClerkClient, UserJSON } from "@clerk/backend";
import { api, internal } from "../_generated/api";

if (!process.env.CLERK_SECRET_KEY) {
  throw new Error("Missing CLERK_SECRET_KEY environment variable");
}

const clerkClient = createClerkClient({
  secretKey: process.env.CLERK_SECRET_KEY!,
});

export const userLoginStatus = query(
  async (
    ctx
  ): Promise<
    | ["No JWT Token", null]
    | ["No Clerk User", null]
    | ["Logged In", Doc<"users">]
  > => {
    const identity = await ctx.auth.getUserIdentity();
    if (!identity) {
      return ["No JWT Token", null];
    }
    const user = await getCurrentUser(ctx);
    if (user === null) {
      return ["No Clerk User", null];
    }
    return ["Logged In", user];
  }
);

export const currentUser = query((ctx: QueryCtx) => getCurrentUser(ctx));

export const getUser = internalQuery({
  args: { subject: v.string() },
  async handler(ctx, args) {
    return await userQuery(ctx, args.subject);
  },
});

export const updateOrCreateUser = internalMutation({
  args: { clerkUser: v.any() },
  async handler(ctx, { clerkUser }: { clerkUser: UserJSON }) {
    const userRecord = await userQuery(ctx, clerkUser.id);

    if (userRecord === null) {
      await ctx.db.insert("users", { clerkUser });

      const primaryEmail = clerkUser.email_addresses?.find(
        (email) => email.id === clerkUser.primary_email_address_id
      );

      if (primaryEmail?.email_address) {
        try {
          await ctx.scheduler.runAfter(0, api.email.actions.sendWelcomeEmail, {
            to: primaryEmail.email_address,
            username: clerkUser.first_name || clerkUser.username || "User",
          });
        } catch (error) {
          console.error("Failed to send welcome email:", error);
        }
      }
    } else {
      await ctx.db.patch(userRecord._id, { clerkUser });
    }
  },
});

export const deleteUserByClerkId = internalMutation({
  args: { id: v.string() },
  async handler(ctx, { id }) {
    const userRecord = await userQuery(ctx, id);

    if (userRecord === null) {
      console.warn("can't delete user, does not exist", id);
    } else {
      await ctx.db.delete(userRecord._id);
    }
  },
});

export async function userQuery(
  ctx: QueryCtx,
  clerkUserId: string
): Promise<(Omit<Doc<"users">, "clerkUser"> & { clerkUser: UserJSON }) | null> {
  return await ctx.db
    .query("users")
    .withIndex("by_clerk_id", (q) => q.eq("clerkUser.id", clerkUserId))
    .unique();
}

export async function userById(
  ctx: QueryCtx,
  id: Id<"users">
): Promise<(Omit<Doc<"users">, "clerkUser"> & { clerkUser: UserJSON }) | null> {
  return await ctx.db.get(id);
}

async function getCurrentUser(ctx: QueryCtx): Promise<Doc<"users"> | null> {
  const identity = await ctx.auth.getUserIdentity();
  if (identity === null) {
    return null;
  }
  return await userQuery(ctx, identity.subject);
}

export async function mustGetCurrentUser(ctx: QueryCtx): Promise<Doc<"users">> {
  const userRecord = await getCurrentUser(ctx);
  if (!userRecord) throw new Error("Can't get current user");
  return userRecord;
}

export const deleteUser = action({
  args: {},
  handler: async (ctx) => {
    const user = await ctx.runQuery(api.auth.users.currentUser);
    if (!user) {
      throw new Error("Can't get current user");
    }

    await ctx.runAction(internal.auth.users.deleteClerkUser, {
      clerkUserId: user.clerkUser.id,
    });

    await ctx.runMutation(internal.auth.users.deleteConvexUser, {
      userId: user._id,
    });

    return { success: true };
  },
});

export const deleteClerkUser = internalAction({
  args: { clerkUserId: v.string() },
  handler: async (_, { clerkUserId }) => {
    try {
      await clerkClient.users.deleteUser(clerkUserId);
      console.log("Successfully deleted user from Clerk:", clerkUserId);
    } catch (error) {
      console.error("Failed to delete user from Clerk:", error);
      throw new Error("Failed to delete user from Clerk");
    }

    return { success: true };
  },
});

export const deleteConvexUser = internalMutation({
  args: { userId: v.id("users") },
  handler: async (ctx, { userId }) => {
    const user = await ctx.db.get(userId);
    if (!user) {
      console.warn("User not found in Convex:", userId);
      return null;
    }

    await ctx.db.delete(userId);
    console.log("Successfully deleted user from Convex:", userId);
    return null;
  },
});
