import { useRef, useState } from "react";
import {
  ReactCrop,
  centerCrop,
  convertToPixelCrop,
  makeAspectCrop,
  type Crop,
  type PixelCrop,
} from "react-image-crop";
import "react-image-crop/dist/ReactCrop.css";
import { ImageIcon, Trash2, Upload } from "lucide-react";
import { Button } from "~/components/ui/button";
import { cn } from "~/lib/utils";

interface AvatarUploaderProps {
  uploadUrl: string;
  teamId?: string;
  savedPhotoUrl?: string | null;
  removePhotoIntent?: string;
}

const ACCEPTED_TYPES = ["image/jpeg", "image/png", "image/webp", "image/gif"];

function centerSquareCrop(width: number, height: number): Crop {
  return centerCrop(
    makeAspectCrop({ unit: "%", width: 85 }, 1, width, height),
    width,
    height
  );
}

async function cropImageToBlob(image: HTMLImageElement, crop: PixelCrop): Promise<Blob> {
  const canvas = document.createElement("canvas");
  const scaleX = image.naturalWidth / image.width;
  const scaleY = image.naturalHeight / image.height;
  canvas.width = 256;
  canvas.height = 256;
  const ctx = canvas.getContext("2d");

  if (!ctx) {
    throw new Error("Canvas is not available");
  }

  ctx.drawImage(
    image,
    crop.x * scaleX,
    crop.y * scaleY,
    crop.width * scaleX,
    crop.height * scaleY,
    0,
    0,
    256,
    256
  );

  return await new Promise((resolve, reject) => {
    canvas.toBlob((blob) => {
      if (blob) resolve(blob);
      else reject(new Error("Could not prepare image"));
    }, "image/jpeg", 0.9);
  });
}

