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

import type { PlayerImageOffset } from '../../../shared/types/appPlayers';
import {
  clampPlayerImageOffset,
  clampPlayerImageScale,
  DEFAULT_PLAYER_IMAGE_SCALE,
  DEFAULT_PLAYER_RING_COLOR,
  PLAYER_IMAGE_SCALE_STEP,
} from '../../../shared/types/appPlayers';

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

export type PlayerTokenViewProps = {
  name: string;
  imageUrl: string | null;
  ringColor?: string;
  imageOffset?: PlayerImageOffset;
  imageScale?: number;
  /** Размер круга в px (для превью в модалке). На сцене задавайте через CSS width/height родителя. */
  sizePx?: number;
  /** Разрешить drag-pan аватара внутри круга и zoom колесом. */
  panEnabled?: boolean;
  onImageOffsetChange?: (offset: PlayerImageOffset) => void;
  onImageScaleChange?: (scale: number) => void;
  className?: string;
  /** Без подписи имени (компактный маркер). */
  hideName?: boolean;
  'data-testid'?: string;
};

export function PlayerTokenView({
  name,
  imageUrl,
  ringColor = DEFAULT_PLAYER_RING_COLOR,
  imageOffset,
  imageScale,
  sizePx = 160,
  panEnabled = false,
  onImageOffsetChange,
  onImageScaleChange,
  className,
  hideName = false,
  'data-testid': testId,
}: PlayerTokenViewProps) {
  const offset = clampPlayerImageOffset(imageOffset);
  const scale = clampPlayerImageScale(imageScale ?? DEFAULT_PLAYER_IMAGE_SCALE);
  const circleRef = useRef<HTMLDivElement | null>(null);
  const scaleRef = useRef(scale);
  scaleRef.current = scale;
  const onImageScaleChangeRef = useRef(onImageScaleChange);
  onImageScaleChangeRef.current = onImageScaleChange;
  const dragRef = useRef<{
    pointerId: number;
    startX: number;
    startY: number;
    origX: number;
    origY: number;
  } | null>(null);

  const onPointerDown = useCallback(
    (e: React.PointerEvent) => {
      if (!panEnabled || !onImageOffsetChange) return;
      e.preventDefault();
      e.stopPropagation();
      (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
      dragRef.current = {
        pointerId: e.pointerId,
        startX: e.clientX,
        startY: e.clientY,
        origX: offset.x,
        origY: offset.y,
      };
    },
    [offset.x, offset.y, onImageOffsetChange, panEnabled],
  );

  const onPointerMove = useCallback(
    (e: React.PointerEvent) => {
      const d = dragRef.current;
      if (!d || d.pointerId !== e.pointerId || !onImageOffsetChange) return;
      const dx = (e.clientX - d.startX) / Math.max(1, sizePx);
      const dy = (e.clientY - d.startY) / Math.max(1, sizePx);
      onImageOffsetChange(clampPlayerImageOffset({ x: d.origX + dx, y: d.origY + dy }));
    },
    [onImageOffsetChange, sizePx],
  );

  const onPointerUp = useCallback((e: React.PointerEvent) => {
    const d = dragRef.current;
    if (!d || d.pointerId !== e.pointerId) return;
    dragRef.current = null;
    try {
      (e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId);
    } catch {
      /* ignore */
    }
  }, []);

  useEffect(() => {
    const el = circleRef.current;
    if (!el || !panEnabled) return;
    const onWheel = (e: WheelEvent) => {
      const cb = onImageScaleChangeRef.current;
      if (!cb) return;
      e.preventDefault();
      e.stopPropagation();
      const direction = e.deltaY < 0 ? 1 : -1;
      cb(clampPlayerImageScale(scaleRef.current + direction * PLAYER_IMAGE_SCALE_STEP));
    };
    el.addEventListener('wheel', onWheel, { passive: false });
    return () => el.removeEventListener('wheel', onWheel);
  }, [panEnabled]);

  const rootClass = [styles.root, className ?? ''].filter(Boolean).join(' ');
  const nameFontPx = Math.max(9, Math.min(13, Math.round(sizePx * 0.16)));
  const nameGapPx = Math.max(2, Math.min(8, Math.round(sizePx * 0.06)));

  return (
    <div
      className={rootClass}
      style={{ width: sizePx, gap: hideName ? undefined : nameGapPx }}
      data-testid={testId}
    >
      <div
        ref={circleRef}
        className={[styles.circle, panEnabled ? styles.circlePan : ''].filter(Boolean).join(' ')}
        style={{
          width: sizePx,
          height: sizePx,
          borderColor: ringColor,
        }}
        onPointerDown={onPointerDown}
        onPointerMove={onPointerMove}
        onPointerUp={onPointerUp}
        onPointerCancel={onPointerUp}
      >
        {imageUrl ? (
          <img
            className={styles.avatar}
            src={imageUrl}
            alt=""
            draggable={false}
            style={{
              transform: `translate(-50%, -50%) translate(${String(offset.x * 100)}%, ${String(offset.y * 100)}%) scale(${String(scale)})`,
            }}
          />
        ) : (
          <div className={styles.avatarEmpty} />
        )}
      </div>
      {!hideName ? (
        <div className={styles.namePlate} title={name} style={{ fontSize: nameFontPx }}>
          {name || '—'}
        </div>
      ) : null}
    </div>
  );
}
