import { supabase } from './supabase';

const BUCKET = 'profile-banners';
// 3:1 hero crop. 1500x500 hits the sweet spot between crisp on a wide
// settings preview and a payload that stays well under the 8 MB pre-encode
// budget we accept from the user (post-WebP that's typically ~150–300 KB).
const TARGET_WIDTH = 1500;
const TARGET_HEIGHT = 500;
const QUALITY = 0.82;
const MAX_INPUT_BYTES = 8 * 1024 * 1024;

// Resizes the source image to a centred 3:1 crop at TARGET_WIDTH x TARGET_HEIGHT
// and re-encodes as WebP (JPEG fallback if WebP encode unsupported).
async function resizeToBanner(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 srcRatio = img.naturalWidth / img.naturalHeight;
    const targetRatio = TARGET_WIDTH / TARGET_HEIGHT;
    let sx = 0;
    let sy = 0;
    let sw = img.naturalWidth;
    let sh = img.naturalHeight;
    if (srcRatio > targetRatio) {
      // Source is wider than 3:1 — crop horizontally, keep full height.
      sw = Math.round(img.naturalHeight * targetRatio);
      sx = Math.round((img.naturalWidth - sw) / 2);
    } else if (srcRatio < targetRatio) {
      // Source is taller than 3:1 — crop vertically, keep full width.
      sh = Math.round(img.naturalWidth / targetRatio);
      sy = Math.round((img.naturalHeight - sh) / 2);
    }

    // Don't upscale — if the source crop is smaller than the target, render
    // at the source crop size so we don't waste bytes on synthetic detail.
    const outWidth = Math.min(TARGET_WIDTH, sw);
    const outHeight = Math.min(TARGET_HEIGHT, sh);

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

    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 uploadBanner(userId: string, file: File): Promise<string> {
  if (!file.type.startsWith('image/')) {
    throw new Error('only image files are accepted');
  }
  if (file.size > MAX_INPUT_BYTES) {
    throw new Error('image must be 8 MB or smaller');
  }
  const blob = await resizeToBanner(file);
  return uploadBannerBlob(userId, blob);
}

// Upload an already-cropped Blob (e.g. from ImageCropDialog). Skips the
// legacy center-crop path so the user-chosen framing is preserved.
export async function uploadBannerBlob(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 BANNER_TARGET_WIDTH = TARGET_WIDTH;
export const BANNER_TARGET_HEIGHT = TARGET_HEIGHT;
export const BANNER_MAX_INPUT_BYTES = MAX_INPUT_BYTES;

export async function deleteBannerObject(publicUrl: string): Promise<void> {
  // Public URLs look like
  //   https://<host>/storage/v1/object/public/profile-banners/<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;
}
