/**
 * commitAvatarUpload — the load-bearing avatar moderation commit (direct-upload path).
 *
 * /api/uploads/complete only stores R2 bytes (no DB persistence). This function is
 * the commit point: in one transaction it creates the image_uploads moderation row
 * (entity_id=userId — REQUIRED for cascade.promote to find the user; r2_key/mime/
 * size_bytes — REQUIRED for imageApprovalKind.loadInput to fetch the bytes) and
 * stages the avatar as PENDING. Returns the image_uploads id for the IMAGE_APPROVAL job.
 *
 * Idempotent: re-committing the same (userId, r2Key) reuses the existing PENDING row.
 */
import { and, eq } from 'drizzle-orm';
import { imageUploads, users } from '@/server/db/schema.js';
import type { TxDrizzleClient } from '@/server/db/client.js';
import { resolveImageNotifications } from '@/server/db/queries/notifications.js';

export interface CommitAvatarUploadInput {
  userId: string;
  /** Content-addressed R2 key from /api/uploads/complete, e.g. originals/<sha256>.jpg */
  r2Key: string;
  mime: string;
  sizeBytes: number;
}

export async function commitAvatarUpload(
  db: TxDrizzleClient,
  input: CommitAvatarUploadInput,
): Promise<string> {
  const { userId, r2Key, mime, sizeBytes } = input;
  // Invariant guard — all four are load-bearing (r2_key → loadInput bytes;
  // entity_id → cascade promote; mime/size_bytes are NOT NULL on image_uploads).
  if (!userId || !r2Key || !mime || !sizeBytes) {
    throw new Error('commitAvatarUpload: missing required field (userId/r2Key/mime/sizeBytes)');
  }

  return db.transaction(async (tx) => {
    // Idempotency: reuse an existing PENDING avatar row for the same key.
    const [existing] = await tx
      .select({ id: imageUploads.id })
      .from(imageUploads)
      .where(
        and(
          eq(imageUploads.entityType, 'user'),
          eq(imageUploads.entityId, userId),
          eq(imageUploads.purpose, 'avatar'),
          eq(imageUploads.r2Key, r2Key),
          eq(imageUploads.approvalStatus, 'PENDING'),
        ),
      )
      .limit(1);

    let imageId: string;
    if (existing) {
      imageId = existing.id;
    } else {
      const [ins] = await tx
        .insert(imageUploads)
        .values({
          uploaderUserId: userId,
          r2Key,
          mime,
          sizeBytes,
          approvalStatus: 'PENDING',
          purpose: 'avatar',
          entityType: 'user',
          entityId: userId,
        })
        .returning({ id: imageUploads.id });
      imageId = ins!.id;
    }

    // Stage the avatar in the pending slot (mirrors the removed setUserAvatarPending,
    // inlined here for atomicity with the image_uploads row).
    await tx
      .update(users)
      .set({
        avatarType: 'UPLOADED',
        pendingAvatarValue: r2Key,
        avatarApprovalStatus: 'PENDING',
        avatarRejectReasonCode: null,
      })
      .where(eq(users.id, userId));

    await resolveImageNotifications(tx, {
      entityType: 'user',
      entityId: userId,
      purpose: 'avatar',
    });

    return imageId;
  });
}
