import {
  useCallback,
  useRef,
  useState,
} from 'react';

import type { Area, Point } from 'react-easy-crop';

import { rpc } from '@/api/request';

export type AspectOption = {
  key: string;
  label: string;
  value: number | undefined;
};

export const ASPECT_OPTIONS: AspectOption[] = [
  { key: 'free', label: 'aspect_free', value: undefined },
  { key: '1:1', label: '1:1', value: 1 },
  { key: '4:3', label: '4:3', value: 4 / 3 },
  { key: '3:4', label: '3:4', value: 3 / 4 },
  { key: '16:9', label: '16:9', value: 16 / 9 },
];

const DEFAULT_ADJUST = {
  brightness: 100,
  contrast: 100,
  saturate: 100,
};

const MIME_BY_EXT: Record<string, string> = {
  jpg: 'image/jpeg',
  jpeg: 'image/jpeg',
  png: 'image/png',
  webp: 'image/webp',
  gif: 'image/gif',
  bmp: 'image/bmp',
  svg: 'image/svg+xml',
};

function guessMimeFromUrl(url: string): string {
  let pathname = url;
  try {
    pathname = new URL(url, window.location.origin).pathname;
  } catch {
    pathname = url;
  }
  const ext = (pathname.split('.').pop() || '').toLowerCase();
  return MIME_BY_EXT[ext] ?? 'image/png';
}

/**
 * Normalizes the server's base64 response into a data: URL and resolves the
 * actual mime type (either from the data: prefix or guessed from the URL).
 */
function toDataUrl(base64: string, imageUrl: string) {
  if (base64.startsWith('data:')) {
    const mimeType = base64.slice(5, base64.indexOf(';')) || 'image/png';
    return { dataUrl: base64, mimeType };
  }
  const mimeType = guessMimeFromUrl(imageUrl);
  return { dataUrl: `data:${mimeType};base64,${base64}`, mimeType };
}

export function useImageEditor() {
  const [objectUrl, setObjectUrl] = useState<string | null>(null);
  const [loading, setLoading] = useState(false);
  const [loadError, setLoadError] = useState(false);
  const [processing, setProcessing] = useState(false);

  const [crop, setCrop] = useState<Point>({ x: 0, y: 0 });
  const [zoom, setZoom] = useState(1);
  const [rotation, setRotation] = useState(0);
  const [flipH, setFlipH] = useState(false);
  const [flipV, setFlipV] = useState(false);
  const [aspect, setAspect] = useState<number | undefined>(undefined);
  const [adjust, setAdjust] = useState(DEFAULT_ADJUST);
  const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null);

  const mimeTypeRef = useRef<string>('image/png');

  const resetTransforms = useCallback(() => {
    setCrop({ x: 0, y: 0 });
    setZoom(1);
    setRotation(0);
    setFlipH(false);
    setFlipV(false);
    setAspect(undefined);
    setAdjust(DEFAULT_ADJUST);
    setCroppedAreaPixels(null);
  }, []);

  const cleanup = useCallback(() => {
    mimeTypeRef.current = 'image/png';
    setObjectUrl(null);
    setLoading(false);
    setProcessing(false);
    setLoadError(false);
    resetTransforms();
  }, [resetTransforms]);

  const load = useCallback(
    async (imageUrl: string) => {
      resetTransforms();
      setObjectUrl(null);
      setLoadError(false);
      setLoading(true);
      try {
        // Fetch the image bytes through the rpc channel (which is CORS-enabled,
        // unlike the raw image host). A data: URL is same-origin and never
        // taints the canvas, so cropping/export work everywhere.
        const response = await rpc.base64({ url: imageUrl });
        const base64 = response?.result;
        if (!base64) {
          throw new Error('Empty base64 response');
        }
        const { dataUrl, mimeType } = toDataUrl(base64, imageUrl);
        mimeTypeRef.current = mimeType;
        setObjectUrl(dataUrl);
      } catch {
        setLoadError(true);
      } finally {
        setLoading(false);
      }
    },
    [resetTransforms],
  );

  const onCropComplete = useCallback((_area: Area, areaPixels: Area) => {
    setCroppedAreaPixels(areaPixels);
  }, []);

  const rotate90 = useCallback(() => {
    setRotation((prev) => (prev + 90) % 360);
  }, []);

  const toggleFlipH = useCallback(() => setFlipH((prev) => !prev), []);
  const toggleFlipV = useCallback(() => setFlipV((prev) => !prev), []);

  const setBrightness = useCallback(
    (value: number) => setAdjust((prev) => ({ ...prev, brightness: value })),
    [],
  );
  const setContrast = useCallback(
    (value: number) => setAdjust((prev) => ({ ...prev, contrast: value })),
    [],
  );
  const setSaturate = useCallback(
    (value: number) => setAdjust((prev) => ({ ...prev, saturate: value })),
    [],
  );

  const selectAspect = useCallback((value: number | undefined) => {
    setAspect(value);
  }, []);

  return {
    objectUrl,
    loading,
    loadError,
    processing,
    setProcessing,
    mimeType: mimeTypeRef,
    crop,
    setCrop,
    zoom,
    setZoom,
    rotation,
    setRotation,
    flipH,
    flipV,
    aspect,
    adjust,
    croppedAreaPixels,
    onCropComplete,
    rotate90,
    toggleFlipH,
    toggleFlipV,
    setBrightness,
    setContrast,
    setSaturate,
    selectAspect,
    reset: resetTransforms,
    load,
    cleanup,
  };
}

export type ImageEditorState = ReturnType<typeof useImageEditor>;
