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

import type { MaterialId, MaterialLegend, ProjectMaterial } from '../../shared/types';
import { RotatedImage } from '../shared/RotatedImage';
import { Button, Input } from '../shared/ui/controls';
import { useAssetUrl } from '../shared/useAssetImageUrl';

import styles from './EditorApp.module.css';
import { useEditorI18n } from './i18n/EditorI18nContext';
import { MaterialLegendEditor } from './MaterialLegendEditor';
import matStyles from './MaterialsModals.module.css';

const DND_MATERIAL_ID_MIME = 'application/x-dnd-material-id';

export type MaterialsBrowserProps = {
  materials: ProjectMaterial[];
  /** editor: CRUD + ⋮; runtime: показ на сцене, без меню */
  mode: 'editor' | 'runtime';
  selectedId: MaterialId | null;
  onSelect: (id: MaterialId | null) => void;
  activeMaterialIds?: readonly MaterialId[];
  onAdd?: () => void;
  onEdit?: (material: ProjectMaterial) => void;
  onDelete?: (materialId: MaterialId) => Promise<void>;
  onReorder?: (materialIds: MaterialId[]) => Promise<void>;
  onRotate?: (materialId: MaterialId, rotationDeg: 0 | 90 | 180 | 270) => void;
  onLegendChange?: (materialId: MaterialId, legend: MaterialLegend) => Promise<void>;
  onTileActivate?: (materialId: MaterialId) => void;
  toolbar?: React.ReactNode;
  className?: string | undefined;
  /** Растянуть тело на всю высоту (окно Electron). */
  fillHeight?: boolean;
  /** Только колонка списка (без большого превью). */
  listOnly?: boolean;
};

