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

import type { AssetId, MaterialsOverlayLayout, MaterialsZoomTool, NpcsZoomTool } from '../../../shared/types';
import { useAssetUrl } from '../useAssetImageUrl';

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

type Corner = 'nw' | 'ne' | 'sw' | 'se';

type MaterialOverlayProps = {
  assetId: AssetId | null;
  rotationDeg?: 0 | 90 | 180 | 270;
  layout: MaterialsOverlayLayout;
  editable?: boolean;
  zoomTool?: MaterialsZoomTool | NpcsZoomTool;
  showClose?: boolean;
  onClose?: () => void;
  closeLabel?: string;
  onLayoutChange?: (layout: MaterialsOverlayLayout) => void;
  onZoomAt?: (nx: number, ny: number) => void;
};

function nextBaseSize(
  viewW: number,
  viewH: number,
  naturalW: number,
  naturalH: number,
  rotationDeg: number,
): { w: number; h: number } {
  const swapped = rotationDeg === 90 || rotationDeg === 270;
  const iw = swapped ? naturalH : naturalW;
  const ih = swapped ? naturalW : naturalH;
  if (iw <= 0 || ih <= 0 || viewW <= 0 || viewH <= 0) return { w: 200, h: 120 };
  const maxW = viewW * 0.92;
  const maxH = viewH * 0.88;
  const fit = Math.min(maxW / iw, maxH / ih);
  return { w: iw * fit, h: ih * fit };
}