export function AvatarUploader({
  uploadUrl,
  teamId,
  savedPhotoUrl,
  removePhotoIntent = "remove-avatar-photo",
}: AvatarUploaderProps) {
  const imageRef = useRef<HTMLImageElement | null>(null);
  const fileInputRef = useRef<HTMLInputElement | null>(null);
  const [imageSrc, setImageSrc] = useState<string | null>(null);
  const [shouldRemoveSavedPhoto, setShouldRemoveSavedPhoto] = useState(false);
  const [fileName, setFileName] = useState("avatar.jpg");
  const [crop, setCrop] = useState<Crop>();
  const [completedCrop, setCompletedCrop] = useState<PixelCrop>();
  const [status, setStatus] = useState<"idle" | "submitting" | "pending" | "error">("idle");
  const [error, setError] = useState<string | null>(null);
  const [isDragging, setIsDragging] = useState(false);

  function prepareFile(file: File | undefined) {
    setError(null);
    setStatus("idle");
    setShouldRemoveSavedPhoto(false);

    if (!file) return;
    if (!ACCEPTED_TYPES.includes(file.type)) {
      setError("Choose a JPG, PNG, WebP, or GIF image.");
      return;
    }
    if (file.size > 5 * 1024 * 1024) {
      setError("Choose an image under 5 MB.");
      return;
    }

    setFileName(file.name);
    const reader = new FileReader();
    reader.addEventListener("load", () => setImageSrc(String(reader.result)));
    reader.readAsDataURL(file);
  }

  function handleFileChange(event: React.ChangeEvent<HTMLInputElement>) {
    prepareFile(event.target.files?.[0]);
    event.target.value = "";
  }

  function clearImage() {
    setImageSrc(null);
    setFileName("avatar.jpg");
    setCrop(undefined);
    setCompletedCrop(undefined);
    setError(null);
    setStatus("idle");
  }

  function handleRemoveSavedPhoto() {
    setShouldRemoveSavedPhoto(true);
    clearImage();
  }

  function handleDrop(event: React.DragEvent<HTMLButtonElement>) {
    event.preventDefault();
    setIsDragging(false);
    prepareFile(event.dataTransfer.files[0]);
  }

  async function handleSubmit() {
    const image = imageRef.current;
    const cropToUse =
      completedCrop ??
      (image && crop ? convertToPixelCrop(crop, image.width, image.height) : undefined);

    if (!image || !cropToUse) {
      setError("Choose and crop an image first.");
      return;
    }

    setStatus("submitting");
    setError(null);

    try {
      const blob = await cropImageToBlob(image, cropToUse);
      const formData = new FormData();
      formData.append("file", blob, fileName.replace(/\.[^.]+$/, ".jpg"));
      if (teamId) formData.append("teamId", teamId);

      const response = await fetch(uploadUrl, { method: "POST", body: formData });
      const result = await response.json().catch(() => ({}));

      if (!response.ok) {
        throw new Error(typeof result.error === "string" ? result.error : "Upload failed.");
      }

      setStatus("pending");
    } catch (err) {
      setStatus("error");
      setError(err instanceof Error ? err.message : "Upload failed.");
    }
  }

  return (
    <div className="space-y-4">
      <input
        ref={fileInputRef}
        type="file"
        accept={ACCEPTED_TYPES.join(",")}
        onChange={handleFileChange}
        className="sr-only"
      />

      {savedPhotoUrl && !shouldRemoveSavedPhoto && !imageSrc && (
        <div className="space-y-3">
          <div className="overflow-hidden rounded-md border border-border bg-background">
            <img src={savedPhotoUrl} alt="Current avatar" className="max-h-[360px] w-full object-cover" />
          </div>
          <Button type="button" variant="outline" onClick={handleRemoveSavedPhoto} className="w-full">
            <Trash2 className="mr-2 h-4 w-4" />
            Remove photo
          </Button>
        </div>
      )}

      {(!savedPhotoUrl || shouldRemoveSavedPhoto) && !imageSrc && (
        <button
          type="button"
          onClick={() => fileInputRef.current?.click()}
          onDragOver={(event) => {
            event.preventDefault();
            setIsDragging(true);
          }}
          onDragEnter={(event) => {
            event.preventDefault();
            setIsDragging(true);
          }}
          onDragLeave={() => setIsDragging(false)}
          onDrop={handleDrop}
          className={cn(
            "flex min-h-40 w-full flex-col items-center justify-center rounded-lg border border-dashed bg-card/40 px-6 py-8 text-center transition-colors",
            isDragging ? "border-[#adf661] bg-[#adf661]/10" : "border-border hover:border-muted-foreground hover:bg-accent/40"
          )}
        >
          <ImageIcon className="mb-3 h-9 w-9 text-muted-foreground" />
          <span className="text-sm font-medium text-muted-foreground">Drop a picture</span>
          <span className="text-xs text-muted-foreground/80">or click to browse</span>
        </button>
      )}

      {imageSrc && (
        <>
          <div className="max-w-sm overflow-hidden rounded-md border border-border bg-background">
            <ReactCrop
              crop={crop}
              onChange={(_, percentCrop) => setCrop(percentCrop)}
              onComplete={(pixelCrop) => setCompletedCrop(pixelCrop)}
              aspect={1}
            >
              <img
                ref={imageRef}
                src={imageSrc}
                alt=""
                onLoad={(event) => {
                  const { width, height } = event.currentTarget;
                  setCrop(centerSquareCrop(width, height));
                }}
                className="max-h-[360px]"
              />
            </ReactCrop>
          </div>

          <Button type="button" variant="outline" onClick={clearImage} className="w-full">
            <Trash2 className="mr-2 h-4 w-4" />
            Remove picture
          </Button>
        </>
      )}

      {status === "pending" && (
        <p className="text-sm text-[#adf661]">Photo submitted for review. It will appear after approval.</p>
      )}
      {error && <p className="text-sm text-destructive">{error}</p>}

      {shouldRemoveSavedPhoto && !imageSrc ? (
        <form method="post">
          <input type="hidden" name="intent" value={removePhotoIntent} />
          <Button type="submit" className="w-full">
            Revert to flag avatar
          </Button>
        </form>
      ) : (
        <Button
          type="button"
          onClick={handleSubmit}
          disabled={!imageSrc || status === "submitting"}
          className="w-full"
        >
          <Upload className="mr-2 h-4 w-4" />
          {status === "submitting" ? "Submitting..." : "Submit photo"}
        </Button>
      )}
    </div>
  );
}
