/**
 * Batch fetch user names from Better Auth user table
 *
 * This helper efficiently fetches multiple user names in parallel queries,
 * avoiding the N+1 query problem when displaying creator names in document lists.
 * Note: Uses parallel individual lookups since Better Auth adapter doesn't support IN queries.
 */

import { isRecord, getString } from '../../lib/utils/type-guards';
import { components } from '../_generated/api';
import type { QueryCtx } from '../_generated/server';
import { isExternalOwnerId } from '../identities/external_identities';

/**
 * Batch fetch user display names for multiple user IDs
 *
 * @param ctx - Query context
 * @param userIds - Array of user IDs to fetch names for
 * @returns Map of userId -> display name (name or email)
 */
export async function getUserNamesBatch(
  ctx: QueryCtx,
  userIds: string[],
): Promise<Map<string, string>> {
  const result = new Map<string, string>();

  // Filter out empty/undefined IDs and deduplicate
  const uniqueIds = [...new Set(userIds.filter((id) => id))];

  if (uniqueIds.length === 0) {
    return result;
  }

  // External / sentinel owners (e.g. `slack:U123`) are not Better Auth users —
  // resolve their display name from externalIdentities and skip the adapter
  // (which throws on non-Convex-id strings). `'system'` has no display name.
  const externalIds = uniqueIds.filter((id) => isExternalOwnerId(id));
  const internalIds = uniqueIds.filter((id) => !isExternalOwnerId(id));

  await Promise.all(
    externalIds.map(async (ownerId) => {
      const identity = await ctx.db
        .query('externalIdentities')
        .withIndex('by_ownerId', (q) => q.eq('ownerId', ownerId))
        .first();
      if (identity?.displayName) {
        result.set(ownerId, identity.displayName);
      }
    }),
  );

  if (internalIds.length === 0) {
    return result;
  }

  // Fetch all users in parallel batches
  // Better Auth adapter findMany doesn't support IN queries, so we need to batch individual lookups
  // But we can parallelize them for efficiency
  // Note: Better Auth uses _id as the primary key, not id. The adapter routes
  // `{field: '_id'}` lookups through `ctx.db.get(id)`, which throws on any
  // non-Convex-ID string. A single legacy row in usageLedger with a malformed
  // userId would take down the whole usage page — catch per-id and skip.
  const userPromises = internalIds.map(async (userId) => {
    try {
      const userRes = await ctx.runQuery(
        components.betterAuth.adapter.findMany,
        {
          model: 'user',
          paginationOpts: { cursor: null, numItems: 1 },
          where: [{ field: '_id', value: userId, operator: 'eq' }],
        },
      );

      const userRaw = userRes?.page?.[0];
      const user = isRecord(userRaw) ? userRaw : undefined;

      if (user) {
        const displayName = getString(user, 'name') || getString(user, 'email');
        if (displayName) {
          return { userId, displayName };
        }
      }
      return null;
    } catch (err) {
      // Invalid / malformed userId (length mismatch, non-ID string, etc).
      // Fall back to showing the raw id in the UI rather than crashing.
      console.warn(
        `[getUserNamesBatch] lookup failed for userId='${userId}': ${
          err instanceof Error ? err.message : String(err)
        }`,
      );
      return null;
    }
  });

  const users = await Promise.all(userPromises);

  for (const user of users) {
    if (user) {
      result.set(user.userId, user.displayName);
    }
  }

  return result;
}
