import { eq, inArray } from "drizzle-orm";
import { database } from "~/database/context";
import * as schema from "~/database/schema";

export type User = typeof schema.users.$inferSelect;
export type NewUser = typeof schema.users.$inferInsert;

/**
 * Returns the best available display identifier for a user.
 * Prefers Clerk username; falls back to displayName (always set from
 * firstName+lastName or email prefix). Returns null only if both are absent.
 */
export function getUserDisplayName(
  user: Pick<User, "username" | "displayName">
): string | null {
  return user.username ?? user.displayName ?? null;
}

export async function createUser(data: NewUser): Promise<User> {
  const db = database();
  const [user] = await db.insert(schema.users).values(data).returning();
  return user;
}

export async function findUserById(id: string): Promise<User | undefined> {
  const db = database();
  return await db.query.users.findFirst({
    where: eq(schema.users.id, id),
  });
}

export async function findUserByClerkId(
  clerkId: string
): Promise<User | undefined> {
  const db = database();
  return await db.query.users.findFirst({
    where: eq(schema.users.clerkId, clerkId),
  });
}

export async function findUsersByClerkIds(clerkIds: string[]): Promise<User[]> {
  if (clerkIds.length === 0) return [];
  const db = database();
  return await db
    .select()
    .from(schema.users)
    .where(inArray(schema.users.clerkId, clerkIds));
}

export async function updateUser(
  id: string,
  data: Partial<NewUser>
): Promise<User> {
  const db = database();
  const [user] = await db
    .update(schema.users)
    .set({ ...data, updatedAt: new Date() })
    .where(eq(schema.users.id, id))
    .returning();
  return user;
}

export async function updateUserByClerkId(
  clerkId: string,
  data: Partial<NewUser>
): Promise<User> {
  const db = database();
  const [user] = await db
    .update(schema.users)
    .set({ ...data, updatedAt: new Date() })
    .where(eq(schema.users.clerkId, clerkId))
    .returning();
  return user;
}

export async function findOrCreateUser(clerkUser: {
  id: string;
  emailAddresses?: Array<{ emailAddress: string }>;
  username?: string | null;
  firstName?: string | null;
  lastName?: string | null;
  imageUrl?: string;
}): Promise<User> {
  // Try to find existing user
  const existingUser = await findUserByClerkId(clerkUser.id);
  
  const email = clerkUser.emailAddresses?.[0]?.emailAddress;
  if (!email) {
    throw new Error("User must have an email address");
  }

  // Generate display name: firstName lastName, or email username
  let displayName = "";
  if (clerkUser.firstName || clerkUser.lastName) {
    displayName = [clerkUser.firstName, clerkUser.lastName]
      .filter(Boolean)
      .join(" ")
      .trim();
  } else {
    // Use email username as fallback
    displayName = email.split("@")[0];
  }

  if (existingUser) {
    // Update user info in case it changed in Clerk
    return await updateUserByClerkId(clerkUser.id, {
      email,
      username: clerkUser.username || undefined,
      displayName,
      firstName: clerkUser.firstName || undefined,
      lastName: clerkUser.lastName || undefined,
      imageUrl: clerkUser.imageUrl,
    });
  }

  // Create new user
  return await createUser({
    clerkId: clerkUser.id,
    email,
    username: clerkUser.username || undefined,
    displayName,
    firstName: clerkUser.firstName || undefined,
    lastName: clerkUser.lastName || undefined,
    imageUrl: clerkUser.imageUrl,
  });
}

export async function deleteUser(id: string): Promise<void> {
  const db = database();
  await db.delete(schema.users).where(eq(schema.users.id, id));
}

export async function findAdmins(): Promise<User[]> {
  const db = database();
  return await db.query.users.findMany({
    where: eq(schema.users.isAdmin, true),
    orderBy: (users, { asc }) => [asc(users.displayName)],
  });
}

export async function isUserAdmin(userId: string): Promise<boolean> {
  const user = await findUserById(userId);
  return user?.isAdmin ?? false;
}

export async function isUserAdminByClerkId(clerkId: string): Promise<boolean> {
  if (process.env.NODE_ENV === "development" && process.env.DEV_ADMIN_CLERK_ID === clerkId) {
    return true;
  }
  const user = await findUserByClerkId(clerkId);
  return user?.isAdmin ?? false;
}

export async function setUserAdmin(userId: string, isAdmin: boolean): Promise<User> {
  return await updateUser(userId, { isAdmin });
}

export async function findAllUsers(): Promise<User[]> {
  const db = database();
  return await db.query.users.findMany({
    orderBy: (users, { asc }) => [asc(users.displayName)],
  });
}