export function MaterialOverlay({
  assetId,
  rotationDeg = 0,
  layout,
  editable = false,
  zoomTool = null,
  showClose = false,
  onClose,
  closeLabel = 'Close',
  onLayoutChange,
  onZoomAt,
}: MaterialOverlayProps) {
  const url = useAssetUrl(assetId);
  const rootRef = useRef<HTMLDivElement | null>(null);
  const [natural, setNatural] = useState<{ w: number; h: number }>({ w: 1600, h: 900 });
  const [view, setView] = useState({ w: 1, h: 1 });
  const dragRef = useRef<
    | { mode: 'move'; startX: number; startY: number; origin: MaterialsOverlayLayout }
    | {
        mode: 'resize';
        corner: Corner;
        startX: number;
        startY: number;
        origin: MaterialsOverlayLayout;
        baseW: number;
        baseH: number;
        viewW: number;
        viewH: number;
      }
    | null
  >(null);

  useEffect(() => {
    const el = rootRef.current;
    if (!el) return;
    const sync = () => setView({ w: el.clientWidth, h: el.clientHeight });
    sync();
    const ro = new ResizeObserver(sync);
    ro.observe(el);
    return () => ro.disconnect();
  }, [url]);

  if (!assetId || !url) return null;

  const interactive = editable || showClose || Boolean(zoomTool);
  const zoomCursor =
    zoomTool === 'zoomIn' ? styles.cursorZoomIn : zoomTool === 'zoomOut' ? styles.cursorZoomOut : '';

  const base = nextBaseSize(view.w, view.h, natural.w, natural.h, rotationDeg);
  const w = base.w * layout.scale;
  const h = base.h * layout.scale;
  const left = layout.cx * view.w - w / 2;
  const top = layout.cy * view.h - h / 2;
  // Рамка — AABB; до CSS-rotate картинка имеет «натуральную» ориентацию.
  const swapped = rotationDeg === 90 || rotationDeg === 270;
  const contentW = swapped ? h : w;
  const contentH = swapped ? w : h;

  const toNorm = (clientX: number, clientY: number) => {
    const root = rootRef.current;
    if (!root) return { nx: 0.5, ny: 0.5 };
    const r = root.getBoundingClientRect();
    return {
      nx: (clientX - r.left) / Math.max(1, r.width),
      ny: (clientY - r.top) / Math.max(1, r.height),
    };
  };

  const onPointerMove = (e: PointerEvent) => {
    const drag = dragRef.current;
    if (!drag || !onLayoutChange) return;
    const root = rootRef.current;
    if (!root) return;
    const r = root.getBoundingClientRect();
    const dx = (e.clientX - drag.startX) / Math.max(1, r.width);
    const dy = (e.clientY - drag.startY) / Math.max(1, r.height);

    if (drag.mode === 'move') {
      onLayoutChange({
        ...drag.origin,
        cx: drag.origin.cx + dx,
        cy: drag.origin.cy + dy,
      });
      return;
    }

    const { baseW, baseH, viewW, viewH, origin, corner } = drag;
    const originW = baseW * origin.scale;
    const originH = baseH * origin.scale;
    const originLeft = origin.cx * viewW - originW / 2;
    const originTop = origin.cy * viewH - originH / 2;
    const originRight = originLeft + originW;
    const originBottom = originTop + originH;

    let anchorX = originLeft;
    let anchorY = originTop;
    if (corner === 'nw') {
      anchorX = originRight;
      anchorY = originBottom;
    } else if (corner === 'ne') {
      anchorX = originLeft;
      anchorY = originBottom;
    } else if (corner === 'sw') {
      anchorX = originRight;
      anchorY = originTop;
    }

    const pointerX = e.clientX - r.left;
    const pointerY = e.clientY - r.top;
    const newW = Math.max(8, Math.abs(pointerX - anchorX));
    const newH = Math.max(8, Math.abs(pointerY - anchorY));
    const nextScale = Math.max(newW / Math.max(1, baseW), newH / Math.max(1, baseH));
    const ww = baseW * nextScale;
    const hh = baseH * nextScale;

    let nextLeft = anchorX;
    let nextTop = anchorY;
    if (corner === 'nw') {
      nextLeft = anchorX - ww;
      nextTop = anchorY - hh;
    } else if (corner === 'ne') {
      nextLeft = anchorX;
      nextTop = anchorY - hh;
    } else if (corner === 'sw') {
      nextLeft = anchorX - ww;
      nextTop = anchorY;
    }

    onLayoutChange({
      cx: (nextLeft + ww / 2) / Math.max(1, viewW),
      cy: (nextTop + hh / 2) / Math.max(1, viewH),
      scale: nextScale,
    });
  };

  const endDrag = () => {
    dragRef.current = null;
    window.removeEventListener('pointermove', onPointerMove);
    window.removeEventListener('pointerup', endDrag);
  };

  const startDrag = (e: React.PointerEvent, mode: 'move' | 'resize', corner?: Corner) => {
    if (!editable || !onLayoutChange || zoomTool) return;
    e.preventDefault();
    e.stopPropagation();
    dragRef.current =
      mode === 'move'
        ? { mode: 'move', startX: e.clientX, startY: e.clientY, origin: { ...layout } }
        : {
            mode: 'resize',
            corner: corner ?? 'se',
            startX: e.clientX,
            startY: e.clientY,
            origin: { ...layout },
            baseW: base.w,
            baseH: base.h,
            viewW: view.w,
            viewH: view.h,
          };
    window.addEventListener('pointermove', onPointerMove);
    window.addEventListener('pointerup', endDrag);
  };

  return (
    <div
      ref={rootRef}
      className={[styles.root, interactive ? styles.interactive : styles.passive, zoomCursor]
        .filter(Boolean)
        .join(' ')}
      role="dialog"
      aria-modal="true"
      onClick={(e) => {
        if (!zoomTool || !onZoomAt) return;
        e.stopPropagation();
        const { nx, ny } = toNorm(e.clientX, e.clientY);
        onZoomAt(nx, ny);
      }}
    >
      <div className={styles.dim} />
      <div
        className={[styles.frame, editable && !zoomTool ? styles.frameEditable : '']
          .filter(Boolean)
          .join(' ')}
        style={{ left, top, width: w, height: h }}
        onPointerDown={(e) => {
          if (zoomTool) return;
          startDrag(e, 'move');
        }}
      >
        <img
          className={styles.image}
          src={url}
          alt=""
          draggable={false}
          style={{
            width: contentW,
            height: contentH,
            transform: `translate(-50%, -50%) rotate(${String(rotationDeg)}deg)`,
          }}
          onLoad={(e) => {
            const img = e.currentTarget;
            setNatural({ w: img.naturalWidth || 1, h: img.naturalHeight || 1 });
          }}
        />
        {editable && !zoomTool
          ? (['nw', 'ne', 'sw', 'se'] as const).map((corner) => (
              <button
                key={corner}
                type="button"
                className={[styles.handle, styles[`handle_${corner}`]].join(' ')}
                aria-label={corner}
                onPointerDown={(e) => startDrag(e, 'resize', corner)}
              />
            ))
          : null}
      </div>
      {showClose ? (
        <button
          type="button"
          className={styles.close}
          onClick={onClose}
          aria-label={closeLabel}
          title={closeLabel}
        >
          ×
        </button>
      ) : null}
    </div>
  );
}
