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

import { noneBinding } from '../../shared/npcs/npcBinding';
import { buildNpcGroupForest } from '../../shared/npcs/npcGroups';
import type { NpcBinding, NpcGroupId, Project, ProjectNpc, ProjectNpcGroup } from '../../shared/types';
import editorStyles from '../editor/EditorApp.module.css';
import {
  filterMaterialImagePaths,
  getDroppedFileEntries,
  pickFirstMaterialImagePath,
  useFileDropZone,
} from '../editor/fileDrop';
import { useEditorI18n } from '../editor/i18n/EditorI18nContext';
import matStyles from '../editor/MaterialsModals.module.css';
import { Button, Input } from '../shared/ui/controls';
import controlStyles from '../shared/ui/Controls.module.css';
import { useAssetUrl } from '../shared/useAssetImageUrl';

import { NpcBindingFields } from './NpcBindingFields';

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

function flattenGroupOptions(
  nodes: ReturnType<typeof buildNpcGroupForest>['roots'],
  depth = 0,
): { id: NpcGroupId; label: string }[] {
  const out: { id: NpcGroupId; label: string }[] = [];
  for (const node of nodes) {
    const prefix = depth > 0 ? '  '.repeat(depth) : '';
    out.push({ id: node.group.id, label: `${prefix}${node.group.name}` });
    out.push(...flattenGroupOptions(node.children, depth + 1));
  }
  return out;
}

type NpcEditModalProps = {
  open: boolean;
  initial: ProjectNpc | null;
  existingNames: string[];
  project: Project | null;
  npcGroups: ProjectNpcGroup[];
  onClose: () => void;
  onPickImage: () => Promise<{ filePath: string; previewDataUrl: string } | null>;
  onSave: (input: {
    name: string;
    filePath?: string;
    groupId?: NpcGroupId | null;
    binding?: NpcBinding;
  }) => Promise<void>;
};

export function NpcEditModal({
  open,
  initial,
  existingNames,
  project,
  npcGroups,
  onClose,
  onPickImage,
  onSave,
}: NpcEditModalProps) {
  const { t } = useEditorI18n();
  const [name, setName] = useState('');
  const [filePath, setFilePath] = useState<string | null>(null);
  const [localPreviewUrl, setLocalPreviewUrl] = useState<string | null>(null);
  const [groupId, setGroupId] = useState<NpcGroupId | ''>('');
  const [binding, setBinding] = useState<NpcBinding>(noneBinding());
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const existingUrl = useAssetUrl(initial?.avatarAssetId ?? null);

  const groupOptions = useMemo(() => {
    const { roots } = buildNpcGroupForest(npcGroups, []);
    return flattenGroupOptions(roots);
  }, [npcGroups]);

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

  useEffect(() => {
    return () => {
      if (localPreviewUrl?.startsWith('blob:')) URL.revokeObjectURL(localPreviewUrl);
    };
  }, [localPreviewUrl]);

  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;
    });
  };

  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?.avatarAssetId);
  const canSave = nameOk && !nameDup && hasImage && !saving;
  const previewSrc = localPreviewUrl ?? existingUrl;

  if (!open) return null;

  return createPortal(
    <>
      <button
        type="button"
        aria-label={t('common.close')}
        onClick={onClose}
        className={editorStyles.modalBackdrop}
      />
      <div role="dialog" aria-modal="true" className={editorStyles.modalDialog}>
        <div className={editorStyles.modalHeader}>
          <div className={editorStyles.modalTitle}>{initial ? t('npcs.editTitle') : t('npcs.addTitle')}</div>
          <button
            type="button"
            aria-label={t('common.close')}
            onClick={onClose}
            className={editorStyles.modalClose}
          >
            ×
          </button>
        </div>

        <div className={editorStyles.fieldGrid}>
          <div className={editorStyles.fieldLabel}>{t('npcs.name')}</div>
          <Input value={name} onChange={setName} placeholder={t('npcs.namePlaceholder')} />
          {!nameOk ? <div className={editorStyles.fieldError}>{t('npcs.nameRequired')}</div> : null}
          {nameDup ? <div className={editorStyles.fieldError}>{t('npcs.nameDup')}</div> : null}
        </div>

        {!initial && groupOptions.length > 0 ? (
          <div className={editorStyles.fieldGrid}>
            <div className={editorStyles.fieldLabel}>{t('npcs.group')}</div>
            <select
              className={controlStyles.input}
              value={groupId}
              onChange={(e) => setGroupId(e.target.value as NpcGroupId | '')}
            >
              <option value="">{t('npcs.ungrouped')}</option>
              {groupOptions.map((g) => (
                <option key={g.id} value={g.id}>
                  {g.label}
                </option>
              ))}
            </select>
          </div>
        ) : null}

        <div className={editorStyles.fieldGrid}>
          <div className={editorStyles.fieldLabel}>{t('npcs.avatar')}</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}>{t('npcs.dropHint')}</div> : null}
            {previewSrc ? (
              <img className={matStyles.previewThumb} src={previewSrc} alt="" />
            ) : (
              <div className={editorStyles.muted}>{t('npcs.avatarEmpty')}</div>
            )}
            <Button
              onClick={() => {
                void (async () => {
                  const picked = await onPickImage();
                  if (!picked) return;
                  setPreviewFromPathAndUrl(picked.filePath, picked.previewDataUrl);
                })();
              }}
            >
              {t('npcs.chooseAvatar')}
            </Button>
          </div>
          {!hasImage ? <div className={editorStyles.fieldError}>{t('npcs.avatarRequired')}</div> : null}
        </div>

        {project && !initial ? (
          <NpcBindingFields project={project} binding={binding} onChange={setBinding} />
        ) : null}

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

        <div className={editorStyles.modalFooter}>
          <Button onClick={onClose} disabled={saving}>
            {t('common.cancel')}
          </Button>
          <Button
            variant="primary"
            disabled={!canSave}
            onClick={() => {
              if (!canSave) return;
              void (async () => {
                setSaving(true);
                setError(null);
                try {
                  await onSave({
                    name: trimmed,
                    ...(filePath ? { filePath } : {}),
                    ...(!initial ? { groupId: groupId || null, binding } : {}),
                  });
                  onClose();
                } catch (e) {
                  setError(e instanceof Error ? e.message : String(e));
                } finally {
                  setSaving(false);
                }
              })();
            }}
          >
            {t('common.save')}
          </Button>
        </div>
      </div>
    </>,
    document.body,
  );
}