export function MaterialsBrowser({
  materials,
  mode,
  selectedId,
  onSelect,
  activeMaterialIds = [],
  onAdd,
  onEdit,
  onDelete,
  onReorder,
  onRotate,
  onLegendChange,
  onTileActivate,
  toolbar,
  className,
  fillHeight = false,
  listOnly = false,
}: MaterialsBrowserProps) {
  const { t } = useEditorI18n();
  const [query, setQuery] = useState('');
  const [menuFor, setMenuFor] = useState<MaterialId | null>(null);
  const [menuPos, setMenuPos] = useState<{ left: number; top: number } | null>(null);
  const [dragId, setDragId] = useState<MaterialId | null>(null);
  const [dropPlace, setDropPlace] = useState<{ id: MaterialId; place: 'before' | 'after' } | null>(null);
  const [pendingDelete, setPendingDelete] = useState<ProjectMaterial | null>(null);

  useEffect(() => {
    if (!menuFor) return;
    const onDown = (e: MouseEvent) => {
      const tgt = e.target as HTMLElement | null;
      if (!tgt) return;
      if (tgt.closest('[data-material-menu-root="1"]')) return;
      setMenuFor(null);
      setMenuPos(null);
    };
    window.addEventListener('mousedown', onDown);
    return () => window.removeEventListener('mousedown', onDown);
  }, [menuFor]);

  const activeSet = useMemo(() => new Set(activeMaterialIds), [activeMaterialIds]);
  const filtered = useMemo(() => {
    const q = query.trim().toLowerCase();
    if (!q) return materials;
    return materials.filter((m) => m.name.toLowerCase().includes(q));
  }, [materials, query]);

  useEffect(() => {
    if (selectedId && filtered.some((m) => m.id === selectedId)) return;
    const next = filtered[0]?.id ?? null;
    if (next !== selectedId) onSelect(next);
    // eslint-disable-next-line react-hooks/exhaustive-deps -- sync selection to filtered list
  }, [filtered, selectedId]);

  const selected = materials.find((m) => m.id === selectedId) ?? null;
  const selectedUrl = useAssetUrl(selected?.assetId ?? null);

  return (
    <div
      className={[
        matStyles.browserRoot,
        fillHeight ? matStyles.browserRootFill : '',
        listOnly ? matStyles.browserRootListOnly : '',
        className,
      ]
        .filter(Boolean)
        .join(' ')}
    >
      {toolbar ? <div className={matStyles.browserToolbar}>{toolbar}</div> : null}
      <div
        className={[
          matStyles.managerBody,
          fillHeight ? matStyles.managerBodyFill : '',
          listOnly ? matStyles.managerBodyListOnly : '',
        ]
          .filter(Boolean)
          .join(' ')}
      >
        <div className={matStyles.side}>
          <Input value={query} onChange={setQuery} placeholder={t('materials.search')} />
          {mode === 'editor' && onAdd ? (
            <div className={matStyles.sideAddBtn}>
              <Button variant="primary" onClick={onAdd}>
                {t('materials.add')}
              </Button>
            </div>
          ) : null}
          <div className={matStyles.list}>
            {filtered.map((m) => (
              <MaterialTile
                key={m.id}
                material={m}
                selected={m.id === selectedId}
                active={activeSet.has(m.id)}
                showMenu={mode === 'editor'}
                dragging={dragId === m.id}
                dropPlace={dropPlace?.id === m.id ? dropPlace.place : null}
                reorderEnabled={mode === 'editor' && Boolean(onReorder)}
                onSelect={() => {
                  onSelect(m.id);
                  if (mode === 'runtime' && onTileActivate) onTileActivate(m.id);
                }}
                onMenu={(e) => {
                  if (mode !== 'editor') return;
                  const r = e.currentTarget.getBoundingClientRect();
                  const menuW = 180;
                  const menuH = 88;
                  const left = Math.max(8, Math.min(r.right - menuW, window.innerWidth - menuW - 8));
                  const top =
                    r.bottom + 8 + menuH > window.innerHeight - 8
                      ? Math.max(8, r.top - menuH - 8)
                      : r.bottom + 8;
                  setMenuPos({ left, top });
                  setMenuFor((cur) => (cur === m.id ? null : m.id));
                }}
                onDragStart={() => setDragId(m.id)}
                onDragEnd={() => {
                  setDragId(null);
                  setDropPlace(null);
                }}
                onDragOver={(place) => {
                  if (!dragId || dragId === m.id) {
                    setDropPlace(null);
                    return;
                  }
                  setDropPlace({ id: m.id, place });
                }}
                onDropReorder={async () => {
                  if (!onReorder || !dragId || !dropPlace || dragId === dropPlace.id) return;
                  const ids = materials.map((x) => x.id);
                  const from = ids.indexOf(dragId);
                  if (from < 0) return;
                  ids.splice(from, 1);
                  let to = ids.indexOf(dropPlace.id);
                  if (to < 0) return;
                  if (dropPlace.place === 'after') to += 1;
                  ids.splice(to, 0, dragId);
                  setDragId(null);
                  setDropPlace(null);
                  await onReorder(ids);
                }}
              />
            ))}
            {materials.length === 0 ? <div className={styles.muted}>{t('materials.empty')}</div> : null}
            {materials.length > 0 && filtered.length === 0 ? (
              <div className={styles.muted}>{t('materials.searchEmpty')}</div>
            ) : null}
          </div>
        </div>

        {!listOnly ? (
          <div
            className={[
              matStyles.previewColumn,
              onLegendChange ? matStyles.previewColumnLegend : '',
            ]
              .filter(Boolean)
              .join(' ')}
          >
            {selected && selectedUrl && onLegendChange ? (
              <div className={matStyles.previewLegendScroll}>
                <MaterialLegendEditor
                  key={selected.id}
                  largeMap
                  legend={selected.legend}
                  previewUrl={selectedUrl}
                  rotationDeg={selected.rotationDeg ?? 0}
                  rotateLabel={t('scene.rotate')}
                  onRotate={
                    onRotate
                      ? () => {
                          const cur = selected.rotationDeg ?? 0;
                          const next = ((cur + 90) % 360) as 0 | 90 | 180 | 270;
                          onRotate(selected.id, next);
                        }
                      : undefined
                  }
                  onChange={(next) => {
                    void onLegendChange(selected.id, next);
                  }}
                />
              </div>
            ) : (
              <div className={matStyles.previewPane}>
                {selected && selectedUrl ? (
                  <div className={matStyles.previewLargeHost}>
                    <RotatedImage
                      url={selectedUrl}
                      rotationDeg={selected.rotationDeg ?? 0}
                      mode="contain"
                    />
                  </div>
                ) : (
                  <div className={matStyles.previewEmpty}>{t('materials.addPrompt')}</div>
                )}
              </div>
            )}
            {selected && onRotate && !onLegendChange ? (
              <div className={matStyles.previewActions}>
                <Button
                  onClick={() => {
                    const cur = selected.rotationDeg ?? 0;
                    const next = ((cur + 90) % 360) as 0 | 90 | 180 | 270;
                    onRotate(selected.id, next);
                  }}
                >
                  {t('scene.rotate')}
                </Button>
              </div>
            ) : null}
          </div>
        ) : null}
      </div>

      {mode === 'editor' && menuFor && menuPos
        ? createPortal(
            <div
              role="menu"
              data-material-menu-root="1"
              className={styles.fileMenu}
              style={{ left: menuPos.left, top: menuPos.top }}
            >
              <button
                type="button"
                role="menuitem"
                className={styles.fileMenuItem}
                onClick={() => {
                  const mat = materials.find((x) => x.id === menuFor);
                  setMenuFor(null);
                  setMenuPos(null);
                  if (mat && onEdit) onEdit(mat);
                }}
              >
                {t('materials.edit')}
              </button>
              <button
                type="button"
                role="menuitem"
                className={styles.fileMenuItemDanger}
                onClick={() => {
                  const mat = materials.find((x) => x.id === menuFor);
                  setMenuFor(null);
                  setMenuPos(null);
                  if (mat) setPendingDelete(mat);
                }}
              >
                {t('common.delete')}
              </button>
            </div>,
            document.body,
          )
        : null}

      {pendingDelete
        ? createPortal(
            <>
              <button
                type="button"
                aria-label={t('common.close')}
                className={styles.modalBackdrop}
                onClick={() => setPendingDelete(null)}
              />
              <div role="dialog" aria-modal="true" className={styles.modalDialog}>
                <div className={styles.modalHeader}>
                  <div className={styles.modalTitle}>{t('materials.deleteTitle')}</div>
                  <button
                    type="button"
                    aria-label={t('common.close')}
                    className={styles.modalClose}
                    onClick={() => setPendingDelete(null)}
                  >
                    ×
                  </button>
                </div>
                <div className={styles.muted}>
                  {t('materials.deleteConfirm', { name: pendingDelete.name })}
                </div>
                <div className={styles.modalFooter}>
                  <Button onClick={() => setPendingDelete(null)}>{t('common.cancel')}</Button>
                  <Button
                    variant="primary"
                    onClick={() => {
                      const id = pendingDelete.id;
                      setPendingDelete(null);
                      if (onDelete) void onDelete(id);
                    }}
                  >
                    {t('common.delete')}
                  </Button>
                </div>
              </div>
            </>,
            document.body,
          )
        : null}
    </div>
  );
}

