"use client";

import Image from "next/image";
import { useEffect, useState } from "react";
import Cropper, { type Area } from "react-easy-crop";
import { ImagePlus, Loader2, Upload, X } from "lucide-react";
import { toStorageUrl } from "@/lib/storage-url";

const outputWidth = 1000;
const outputHeight = 600;
const outputAspect = outputWidth / outputHeight;

type ModelImageUploadFieldProps = {
  imageUrl: string;
  isUploading: boolean;
  onUpload: (image: Blob) => Promise<void>;
  onError: (message: string) => void;
};

export function ModelImageUploadField({
  imageUrl,
  isUploading,
  onUpload,
  onError,
}: ModelImageUploadFieldProps) {
  const [selectedImageUrl, setSelectedImageUrl] = useState("");
  const [crop, setCrop] = useState({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(1);
  const [croppedArea, setCroppedArea] = useState<Area | null>(null);
  const [isProcessing, setIsProcessing] = useState(false);

  const isBusy = isUploading || isProcessing;
  const previewImageUrl = toStorageUrl(imageUrl);

  useEffect(() => {
    return () => {
      if (selectedImageUrl) {
        URL.revokeObjectURL(selectedImageUrl);
      }
    };
  }, [selectedImageUrl]);

  function closeCropper() {
    if (selectedImageUrl) {
      URL.revokeObjectURL(selectedImageUrl);
    }

    setSelectedImageUrl("");
    setCrop({ x: 0, y: 0 });
    setZoom(1);
    setCroppedArea(null);
  }

  function chooseImage(event: React.ChangeEvent<HTMLInputElement>) {
    const file = event.target.files?.[0];
    event.target.value = "";

    if (!file) {
      return;
    }

    if (!file.type.startsWith("image/")) {
      onError("Choose a valid image file.");
      return;
    }

    if (selectedImageUrl) {
      URL.revokeObjectURL(selectedImageUrl);
    }

    setSelectedImageUrl(URL.createObjectURL(file));
    setCrop({ x: 0, y: 0 });
    setZoom(1);
    setCroppedArea(null);
  }

  async function uploadProcessedImage() {
    if (!selectedImageUrl || !croppedArea) {
      onError("Select and crop an image before uploading.");
      return;
    }

    setIsProcessing(true);

    try {
      const processedImage = await cropAndConvertToWebp(selectedImageUrl, croppedArea);
      await onUpload(processedImage);
      closeCropper();
    } catch (error) {
      onError(error instanceof Error ? error.message : "Could not process model image.");
    } finally {
      setIsProcessing(false);
    }
  }

  return (
    <div>
      <span className="mb-1.5 block text-xs font-black uppercase tracking-[0.14em] text-muted-foreground">
        Model image
      </span>
      <div className="grid gap-3 rounded-md border border-border bg-slate-50 p-3 sm:grid-cols-[120px_minmax(0,1fr)] sm:items-center">
        <div className="relative h-24 overflow-hidden rounded-md border border-border bg-white">
          {previewImageUrl ? (
            <Image src={previewImageUrl} alt="Model preview" fill className="object-contain p-3" />
          ) : (
            <div className="grid h-full place-items-center text-muted-foreground">
              <ImagePlus className="h-6 w-6" />
            </div>
          )}
        </div>
        <label className="flex min-w-0 cursor-pointer flex-col items-center justify-center rounded-md border border-dashed border-border bg-white px-4 py-5 text-center transition hover:border-primary/50 hover:bg-primary/5">
          <input
            type="file"
            accept="image/png,image/jpeg,image/webp"
            onChange={chooseImage}
            disabled={isBusy}
            className="sr-only"
          />
          <span className="inline-flex items-center gap-2 text-sm font-black text-foreground">
            {isBusy ? (
              <Loader2 className="h-4 w-4 animate-spin" />
            ) : (
              <ImagePlus className="h-4 w-4" />
            )}
            {isBusy ? "Processing..." : previewImageUrl ? "Replace image" : "Upload image"}
          </span>
          <span className="mt-1 text-xs font-semibold text-muted-foreground">
            Crop 1000x600, remove background, upload WEBP
          </span>
        </label>
      </div>

      {selectedImageUrl ? (
        <div className="fixed inset-0 z-[70] grid place-items-center overflow-y-auto bg-slate-950/60 px-4 py-8">
          <div className="max-h-full w-full max-w-4xl overflow-hidden rounded-lg border border-border bg-white shadow-xl">
            <div className="flex items-center justify-between gap-4 border-b border-border p-4">
              <div>
                <h3 className="text-lg font-black tracking-tight text-foreground">
                  Crop model image
                </h3>
                <p className="mt-1 text-xs font-bold text-muted-foreground">
                  Output size: 1000x600 WEBP with background removal
                </p>
              </div>
              <button
                type="button"
                onClick={closeCropper}
                disabled={isBusy}
                className="grid h-9 w-9 place-items-center rounded-md border border-border text-muted-foreground transition hover:bg-slate-100 hover:text-foreground disabled:opacity-60"
                aria-label="Close image cropper"
              >
                <X className="h-4 w-4" />
              </button>
            </div>

            <div className="p-4">
              <div className="relative aspect-[1000/600] overflow-hidden rounded-md bg-slate-950">
                <Cropper
                  image={selectedImageUrl}
                  crop={crop}
                  zoom={zoom}
                  aspect={outputAspect}
                  onCropChange={setCrop}
                  onZoomChange={setZoom}
                  onCropComplete={(_, croppedPixels) => setCroppedArea(croppedPixels)}
                  objectFit="contain"
                />
              </div>

              <div className="mt-4 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
                <label className="flex flex-1 items-center gap-3 text-sm font-bold text-muted-foreground">
                  Zoom
                  <input
                    type="range"
                    min="1"
                    max="3"
                    step="0.01"
                    value={zoom}
                    onChange={(event) => setZoom(Number(event.target.value))}
                    disabled={isBusy}
                    className="w-full accent-primary"
                  />
                </label>
                <div className="flex flex-col-reverse gap-3 sm:flex-row">
                  <button
                    type="button"
                    onClick={closeCropper}
                    disabled={isBusy}
                    className="h-10 rounded-md border border-border px-4 text-sm font-black text-muted-foreground transition hover:bg-slate-100 hover:text-foreground disabled:opacity-60"
                  >
                    Cancel
                  </button>
                  <button
                    type="button"
                    onClick={uploadProcessedImage}
                    disabled={isBusy}
                    className="inline-flex h-10 items-center justify-center gap-2 rounded-md bg-primary px-4 text-sm font-black text-primary-foreground transition hover:opacity-95 disabled:opacity-60"
                  >
                    {isBusy ? (
                      <Loader2 className="h-4 w-4 animate-spin" />
                    ) : (
                      <Upload className="h-4 w-4" />
                    )}
                    Upload WEBP
                  </button>
                </div>
              </div>
            </div>
          </div>
        </div>
      ) : null}
    </div>
  );
}

async function cropAndConvertToWebp(imageUrl: string, cropPixels: Area) {
  const image = await loadImage(imageUrl);
  const canvas = document.createElement("canvas");
  canvas.width = outputWidth;
  canvas.height = outputHeight;

  const context = canvas.getContext("2d");

  if (!context) {
    throw new Error("Image processing is not available in this browser.");
  }

  context.clearRect(0, 0, outputWidth, outputHeight);
  context.drawImage(
    image,
    cropPixels.x,
    cropPixels.y,
    cropPixels.width,
    cropPixels.height,
    0,
    0,
    outputWidth,
    outputHeight,
  );

  const blob = await new Promise<Blob | null>((resolve) => {
    canvas.toBlob(resolve, "image/webp", 0.92);
  });

  if (!blob) {
    throw new Error("Could not convert image to WEBP.");
  }

  return blob;
}

function loadImage(src: string) {
  return new Promise<HTMLImageElement>((resolve, reject) => {
    const image = new window.Image();
    image.onload = () => resolve(image);
    image.onerror = () => reject(new Error("Could not load selected image."));
    image.src = src;
  });
}
