import React, { useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';

import {
  DEFAULT_SCENE_VIEW_CAMERA,
  type SceneViewCamera,
} from '../../shared/types/sceneView';

import styles from './RotatedImage.module.css';

type Mode = 'cover' | 'contain';

type RotatedImageProps = {
  url: string;
  rotationDeg: 0 | 90 | 180 | 270;
  mode: Mode;
  alt?: string;
  loading?: React.ImgHTMLAttributes<HTMLImageElement>['loading'];
  decoding?: React.ImgHTMLAttributes<HTMLImageElement>['decoding'];
  /** Высота/ширина полностью контролируются родителем. */
  style?: React.CSSProperties;
  /** Зум/пан поверх contain/cover (только для mode=contain в сценах). */
  viewCamera?: SceneViewCamera | null;
  /** Прямоугольник видимого контента (contain/cover) внутри контейнера. */
  onContentRectChange?: ((rect: { x: number; y: number; w: number; h: number }) => void) | undefined;
};

function useElementSize<T extends HTMLElement>() {
  const ref = useRef<T | null>(null);
  const [size, setSize] = useState<{ w: number; h: number }>({ w: 0, h: 0 });

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    // clientWidth/Height — локальная вёрстка; getBoundingClientRect учитывает transform предков (React Flow zoom).
    const readLayoutSize = () => {
      setSize({ w: el.clientWidth, h: el.clientHeight });
    };
    const ro = new ResizeObserver(() => {
      readLayoutSize();
    });
    ro.observe(el);
    readLayoutSize();
    return () => ro.disconnect();
  }, []);

  return [ref, size] as const;
}

export function RotatedImage({
  url,
  rotationDeg,
  mode,
  alt = '',
  loading,
  decoding,
  style,
  viewCamera = null,
  onContentRectChange,
}: RotatedImageProps) {
  const [ref, size] = useElementSize<HTMLDivElement>();
  const [imgSize, setImgSize] = useState<{ w: number; h: number } | null>(null);
  const imgRef = useRef<HTMLImageElement | null>(null);

  const cam = viewCamera ?? DEFAULT_SCENE_VIEW_CAMERA;
  const viewScale = mode === 'contain' ? Math.max(1, cam.scale) : 1;
  const viewOx = mode === 'contain' ? cam.ox : 0.5;
  const viewOy = mode === 'contain' ? cam.oy : 0.5;

  useLayoutEffect(() => {
    // If the image is served from cache, onLoad may fire before listeners attach.
    // Reading from the <img> element itself is the most reliable source.
    const el = imgRef.current;
    if (!el) return;
    if (!el.complete) return;
    const w0 = el.naturalWidth || 0;
    const h0 = el.naturalHeight || 0;
    if (w0 <= 0 || h0 <= 0) return;
    // eslint-disable-next-line react-hooks/set-state-in-effect, @typescript-eslint/prefer-optional-chain -- read cached <img> dimensions when onLoad may not fire
    setImgSize((prev) => (prev && prev.w === w0 && prev.h === h0 ? prev : { w: w0, h: h0 }));
  }, [url]);

  const fitScale = useMemo(() => {
    if (!imgSize) return 1;
    if (size.w <= 1 || size.h <= 1) return 1;
    const rotated = rotationDeg === 90 || rotationDeg === 270;
    const iw = rotated ? imgSize.h : imgSize.w;
    const ih = rotated ? imgSize.w : imgSize.h;
    const sx = size.w / iw;
    const sy = size.h / ih;
    return mode === 'cover' ? Math.max(sx, sy) : Math.min(sx, sy);
  }, [imgSize, mode, rotationDeg, size.h, size.w]);

  const scale = fitScale * viewScale;

  const contentRect = useMemo(() => {
    if (!imgSize) return null;
    if (size.w <= 1 || size.h <= 1) return null;
    const rotated = rotationDeg === 90 || rotationDeg === 270;
    const bw = (rotated ? imgSize.h : imgSize.w) * scale;
    const bh = (rotated ? imgSize.w : imgSize.h) * scale;
    const x = size.w / 2 - viewOx * bw;
    const y = size.h / 2 - viewOy * bh;
    return { x, y, w: bw, h: bh };
  }, [imgSize, rotationDeg, scale, size.h, size.w, viewOx, viewOy]);

  useEffect(() => {
    if (!onContentRectChange || !contentRect) return;
    onContentRectChange(contentRect);
  }, [contentRect, onContentRectChange]);

  const w = imgSize ? imgSize.w * scale : undefined;
  const h = imgSize ? imgSize.h * scale : undefined;
  const leftPx = contentRect ? contentRect.x + contentRect.w / 2 : undefined;
  const topPx = contentRect ? contentRect.y + contentRect.h / 2 : undefined;

  return (
    <div ref={ref} className={styles.root} style={style}>
      <img
        ref={imgRef}
        alt={alt}
        src={url}
        loading={loading}
        decoding={decoding}
        draggable={false}
        className={styles.img}
        onLoad={(e) => {
          const el = e.currentTarget;
          const w0 = el.naturalWidth || 0;
          const h0 = el.naturalHeight || 0;
          if (w0 <= 0 || h0 <= 0) return;
          setImgSize((prev) => {
            // eslint-disable-next-line @typescript-eslint/prefer-optional-chain -- rule can misfire on React state unions
            if (prev && prev.w === w0 && prev.h === h0) return prev;
            return { w: w0, h: h0 };
          });
        }}
        style={{
          width: w ?? '100%',
          height: h ?? '100%',
          left: leftPx !== undefined ? `${String(leftPx)}px` : '50%',
          top: topPx !== undefined ? `${String(topPx)}px` : '50%',
          objectFit: imgSize ? undefined : mode,
          transform: `translate(-50%, -50%) rotate(${String(rotationDeg)}deg)`,
        }}
      />
    </div>
  );
}
