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

import { ipcChannels } from '../../shared/ipc/contracts';
import type { MaterialId, MaterialLegend, ProjectMaterial } from '../../shared/types';
import { getDndApi } from '../shared/dndApi';
import { Button, Input } from '../shared/ui/controls';
import { useAssetUrl } from '../shared/useAssetImageUrl';

import styles from './EditorApp.module.css';
import {
  filterMaterialImagePaths,
  getDroppedFileEntries,
  pickFirstMaterialImagePath,
  useFileDropZone,
} from './fileDrop';
import { useEditorI18n } from './i18n/EditorI18nContext';
import { MaterialsBrowser } from './MaterialsBrowser';
import matStyles from './MaterialsModals.module.css';

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

type MaterialEditModalProps = {
  open: boolean;
  initial: ProjectMaterial | null;
  existingNames: string[];
  onClose: () => void;
  onPickImage: () => Promise<{ filePath: string; previewDataUrl: string } | null>;
  onSave: (input: { name: string; filePath?: string }) => Promise<void>;
};

export function MaterialEditModal({
  open,
  initial,
  existingNames,
  onClose,
  onPickImage,
  onSave,
}: MaterialEditModalProps) {
  const { t } = useEditorI18n();
  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 [saveProgress, setSaveProgress] = useState<{ percent: number; detail: string } | null>(null);
  const [error, setError] = useState<string | null>(null);
  const existingUrl = useAssetUrl(initial?.assetId ?? null);

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

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

  useEffect(() => {
    if (!open) return;
    return api.on(ipcChannels.project.materialUpsertProgress, (evt) => {
      setSaveProgress({
        percent: Math.max(0, Math.min(100, Math.round(evt.percent))),
        detail: evt.detail?.trim() || t('materials.savingWait'),
      });
    });
  }, [api, open, t]);

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

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

  if (!open) return null;

  const progressPercent = saveProgress?.percent ?? (saving ? 0 : 0);
  const progressDetail = saveProgress?.detail ?? t('materials.savingWait');

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

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

        <div className={styles.fieldGrid}>
          <div className={styles.fieldLabel}>{t('materials.image')}</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 (!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={styles.dropHintOverlay}>{t('materials.dropHint')}</div> : null}
            {previewSrc ? (
              <img className={matStyles.previewThumb} src={previewSrc} alt="" />
            ) : (
              <div className={matStyles.imageDropEmpty}>
                <div className={styles.muted}>{t('materials.imageEmpty')}</div>
                <Button
                  disabled={saving}
                  onClick={() => {
                    void (async () => {
                      const picked = await onPickImage();
                      if (!picked) return;
                      setPreviewFromPathAndUrl(picked.filePath, picked.previewDataUrl);
                    })();
                  }}
                >
                  {t('materials.chooseImage')}
                </Button>
              </div>
            )}
            {previewSrc ? (
              <Button
                disabled={saving}
                onClick={() => {
                  void (async () => {
                    const picked = await onPickImage();
                    if (!picked) return;
                    setPreviewFromPathAndUrl(picked.filePath, picked.previewDataUrl);
                  })();
                }}
              >
                {t('materials.chooseImage')}
              </Button>
            ) : null}
          </div>
          {!hasImage ? <div className={styles.fieldError}>{t('materials.imageRequired')}</div> : null}
        </div>

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

        <div className={styles.modalFooter}>
          <Button onClick={onClose} disabled={saving}>
            {t('common.cancel')}
          </Button>
          <Button
            variant="primary"
            disabled={!canSave}
            onClick={() => {
              if (!canSave) return;
              void (async () => {
                flushSync(() => {
                  setSaving(true);
                  setSaveProgress({ percent: 0, detail: t('materials.savingWait') });
                  setError(null);
                });
                try {
                  await onSave(filePath ? { name: trimmed, filePath } : { name: trimmed });
                  onClose();
                } catch (e) {
                  setError(e instanceof Error ? e.message : String(e));
                } finally {
                  setSaving(false);
                  setSaveProgress(null);
                }
              })();
            }}
          >
            {saving ? t('common.saving') : t('common.save')}
          </Button>
        </div>
      </div>
      {saving ? (
        <div
          className={styles.progressOverlay}
          role="dialog"
          aria-label={t('materials.savingProgress')}
          aria-busy
        >
          <div className={styles.progressModal}>
            <div className={styles.progressTitle}>{t('materials.savingTitle')}</div>
            <div className={styles.previewSpinner} aria-hidden />
            <div className={styles.progressBar}>
              <div
                className={styles.progressFill}
                style={{ width: `${String(Math.max(0, Math.min(100, progressPercent)))}%` }}
              />
            </div>
            <div className={styles.progressMeta}>
              <div>{progressDetail}</div>
              <div>{progressPercent}%</div>
            </div>
          </div>
        </div>
      ) : null}
    </>,
    document.body,
  );
}

type MaterialsManagerModalProps = {
  open: boolean;
  materials: ProjectMaterial[];
  onClose: () => void;
  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>;
};

export function MaterialsManagerModal({
  open,
  materials,
  onClose,
  onAdd,
  onEdit,
  onDelete,
  onReorder,
  onRotate,
  onLegendChange,
}: MaterialsManagerModalProps) {
  const { t } = useEditorI18n();
  const [selectedId, setSelectedId] = useState<MaterialId | null>(null);
  const onSelect = useCallback((id: MaterialId | null) => setSelectedId(id), []);

  useEffect(() => {
    if (!open) return;
    setSelectedId(materials[0]?.id ?? null);
  }, [open]);

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

  if (!open) return null;

  return createPortal(
    <>
      <button type="button" aria-label={t('common.close')} onClick={onClose} className={styles.modalBackdrop} />
      <div role="dialog" aria-modal="true" className={[styles.modalDialog, matStyles.managerDialog].join(' ')}>
        <div className={styles.modalHeader}>
          <div className={styles.modalTitle}>{t('materials.managerTitle')}</div>
          <button type="button" aria-label={t('common.close')} onClick={onClose} className={styles.modalClose}>
            ×
          </button>
        </div>
        <MaterialsBrowser
          mode="editor"
          materials={materials}
          selectedId={selectedId}
          onSelect={onSelect}
          onAdd={onAdd}
          onEdit={onEdit}
          onDelete={onDelete}
          onReorder={onReorder}
          onRotate={onRotate}
          {...(onLegendChange ? { onLegendChange } : {})}
        />
      </div>
    </>,
    document.body,
  );
}
