import { supabase } from './supabase';

const BUCKET = 'profile-avatars';
const MAX_DIM = 512;
const QUALITY = 0.85;

// Resizes the source image to a centred-cropped square ≤ MAX_DIM and
// re-encodes as WebP. Falls back to JPEG if WebP isn't supported (rare).
async function resizeToSquare(file: File): Promise<Blob> {
  const url = URL.createObjectURL(file);
  try {
    const img = await new Promise<HTMLImageElement>((resolve, reject) => {
      const i = new Image();
      i.onload = () => resolve(i);
      i.onerror = () => reject(new Error('image load failed'));
      i.src = url;
    });

    const minSide = Math.min(img.naturalWidth, img.naturalHeight);
    const sx = (img.naturalWidth - minSide) / 2;
    const sy = (img.naturalHeight - minSide) / 2;
    const target = Math.min(MAX_DIM, minSide);

    const canvas = document.createElement('canvas');
    canvas.width = target;
    canvas.height = target;
    const ctx = canvas.getContext('2d');
    if (!ctx) throw new Error('canvas context unavailable');
    ctx.drawImage(img, sx, sy, minSide, minSide, 0, 0, target, target);

    const blob = await new Promise<Blob | null>((resolve) =>
      canvas.toBlob(resolve, 'image/webp', QUALITY),
    );
    if (blob) return blob;

    const jpeg = await new Promise<Blob | null>((resolve) =>
      canvas.toBlob(resolve, 'image/jpeg', QUALITY),
    );
    if (!jpeg) throw new Error('canvas toBlob returned null');
    return jpeg;
  } finally {
    URL.revokeObjectURL(url);
  }
}

export async function uploadAvatar(userId: string, file: File): Promise<string> {
  if (!file.type.startsWith('image/')) {
    throw new Error('only image files are accepted');
  }
  const blob = await resizeToSquare(file);
  return uploadAvatarBlob(userId, blob);
}

// Upload an already-cropped Blob (e.g. from ImageCropDialog) without going
// through the legacy center-crop. Caller is responsible for sizing — the
// dialog already clamps to MAX_DIM via its outputWidth.
export async function uploadAvatarBlob(userId: string, blob: Blob): Promise<string> {
  const ext = blob.type === 'image/webp' ? 'webp' : 'jpg';
  // Random filename so old uploads don't get overwritten before we update
  // the profile row — Supabase Storage CDN caches by URL, so a fresh path
  // also forces clients to fetch the new image.
  const name =
    userId + '/' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 8) + '.' + ext;
  const { error: upErr } = await supabase.storage.from(BUCKET).upload(name, blob, {
    contentType: blob.type,
    cacheControl: '604800',
    upsert: false,
  });
  if (upErr) throw upErr;
  const { data: pub } = supabase.storage.from(BUCKET).getPublicUrl(name);
  return pub.publicUrl;
}

export const AVATAR_TARGET_DIM = MAX_DIM;

export async function deleteAvatarObject(publicUrl: string): Promise<void> {
  // Public URLs look like
  //   https://<host>/storage/v1/object/public/profile-avatars/<path>
  // Extract <path> and remove.
  const marker = '/object/public/' + BUCKET + '/';
  const idx = publicUrl.indexOf(marker);
  if (idx === -1) return;
  const path = publicUrl.slice(idx + marker.length);
  const { error } = await supabase.storage.from(BUCKET).remove([path]);
  if (error) throw error;
}
