import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";

import {
  Box,
  Button,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Slider,
  Typography,
} from "@mui/material";

import { useI18n } from "../i18n/I18nProvider";

const CROPPER_SIZE = 280;
const OUTPUT_SIZE = 512;

type DragState = {
  startX: number;
  startY: number;
  originX: number;
  originY: number;
};

function clamp(value: number, min: number, max: number) {
  return Math.min(max, Math.max(min, value));
}

export default function CropImageDialog({
  open,
  file,
  onClose,
  onSave,
}: {
  open: boolean;
  file: File | null;
  onClose: () => void;
  onSave: (blob: Blob) => Promise<void> | void;
}) {
  const { t } = useI18n();
  const [imageUrl, setImageUrl] = useState<string | null>(null);
  const [imageSize, setImageSize] = useState<{ width: number; height: number } | null>(null);
  const [zoom, setZoom] = useState(1);
  const [position, setPosition] = useState({ x: 0, y: 0 });
  const [saving, setSaving] = useState(false);
  const dragRef = useRef<DragState | null>(null);
  const imgRef = useRef<HTMLImageElement | null>(null);

  useEffect(() => {
    if (!file || !open) {
      setImageUrl(null);
      setImageSize(null);
      setZoom(1);
      setPosition({ x: 0, y: 0 });
      return;
    }

    const url = URL.createObjectURL(file);
    setImageUrl(url);
    setImageSize(null);
    setZoom(1);
    setPosition({ x: 0, y: 0 });

    return () => URL.revokeObjectURL(url);
  }, [file, open]);

  const rendered = useMemo(() => {
    if (!imageSize) return null;
    const scale = Math.max(CROPPER_SIZE / imageSize.width, CROPPER_SIZE / imageSize.height) * zoom;
    const width = imageSize.width * scale;
    const height = imageSize.height * scale;
    return { scale, width, height };
  }, [imageSize, zoom]);

  useEffect(() => {
    if (!rendered) return;
    const minX = Math.min(0, CROPPER_SIZE - rendered.width);
    const minY = Math.min(0, CROPPER_SIZE - rendered.height);
    setPosition((prev) => ({
      x: clamp(prev.x, minX, 0),
      y: clamp(prev.y, minY, 0),
    }));
  }, [rendered]);

  const beginDrag = (clientX: number, clientY: number) => {
    dragRef.current = {
      startX: clientX,
      startY: clientY,
      originX: position.x,
      originY: position.y,
    };
  };

  const moveDrag = useCallback((clientX: number, clientY: number) => {
    if (!dragRef.current || !rendered) return;
    const minX = Math.min(0, CROPPER_SIZE - rendered.width);
    const minY = Math.min(0, CROPPER_SIZE - rendered.height);
    const nextX = dragRef.current.originX + (clientX - dragRef.current.startX);
    const nextY = dragRef.current.originY + (clientY - dragRef.current.startY);
    setPosition({ x: clamp(nextX, minX, 0), y: clamp(nextY, minY, 0) });
  }, [rendered]);

  useEffect(() => {
    const onMouseMove = (event: MouseEvent) => moveDrag(event.clientX, event.clientY);
    const onTouchMove = (event: TouchEvent) => {
      const touch = event.touches[0];
      if (touch) moveDrag(touch.clientX, touch.clientY);
    };
    const stop = () => {
      dragRef.current = null;
    };

    window.addEventListener("mousemove", onMouseMove);
    window.addEventListener("mouseup", stop);
    window.addEventListener("touchmove", onTouchMove, { passive: false });
    window.addEventListener("touchend", stop);

    return () => {
      window.removeEventListener("mousemove", onMouseMove);
      window.removeEventListener("mouseup", stop);
      window.removeEventListener("touchmove", onTouchMove);
      window.removeEventListener("touchend", stop);
    };
  }, [moveDrag]);

  const exportCrop = async () => {
    if (!file || !rendered) return;
    const image = imgRef.current;
    if (!image) return;

    const canvas = document.createElement("canvas");
    canvas.width = OUTPUT_SIZE;
    canvas.height = OUTPUT_SIZE;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;

    const cropX = -position.x / rendered.scale;
    const cropY = -position.y / rendered.scale;
    const cropWidth = CROPPER_SIZE / rendered.scale;
    const cropHeight = CROPPER_SIZE / rendered.scale;

    ctx.imageSmoothingEnabled = true;
    ctx.imageSmoothingQuality = "high";
    ctx.drawImage(image, cropX, cropY, cropWidth, cropHeight, 0, 0, OUTPUT_SIZE, OUTPUT_SIZE);

    setSaving(true);
    try {
      const blob = await new Promise<Blob | null>((resolve) => canvas.toBlob(resolve, "image/png", 0.95));
      if (!blob) return;
      await onSave(blob);
    } finally {
      setSaving(false);
    }
  };

  return (
    <Dialog open={open} onClose={saving ? undefined : onClose} fullWidth maxWidth="sm">
      <DialogTitle>{t("cropDialogTitle")}</DialogTitle>
      <DialogContent>
        <Typography variant="body2" sx={{ color: "text.secondary", mb: 2 }}>
          {t("cropDialogBody")}
        </Typography>

        <Box sx={{ display: "flex", justifyContent: "center", mb: 2 }}>
          <Box
            sx={{
              width: CROPPER_SIZE,
              height: CROPPER_SIZE,
              borderRadius: "50%",
              overflow: "hidden",
              position: "relative",
              bgcolor: "grey.100",
              border: "1px solid",
              borderColor: "divider",
              cursor: "grab",
              userSelect: "none",
              touchAction: "none",
              boxShadow: "inset 0 0 0 999px rgba(0,0,0,0.03)",
            }}
            onMouseDown={(e) => beginDrag(e.clientX, e.clientY)}
            onTouchStart={(e) => {
              const touch = e.touches[0];
              if (touch) beginDrag(touch.clientX, touch.clientY);
            }}
          >
            {imageUrl ? (
              <img
                ref={imgRef}
                src={imageUrl}
                alt={t("cropDialogPreviewAlt")}
                onLoad={(e) => {
                  const target = e.currentTarget;
                  setImageSize({ width: target.naturalWidth, height: target.naturalHeight });
                }}
                draggable={false}
                style={{
                  position: "absolute",
                  left: position.x,
                  top: position.y,
                  width: rendered?.width ?? "auto",
                  height: rendered?.height ?? "auto",
                  maxWidth: "none",
                  pointerEvents: "none",
                }}
              />
            ) : null}
          </Box>
        </Box>

        <Box sx={{ px: 1 }}>
          <Typography variant="body2" sx={{ mb: 1, fontWeight: 700 }}>{t("cropDialogZoom")}</Typography>
          <Slider min={1} max={3} step={0.01} value={zoom} onChange={(_, value) => setZoom(value as number)} />
        </Box>
      </DialogContent>
      <DialogActions>
        <Button onClick={onClose} disabled={saving}>{t("cancel")}</Button>
        <Button variant="contained" onClick={() => void exportCrop()} disabled={!file || saving || !rendered}>
          {saving ? t("profileUploading") : t("cropDialogSave")}
        </Button>
      </DialogActions>
    </Dialog>
  );
}
