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

import { ipcChannels } from '../../shared/ipc/contracts';
import type { AppToken } from '../../shared/types';
import {
  filterMaterialImagePaths,
  getDroppedFileEntries,
  pickFirstMaterialImagePath,
  useFileDropZone,
} from '../editor/fileDrop';
import editorStyles from '../editor/EditorApp.module.css';
import matStyles from '../editor/MaterialsModals.module.css';
import { getDndApi } from '../shared/dndApi';
import { Button, Input } from '../shared/ui/controls';
import { useTokenImageUrl } from '../shared/tokens/useTokenImageUrl';

type Props = {
  open: boolean;
  initial: AppToken | null;
  existingNames: string[];
  onClose: () => void;
  onSaved: (token: AppToken) => void;
};

function normalizeName(input: string): string {
  return input.trim().toLowerCase();
}

export function TokenEditModal({ open, initial, existingNames, onClose, onSaved }: Props) {
  const api = getDndApi();
  const [name, setName] = useState('');
  const [filePath, setFilePath] = useState<string | null>(null);
  const [localPreviewUrl, setLocalPreviewUrl] = useState<string | null>(null);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const existingUrl = useTokenImageUrl(initial?.id ?? null);

  useEffect(() => {
    if (!open) return;
    setName(initial?.name ?? '');
    setFilePath(null);
    setLocalPreviewUrl(null);
    setSaving(false);
    setError(null);
  }, [open, initial]);

  useEffect(() => {
    if (!open) return;
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') onClose();
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose, open]);

  const setPreviewFromPathAndUrl = (path: string, previewUrl: string) => {
    setFilePath(path);
    setLocalPreviewUrl((prev) => {
      if (prev?.startsWith('blob:')) URL.revokeObjectURL(prev);
      return previewUrl || null;
    });
  };

  const drop = useFileDropZone({
    onDropPaths: (paths) => {
      const picked = pickFirstMaterialImagePath(paths);
      if (!picked) return;
      setPreviewFromPathAndUrl(picked, '');
    },
    filterPaths: filterMaterialImagePaths,
  });

  const trimmed = name.trim();
  const nameOk = trimmed.length >= 1;
  const nameDup =
    nameOk &&
    existingNames.some(
      (n) =>
        normalizeName(n) === normalizeName(trimmed) &&
        normalizeName(n) !== normalizeName(initial?.name ?? ''),
    );
  const hasImage = Boolean(filePath) || Boolean(initial);
  const canSave = nameOk && !nameDup && hasImage && !saving;
  const previewSrc = localPreviewUrl || existingUrl;

  if (!open) return null;

  return createPortal(
    <>
      <button type="button" aria-label="Закрыть" onClick={onClose} className={editorStyles.modalBackdrop} />
      <div role="dialog" aria-modal="true" className={editorStyles.modalDialog}>
        <div className={editorStyles.modalHeader}>
          <div className={editorStyles.modalTitle}>
            {initial ? 'Изменить токен' : 'Добавить токен'}
          </div>
          <button type="button" aria-label="Закрыть" onClick={onClose} className={editorStyles.modalClose}>
            ×
          </button>
        </div>

        <div className={editorStyles.fieldGrid}>
          <div className={editorStyles.fieldLabel}>Название</div>
          <Input value={name} onChange={setName} placeholder="Название токена" />
          {!nameOk ? <div className={editorStyles.fieldError}>Укажите название</div> : null}
          {nameDup ? <div className={editorStyles.fieldError}>Такое название уже есть</div> : null}
        </div>

        <div className={editorStyles.fieldGrid}>
          <div className={editorStyles.fieldLabel}>Изображение</div>
          <div
            className={[matStyles.imageDrop, drop.dragOver ? matStyles.imageDropOver : ''].join(' ')}
            onDragEnter={drop.onDragEnter}
            onDragLeave={drop.onDragLeave}
            onDragOver={drop.onDragOver}
            onDrop={(e) => {
              drop.onDrop(e);
              const entries = getDroppedFileEntries(e);
              const files = e.dataTransfer.files;
              for (let i = 0; i < entries.length; i += 1) {
                const entry = entries[i];
                if (!entry || !pickFirstMaterialImagePath([entry.path])) continue;
                const file = files[i];
                if (file) {
                  setPreviewFromPathAndUrl(entry.path, URL.createObjectURL(file));
                  return;
                }
                setPreviewFromPathAndUrl(entry.path, '');
                return;
              }
            }}
          >
            {drop.dragOver ? (
              <div className={editorStyles.dropHintOverlay}>Отпустите, чтобы загрузить изображение</div>
            ) : null}
            {previewSrc ? (
              <img src={previewSrc} alt="" className={matStyles.previewThumb} draggable={false} />
            ) : (
              <div className={editorStyles.muted}>Перетащите изображение или выберите файл</div>
            )}
            <Button
              onClick={() => {
                void (async () => {
                  const res = await api.invoke(ipcChannels.tokens.pickImage, {});
                  if (res.canceled) return;
                  setPreviewFromPathAndUrl(res.filePath, res.previewDataUrl);
                })();
              }}
            >
              Выбрать изображение
            </Button>
          </div>
          {!hasImage ? <div className={editorStyles.fieldError}>Нужно изображение</div> : null}
        </div>

        {error ? <div className={editorStyles.fieldError}>{error}</div> : null}

        <div className={editorStyles.modalFooter}>
          <Button onClick={onClose} disabled={saving}>
            Отмена
          </Button>
          <Button
            variant="primary"
            disabled={!canSave}
            onClick={() => {
              if (!canSave) return;
              void (async () => {
                setSaving(true);
                setError(null);
                try {
                  const { token } = await api.invoke(ipcChannels.tokens.upsert, {
                    id: initial?.id ?? null,
                    name: trimmed,
                    filePath: filePath ?? null,
                  });
                  onSaved(token);
                  onClose();
                } catch (e) {
                  setError(e instanceof Error ? e.message : 'Не удалось сохранить');
                } finally {
                  setSaving(false);
                }
              })();
            }}
          >
            Сохранить
          </Button>
        </div>
      </div>
    </>,
    document.body,
  );
}
