import React, { ChangeEvent, useEffect, useRef, useState } from "react";
import DragableBox from "./DragableBox";
import { Coordinates } from "~/types";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import { Label } from "@/components/ui/label";
import { useSearchParams } from "@remix-run/react";

type ContainerDimensions = {
  height: number;
  width: number;
};

type FileDimensions = {
  x: number;
  y: number;
};

type UnitType = "pixels" | "percentage";

interface ComponentProps {
  image?: string;
}

export default function CropSelector({ image }: ComponentProps) {
  const [firstDragable, setFirstDragable] = useState<Coordinates>({
    x: 0,
    y: 0,
  });
  const [secondDragable, setSecondDragable] = useState<Coordinates>({
    x: 100,
    y: 100,
  });
  const [selectorPosition, setSelectorPosition] = useState<Coordinates>({
    x: 0,
    y: 0,
  });
  const [containerSize, setContainerSize] = useState<ContainerDimensions>({
    height: 0,
    width: 0,
  });
  const [pixelDimensions, setPixelDimensions] = useState<ContainerDimensions>({
    height: 100,
    width: 100,
  });
  const [unit, setUnit] = useState<UnitType>("pixels");
  const [searchParams, setSearchParams] = useSearchParams();
  const containerRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    setSelectorPosition({
      x: Math.min(firstDragable.x, secondDragable.x),
      y: Math.min(firstDragable.y, secondDragable.y),
    });
    const setUrlParams = setTimeout(() => {
      writeDimensionsToURL();
    }, 500);

    return () => clearTimeout(setUrlParams);
  }, [firstDragable, secondDragable]);

  useEffect(() => {
    if (!containerRef.current) return;

    const resizeObserver = new ResizeObserver((entries) => {
      const entry = entries[0];
      setContainerSize({
        width: entry.contentRect.width,
        height: entry.contentRect.height,
      });
    });

    resizeObserver.observe(containerRef.current);

    return () => resizeObserver.disconnect();
  }, [containerRef]);

  const selectorWidth = Math.abs(secondDragable.x - firstDragable.x);
  const selectorHeight = Math.abs(secondDragable.y - firstDragable.y);

  const handleSelectorDrag = (newPosition: Coordinates) => {
    const deltaX = newPosition.x - selectorPosition.x;
    const deltaY = newPosition.y - selectorPosition.y;

    const newFirstX = Math.min(firstDragable.x, secondDragable.x) + deltaX;
    const newFirstY = Math.min(firstDragable.y, secondDragable.y) + deltaY;
    const newSecondX = Math.max(firstDragable.x, secondDragable.x) + deltaX;
    const newSecondY = Math.max(firstDragable.y, secondDragable.y) + deltaY;

    if (newFirstX >= 0 && newSecondX <= 100 && newFirstY >= 0 && newSecondY <= 100) {
      setFirstDragable({
        x: newFirstX,
        y: newFirstY,
      });
      setSecondDragable({
        x: newSecondX,
        y: newSecondY,
      });
    }
  };

  const pixelsToPercentage = (pixels: number, totalSize: number): number => {
    return totalSize > 0 ? (pixels / totalSize) * 100 : 0;
  };

  const percentageToPixels = (percentage: number, totalSize: number): number => {
    return (percentage * totalSize) / 100;
  };

  const getActualPixelDimensions = (): ContainerDimensions => {
    return {
      width: Math.round(percentageToPixels(selectorWidth, containerSize.width)),
      height: Math.round(percentageToPixels(selectorHeight, containerSize.height)),
    };
  };

  const writeDimensionsToURL = (): void => {
    setSearchParams((prev) => ({
      ...prev,
      x1: ((firstDragable.x / 100) * containerSize.width).toFixed(),
      y1: ((firstDragable.y / 100) * containerSize.height).toFixed(),
      x2: ((secondDragable.x / 100) * containerSize.width).toFixed(),
      y2: ((secondDragable.y / 100) * containerSize.height).toFixed(),
    }));
  };

  const getDisplayValue = (dimension: "width" | "height"): number => {
    const actualPixels = getActualPixelDimensions();
    if (unit === "percentage") {
      return dimension === "width" ? selectorWidth : selectorHeight;
    }
    return dimension === "width" ? actualPixels.width : actualPixels.height;
  };

  const handleDimensionChange = (
    dimension: "width" | "height",
    event: React.ChangeEvent<HTMLInputElement>
  ) => {
    const inputValue = Math.max(0, parseFloat(event.target.value) || 0);
    let newPercentage: number;

    if (unit === "percentage") {
      newPercentage = Math.min(inputValue, 100);
    } else {
      // Convert pixel input to percentage
      const totalSize = dimension === "width" ? containerSize.width : containerSize.height;
      newPercentage = pixelsToPercentage(Math.min(inputValue, totalSize), totalSize);
    }

    if (dimension === "width") {
      const newX = Math.min(firstDragable.x, 100 - newPercentage);
      setFirstDragable((prev) => ({ ...prev, x: newX }));
      setSecondDragable((prev) => ({ ...prev, x: newX + newPercentage }));
    } else {
      const newY = Math.min(firstDragable.y, 100 - newPercentage);
      setFirstDragable((prev) => ({ ...prev, y: newY }));
      setSecondDragable((prev) => ({ ...prev, y: newY + newPercentage }));
    }
  };

  return (
    <div className="flex flex-col w-full p-4 h-fit justify-center items-center gap-4">
      <div
        ref={containerRef}
        className="relative w-auto h-fit min-w-40 min-h-40 border-black border"
      >
        <DragableBox
          position={selectorPosition}
          mode="box"
          width={selectorWidth}
          height={selectorHeight}
          coordSetter={setSelectorPosition}
          onDrag={handleSelectorDrag}
          className="border border-black"
        />
        <DragableBox position={firstDragable} coordSetter={setFirstDragable} mode="handle">
          <div className="bg-red-600 h-2 w-2 rounded-full hover:scale-125" />
        </DragableBox>
        <img src={image} />
        <DragableBox position={secondDragable} coordSetter={setSecondDragable} mode="handle">
          <div className="bg-red-600 h-2 w-2 rounded-full hover:scale-125" />
        </DragableBox>
      </div>

      <RadioGroup
        defaultValue="pixels"
        value={unit}
        onValueChange={(value) => setUnit(value as UnitType)}
        className="flex gap-4 mb-2"
      >
        <div className="flex items-center space-x-2">
          <RadioGroupItem value="pixels" id="pixels" />
          <Label htmlFor="pixels">Pixels</Label>
        </div>
        <div className="flex items-center space-x-2">
          <RadioGroupItem value="percentage" id="percentage" />
          <Label htmlFor="percentage">Percentage</Label>
        </div>
      </RadioGroup>

      <div className="flex gap-4">
        <div className="flex flex-col gap-2">
          <label htmlFor="width">Width ({unit === "pixels" ? "px" : "%"})</label>
          <input
            id="width"
            type="number"
            min={0}
            max={unit === "percentage" ? 100 : containerSize.width}
            value={getDisplayValue("width").toFixed(unit === "percentage" ? 1 : 0)}
            onChange={(e: ChangeEvent<HTMLInputElement>) => handleDimensionChange("width", e)}
            className="w-24"
          />
        </div>
        <div className="flex flex-col gap-2">
          <label htmlFor="height">Height ({unit === "pixels" ? "px" : "%"})</label>
          <input
            id="height"
            type="number"
            min={0}
            max={unit === "percentage" ? 100 : containerSize.height}
            value={getDisplayValue("height").toFixed(unit === "percentage" ? 1 : 0)}
            onChange={(e) => handleDimensionChange("height", e)}
            className="w-24"
          />
        </div>
      </div>
    </div>
  );
}
