"use client";

import { useCallback, useState } from "react";
import Cropper from "react-easy-crop";
import { FaSpinner } from "react-icons/fa6";

import Button from "../ui/Button";
import { useModal } from "../../lib/modal-system/ModalContext";

interface Area {
  x: number;
  y: number;
  width: number;
  height: number;
}

interface ImageCropperModalProps {
  image: string;
  aspectRatio?: number;
  onCropped: (croppedImageUrl: string) => void;
}

function getRadianAngle(degreeValue: number) {
  return (degreeValue * Math.PI) / 180;
}

function rotateSize(width: number, height: number, rotation: number) {
  const rotRad = getRadianAngle(rotation);

  return {
    width:
      Math.abs(Math.cos(rotRad) * width) + Math.abs(Math.sin(rotRad) * height),
    height:
      Math.abs(Math.sin(rotRad) * width) + Math.abs(Math.cos(rotRad) * height),
  };
}

function createImage(url: string) {
  return new Promise<HTMLImageElement>((resolve, reject) => {
    const image = new Image();

    image.addEventListener("load", () => resolve(image));
    image.addEventListener("error", reject);

    image.setAttribute("crossOrigin", "anonymous");
    image.src = url;
  });
}

async function getCroppedImg(
  imageSrc: string,
  pixelCrop: Area,
  rotation = 0,
  flip = { horizontal: false, vertical: false },
) {
  const image = await createImage(imageSrc);

  const canvas = document.createElement("canvas");
  const ctx = canvas.getContext("2d");

  if (!ctx) {
    return null;
  }

  const rotRad = getRadianAngle(rotation);

  const { width: bBoxWidth, height: bBoxHeight } = rotateSize(
    image.width,
    image.height,
    rotation,
  );

  canvas.width = bBoxWidth;
  canvas.height = bBoxHeight;

  ctx.translate(bBoxWidth / 2, bBoxHeight / 2);
  ctx.rotate(rotRad);

  ctx.scale(flip.horizontal ? -1 : 1, flip.vertical ? -1 : 1);

  ctx.translate(-image.width / 2, -image.height / 2);

  ctx.drawImage(image, 0, 0);

  const croppedCanvas = document.createElement("canvas");
  const croppedCtx = croppedCanvas.getContext("2d");

  if (!croppedCtx) {
    return null;
  }

  croppedCanvas.width = pixelCrop.width;
  croppedCanvas.height = pixelCrop.height;

  croppedCtx.drawImage(
    canvas,
    pixelCrop.x,
    pixelCrop.y,
    pixelCrop.width,
    pixelCrop.height,
    0,
    0,
    pixelCrop.width,
    pixelCrop.height,
  );

  return new Promise<string | null>((resolve) => {
    croppedCanvas.toBlob((file) => {
      resolve(file ? URL.createObjectURL(file) : null);
    }, "image/jpeg");
  });
}

export default function ImageCropperModal({
  image,
  aspectRatio = 1,
  onCropped,
}: ImageCropperModalProps) {
  const { closeModal } = useModal();

  const [crop, setCrop] = useState({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(1);
  const [rotation, setRotation] = useState(0);

  const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);

  const [isProcessing, setIsProcessing] = useState(false);

  const onCropComplete = useCallback(
    (_croppedArea: Area, croppedAreaPixels: Area) => {
      setCroppedAreaPixels(croppedAreaPixels);
    },
    [],
  );

  const handleZoomSliderChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      setZoom(Number(e.target.value));
    },
    [],
  );

  const handleRotationSliderChange = useCallback(
    (e: React.ChangeEvent<HTMLInputElement>) => {
      setRotation(Number(e.target.value));
    },
    [],
  );

  const handleConfirm = useCallback(async () => {
    if (!croppedAreaPixels) return;

    setIsProcessing(true);

    try {
      const croppedImageUrl = await getCroppedImg(
        image,
        croppedAreaPixels,
        rotation,
      );

      if (!croppedImageUrl) return;

      onCropped(croppedImageUrl);

      closeModal();
    } catch (error) {
      console.error("Error cropping image:", error);
    } finally {
      setIsProcessing(false);
    }
  }, [croppedAreaPixels, image, rotation, onCropped, closeModal]);

  const handleCancel = useCallback(() => {
    closeModal();
  }, [closeModal]);

  return (
    <div className="flex flex-col gap-4">
      <h2 className="font-heading text-lg xl:text-xl 2xl:text-2xl font-semibold text-center text-heading">
        Crop Image
      </h2>

      {/* Cropper Area */}
      <div className="relative h-[300px] w-full overflow-hidden rounded-xl bg-gray-100">
        <Cropper
          image={image}
          crop={crop}
          zoom={zoom}
          rotation={rotation}
          aspect={aspectRatio}
          onCropChange={setCrop}
          onZoomChange={setZoom}
          onCropComplete={onCropComplete}
          restrictPosition={false}
        />
      </div>

      {/* Zoom Control */}
      <div className="flex flex-col gap-2">
        <div className="flex items-center justify-between">
          <label className="text-sm font-medium text-heading">Zoom</label>

          <span className="text-sm text-text">{zoom.toFixed(1)}x</span>
        </div>

        <input
          type="range"
          min={1}
          max={3}
          step={0.1}
          value={zoom}
          onChange={handleZoomSliderChange}
          className="w-full accent-[#9654F4]"
        />
      </div>

      {/* Rotation Control */}
      <div className="flex flex-col gap-2">
        <div className="flex items-center justify-between">
          <label className="text-sm font-medium text-heading">Rotation</label>

          <span className="text-sm text-text">{rotation}°</span>
        </div>

        <input
          type="range"
          min={0}
          max={360}
          step={1}
          value={rotation}
          onChange={handleRotationSliderChange}
          className="w-full accent-[#9654F4]"
        />
      </div>

      {/* Action Buttons */}
      <div className="flex gap-3 mt-2">
        <Button
          type="button"
          variant="white"
          onClick={handleCancel}
          className="flex-1"
          buttonClassName="shadow-none!"
          disabled={isProcessing}
        >
          Cancel
        </Button>

        <Button
          type="button"
          onClick={handleConfirm}
          className="flex-1"
          loading={isProcessing}
        >
          {isProcessing ? (
            <span className="flex items-center justify-center gap-2">
              <FaSpinner className="size-5 animate-spin" />
              Processing...
            </span>
          ) : (
            "Crop"
          )}
        </Button>
      </div>
    </div>
  );
}
