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

import type { AssetId, NpcId, NpcsOverlayLayout, NpcsZoomTool } from '../../../shared/types';
import { DEFAULT_NPCS_OVERLAY_LAYOUT } from '../../../shared/types';
import styles from '../materials/MaterialOverlay.module.css';
import { useAssetUrl } from '../useAssetImageUrl';

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

export type NpcsSceneOverlayItem = {
  npcId: NpcId;
  assetId: AssetId | null;
  layout: NpcsOverlayLayout;
};

type NpcsSceneOverlayProps = {
  items: readonly NpcsSceneOverlayItem[];
  editable?: boolean;
  zoomTool?: NpcsZoomTool;
  showClose?: boolean;
  onClose?: () => void;
  closeLabel?: string;
  onLayoutChange?: (npcId: NpcId, layout: NpcsOverlayLayout) => void;
  onZoomAt?: (npcId: NpcId | undefined, nx: number, ny: number) => void;
};

function nextBaseSize(
  viewW: number,
  viewH: number,
  naturalW: number,
  naturalH: number,
): { w: number; h: number } {
  if (naturalW <= 0 || naturalH <= 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 / naturalW, maxH / naturalH);
  return { w: naturalW * fit, h: naturalH * fit };
}

function NpcAvatarFrame({
  item,
  view,
  rootRef,
  editable,
  zoomTool,
  onLayoutChange,
}: {
  item: NpcsSceneOverlayItem;
  view: { w: number; h: number };
  rootRef: React.RefObject<HTMLDivElement | null>;
  editable: boolean;
  zoomTool: NpcsZoomTool;
  onLayoutChange?: (npcId: NpcId, layout: NpcsOverlayLayout) => void;
}) {
  const url = useAssetUrl(item.assetId);
  const [natural, setNatural] = useState<{ w: number; h: number }>({ w: 1600, h: 900 });
  const dragRef = useRef<
    | { mode: 'move'; startX: number; startY: number; origin: NpcsOverlayLayout }
    | {
        mode: 'resize';
        corner: Corner;
        startX: number;
        startY: number;
        origin: NpcsOverlayLayout;
        baseW: number;
        baseH: number;
        viewW: number;
        viewH: number;
      }
    | null
  >(null);

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

  const layout = item.layout ?? DEFAULT_NPCS_OVERLAY_LAYOUT;
  const base = nextBaseSize(view.w, view.h, natural.w, natural.h);
  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;

  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(item.npcId, {
        ...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(item.npcId, {
      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
      className={[styles.frame, editable && !zoomTool ? styles.frameEditable : ''].filter(Boolean).join(' ')}
      data-npc-id={item.npcId}
      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: w,
          height: h,
          transform: 'translate(-50%, -50%)',
        }}
        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>
  );
}

export function NpcsSceneOverlay({
  items,
  editable = false,
  zoomTool = null,
  showClose = false,
  onClose,
  closeLabel = 'Close',
  onLayoutChange,
  onZoomAt,
}: NpcsSceneOverlayProps) {
  const rootRef = useRef<HTMLDivElement | null>(null);
  const [view, setView] = useState({ w: 1, h: 1 });

  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();
  }, [items.length]);

  if (items.length === 0) return null;

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

  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 npcIdFromTarget = (target: EventTarget | null): NpcId | undefined => {
    if (!(target instanceof Element)) return undefined;
    const frame = target.closest('[data-npc-id]');
    const raw = frame?.getAttribute('data-npc-id');
    return raw ? (raw as NpcId) : undefined;
  };

  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(npcIdFromTarget(e.target), nx, ny);
      }}
    >
      <div className={styles.dim} />
      {items.map((item) =>
        onLayoutChange ? (
          <NpcAvatarFrame
            key={item.npcId}
            item={item}
            view={view}
            rootRef={rootRef}
            editable={editable}
            zoomTool={zoomTool}
            onLayoutChange={onLayoutChange}
          />
        ) : (
          <NpcAvatarFrame
            key={item.npcId}
            item={item}
            view={view}
            rootRef={rootRef}
            editable={editable}
            zoomTool={zoomTool}
          />
        ),
      )}
      {showClose ? (
        <button
          type="button"
          className={styles.close}
          onClick={onClose}
          aria-label={closeLabel}
          title={closeLabel}
        >
          ×
        </button>
      ) : null}
    </div>
  );
}
