import React, { useEffect, useState } from 'react';
import { createPortal } from 'react-dom';

import type { SceneTrap, SceneTrapsState } from '../../../shared/types';
import { defaultTrapRuntime } from '../../../shared/types/sceneTraps';

import {
  isTrapMediaActivationKind,
  playTrapActivationSound,
  trapActivationAnchorTransform,
  trapActivationLifeMs,
  trapActivationVideoUrl,
  type TrapMediaActivationKind,
} from './trapActivation';
import { TrapGlyph } from './TrapGlyph';
import styles from './SceneTrapsOverlay.module.css';

type Props = {
  traps: readonly SceneTrap[];
  session: SceneTrapsState | null;
  viewport: { x: number; y: number; w: number; h: number } | null;
  /** Пульт: показывать все ловушки + RMB меню. Презентация: только revealed. */
  mode: 'control' | 'presentation';
  /** На пульте: false — без контекстного меню (например, активна кисть эффектов). */
  interactive?: boolean;
  onReveal?: (trapId: string) => void;
  onActivate?: (trapId: string) => void;
  onDisarm?: (trapId: string) => void;
};

type ActivationFx = {
  trapId: string;
  token: number;
  kind: TrapMediaActivationKind | 'flash';
};

function menuPosition(clientX: number, clientY: number): { x: number; y: number } {
  const menuW = 200;
  const menuH = 140;
  const pad = 8;
  return {
    x: Math.max(pad, Math.min(clientX, window.innerWidth - menuW - pad)),
    y: Math.max(pad, Math.min(clientY, window.innerHeight - menuH - pad)),
  };
}

export function SceneTrapsOverlay({
  traps,
  session,
  viewport,
  mode,
  interactive = true,
  onReveal,
  onActivate,
  onDisarm,
}: Props) {
  const [menu, setMenu] = useState<{ trapId: string; x: number; y: number } | null>(null);
  const [activationFx, setActivationFx] = useState<ActivationFx | null>(null);

  useEffect(() => {
    if (!interactive) setMenu(null);
  }, [interactive]);

  useEffect(() => {
    const act = session?.lastActivation;
    if (!act) return;
    const trap = traps.find((t) => t.id === act.trapId);
    // poison/explosion: VFX/SFX через effects store (ControlApp), без локального flash/video.
    if (trap?.type === 'poison' || trap?.type === 'explosion') {
      setActivationFx(null);
      return;
    }
    const kind: ActivationFx['kind'] =
      trap && isTrapMediaActivationKind(trap.type) ? trap.type : 'flash';
    setActivationFx({ trapId: act.trapId, token: act.token, kind });
    if (kind !== 'flash' && mode === 'control') {
      // SFX только на пульте — иначе при двух окнах звук удвоится (как у прочих эффектов).
      playTrapActivationSound(kind);
    }
    const ms = kind !== 'flash' ? trapActivationLifeMs(kind) : 750;
    const t = window.setTimeout(() => setActivationFx(null), ms);
    return () => window.clearTimeout(t);
  }, [session?.lastActivation?.token, session?.lastActivation?.trapId, traps, mode]);

  useEffect(() => {
    if (!menu) return;
    const close = (e: PointerEvent) => {
      const t = e.target;
      if (t instanceof Element && t.closest('[data-trap-menu-root="1"]')) return;
      setMenu(null);
    };
    window.addEventListener('pointerdown', close, true);
    return () => window.removeEventListener('pointerdown', close, true);
  }, [menu]);

  if (!viewport || traps.length === 0) return null;
  const minDim = Math.min(viewport.w, viewport.h);

  const mediaTrap =
    activationFx && activationFx.kind !== 'flash'
      ? traps.find((t) => t.id === activationFx.trapId)
      : undefined;
  const mediaSizePx = mediaTrap ? Math.max(16, mediaTrap.sizeN * minDim) : 0;
  /** Видео 16:9, центрируем на ловушке; ширина ~4× маркера, не меньше 18% кадра. */
  const mediaFxW = mediaTrap ? Math.max(mediaSizePx * 4.2, minDim * 0.18) : 0;
  const mediaKind =
    activationFx && activationFx.kind !== 'flash' ? activationFx.kind : null;

  return (
    <div className={styles.layer}>
      {traps.map((trap) => {
        const rt = session?.byId[trap.id] ?? defaultTrapRuntime();
        if (mode === 'presentation' && !rt.revealed) return null;
        const sizePx = Math.max(16, trap.sizeN * minDim);
        const left = viewport.x + trap.nx * viewport.w;
        const top = viewport.y + trap.ny * viewport.h;
        const cls = [
          styles.trap,
          rt.status === 'active' ? styles.trapActive : '',
          rt.status === 'disarmed' ? styles.trapDisarmed : '',
          mode === 'control' && !rt.revealed ? styles.trapGmHidden : '',
          mode === 'control' && !interactive ? styles.trapNonInteractive : '',
        ]
          .filter(Boolean)
          .join(' ');
        return (
          <div
            key={trap.id}
            className={cls}
            style={{ left, top, width: sizePx, height: sizePx }}
            onContextMenu={
              mode === 'control' && interactive
                ? (e) => {
                    e.preventDefault();
                    e.stopPropagation();
                    const pos = menuPosition(e.clientX, e.clientY);
                    setMenu({ trapId: trap.id, x: pos.x, y: pos.y });
                  }
                : undefined
            }
          >
            <TrapGlyph type={trap.type} status={rt.status} size={Math.max(14, sizePx * 0.55)} />
            {trap.label && trap.label.trim().toLowerCase() !== 'свободная' ? (
              <div className={styles.label}>{trap.label}</div>
            ) : null}
            {activationFx?.trapId === trap.id && activationFx.kind === 'flash' ? (
              <div className={styles.flash} />
            ) : null}
          </div>
        );
      })}
      {mediaTrap && activationFx && mediaKind ? (
        <video
          key={activationFx.token}
          className={styles.trapMediaFx}
          style={{
            left: viewport.x + mediaTrap.nx * viewport.w,
            top: viewport.y + mediaTrap.ny * viewport.h,
            width: mediaFxW,
            transform: trapActivationAnchorTransform(mediaKind),
          }}
          src={trapActivationVideoUrl(mediaKind)}
          autoPlay
          muted
          playsInline
          preload="auto"
          onEnded={() => {
            setActivationFx((cur) =>
              cur?.token === activationFx.token && cur.kind === mediaKind ? null : cur,
            );
          }}
        />
      ) : null}
      {menu && mode === 'control' && interactive
        ? createPortal(
            <div
              role="menu"
              data-trap-menu-root="1"
              className={styles.menu}
              style={{ left: menu.x, top: menu.y }}
              onPointerDown={(e) => e.stopPropagation()}
            >
              <button
                type="button"
                role="menuitem"
                className={styles.menuItem}
                onClick={() => {
                  onReveal?.(menu.trapId);
                  setMenu(null);
                }}
              >
                Проявить
              </button>
              <button
                type="button"
                role="menuitem"
                className={styles.menuItem}
                onClick={() => {
                  onActivate?.(menu.trapId);
                  setMenu(null);
                }}
              >
                Активировать
              </button>
              <button
                type="button"
                role="menuitem"
                className={styles.menuItem}
                onClick={() => {
                  onDisarm?.(menu.trapId);
                  setMenu(null);
                }}
              >
                Обезвредить
              </button>
            </div>,
            document.body,
          )
        : null}
    </div>
  );
}