function MaterialTile({
  material,
  selected,
  active,
  showMenu,
  dragging,
  dropPlace,
  reorderEnabled,
  onSelect,
  onMenu,
  onDragStart,
  onDragEnd,
  onDragOver,
  onDropReorder,
}: {
  material: ProjectMaterial;
  selected: boolean;
  active: boolean;
  showMenu: boolean;
  dragging: boolean;
  dropPlace: 'before' | 'after' | null;
  reorderEnabled: boolean;
  onSelect: () => void;
  onMenu: (e: React.MouseEvent<HTMLButtonElement>) => void;
  onDragStart: () => void;
  onDragEnd: () => void;
  onDragOver: (place: 'before' | 'after') => void;
  onDropReorder: () => void;
}) {
  const { t } = useEditorI18n();
  const url = useAssetUrl(material.assetId);
  return (
    <div
      className={[
        matStyles.tile,
        selected ? matStyles.tileSelected : '',
        active ? matStyles.tileActive : '',
        dragging ? matStyles.tileDragging : '',
        dropPlace === 'before' ? matStyles.tileDropBefore : '',
        dropPlace === 'after' ? matStyles.tileDropAfter : '',
      ]
        .filter(Boolean)
        .join(' ')}
      draggable={reorderEnabled}
      onDragStart={(e) => {
        if (!reorderEnabled) return;
        e.dataTransfer.setData(DND_MATERIAL_ID_MIME, material.id);
        e.dataTransfer.effectAllowed = 'move';
        onDragStart();
      }}
      onDragEnd={onDragEnd}
      onDragOver={(e) => {
        if (!reorderEnabled) return;
        e.preventDefault();
        const rect = e.currentTarget.getBoundingClientRect();
        const place = e.clientY < rect.top + rect.height / 2 ? 'before' : 'after';
        onDragOver(place);
      }}
      onDrop={(e) => {
        if (!reorderEnabled) return;
        e.preventDefault();
        onDropReorder();
      }}
    >
      <button type="button" className={matStyles.tileBody} onClick={onSelect}>
        {url ? (
          <div className={matStyles.tileImg}>
            <RotatedImage url={url} rotationDeg={material.rotationDeg ?? 0} mode="contain" />
          </div>
        ) : (
          <div className={matStyles.tileImgEmpty} />
        )}
        <div className={matStyles.tileName}>{material.name}</div>
      </button>
      {showMenu ? (
        <button
          type="button"
          className={matStyles.tileMenu}
          data-material-menu-root="1"
          aria-label={t('materials.tileMenu')}
          onClick={onMenu}
        >
          ⋮
        </button>
      ) : null}
    </div>
  );
}
