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

import {
  collectSceneIdsForSelections,
  findSceneTitleConflicts,
  storylineSelectionKey,
  type SceneImportResolution,
  type SceneTitleConflict,
  type StorylineImportMergeReport,
  type StorylineLabels,
  type StorylineListItem,
  type StorylineSelection,
} from '../../shared/graph/storylineExportImport';
import type { Project, ProjectId, SceneId } from '../../shared/types';
import { Button } from '../shared/ui/controls';
import { useEditorI18n } from './i18n/EditorI18nContext';
import styles from './EditorApp.module.css';

type ExportProjectModalProps = {
  open: boolean;
  projects: { id: ProjectId; name: string; fileName: string }[];
  initialProjectId: ProjectId | null;
  storylineLabels: StorylineLabels;
  loadStorylines: (projectId: ProjectId) => Promise<StorylineListItem[]>;
  onClose: () => void;
  onExport: (projectId: ProjectId, selections: StorylineSelection[]) => Promise<void>;
};

export function ExportProjectModal({
  open,
  projects,
  initialProjectId,
  storylineLabels,
  loadStorylines,
  onClose,
  onExport,
}: ExportProjectModalProps) {
  const { t } = useEditorI18n();
  const [projectId, setProjectId] = useState<ProjectId | null>(initialProjectId);
  const [storylines, setStorylines] = useState<StorylineListItem[]>([]);
  const [selectedKeys, setSelectedKeys] = useState<Set<string>>(new Set());
  const [loadingStorylines, setLoadingStorylines] = useState(false);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (!open) return;
    setProjectId(initialProjectId);
    setSaving(false);
    setError(null);
    setSelectedKeys(new Set());
  }, [initialProjectId, open]);

  useEffect(() => {
    if (!open || !projectId) {
      setStorylines([]);
      return;
    }
    let cancelled = false;
    setLoadingStorylines(true);
    void (async () => {
      try {
        const list = await loadStorylines(projectId);
        if (cancelled) return;
        setStorylines(list);
        setSelectedKeys(new Set(list.map((item) => storylineSelectionKey(item.selection))));
      } catch (e) {
        if (!cancelled) setError(e instanceof Error ? e.message : String(e));
      } finally {
        if (!cancelled) setLoadingStorylines(false);
      }
    })();
    return () => {
      cancelled = true;
    };
  }, [loadStorylines, open, projectId]);

  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;

  const canExport =
    projectId !== null &&
    projects.some((p) => p.id === projectId) &&
    selectedKeys.size > 0 &&
    !loadingStorylines;

  const toggleKey = (key: string, disabled?: boolean) => {
    if (disabled) return;
    setSelectedKeys((prev) => {
      const next = new Set(prev);
      if (next.has(key)) next.delete(key);
      else next.add(key);
      return next;
    });
  };

  const selectedSelections = storylines
    .filter((item) => selectedKeys.has(storylineSelectionKey(item.selection)))
    .map((item) => item.selection);

  return createPortal(
    <>
      <button type="button" aria-label={t('common.close')} onClick={onClose} className={styles.modalBackdrop} />
      <div role="dialog" aria-modal="true" className={styles.modalDialog}>
        <div className={styles.modalHeader}>
          <div className={styles.modalTitle}>{t('export.title')}</div>
          <button type="button" aria-label={t('common.close')} onClick={onClose} className={styles.modalClose}>
            ×
          </button>
        </div>

        <div className={styles.fieldGrid}>
          <div className={styles.fieldLabel}>{t('export.project')}</div>
          <select
            className={styles.selectInput}
            value={projectId ?? ''}
            onChange={(e) => setProjectId((e.target.value as ProjectId) || null)}
            disabled={projects.length === 0}
          >
            {projects.map((p) => (
              <option key={p.id} value={p.id}>
                {p.name} ({p.fileName})
              </option>
            ))}
          </select>

          <div className={styles.fieldLabel}>{t('storyline.section')}</div>
          {loadingStorylines ? (
            <div className={styles.muted}>{t('storyline.loading')}</div>
          ) : storylines.length === 0 ? (
            <div className={styles.muted}>{t('storyline.empty')}</div>
          ) : (
            <div className={styles.storylineChecklist}>
              {storylines.map((item) => {
                const key = storylineSelectionKey(item.selection);
                const checked = selectedKeys.has(key);
                const disabled = item.disabled === true;
                return (
                  <label key={key} className={disabled ? styles.storylineCheckDisabled : styles.storylineCheck}>
                    <input
                      type="checkbox"
                      checked={checked}
                      disabled={disabled}
                      onChange={() => toggleKey(key, disabled)}
                    />
                    <span>{item.label}</span>
                    {disabled && item.disabledReason === 'main_exists' ? (
                      <span className={styles.muted}> — {t('storyline.mainExistsHint')}</span>
                    ) : null}
                  </label>
                );
              })}
            </div>
          )}

          <div className={styles.muted}>{t('export.hint')}</div>
        </div>

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

        <div className={styles.modalFooter}>
          <Button onClick={onClose} disabled={saving} title={saving ? t('export.exporting') : undefined}>
            {t('common.cancel')}
          </Button>
          <Button
            variant="primary"
            disabled={!canExport || saving}
            onClick={() => {
              if (!projectId || !canExport) return;
              void (async () => {
                setSaving(true);
                setError(null);
                try {
                  await onExport(projectId, selectedSelections);
                  onClose();
                } catch (e) {
                  setError(e instanceof Error ? e.message : String(e));
                } finally {
                  setSaving(false);
                }
              })();
            }}
          >
            {t('export.saveAs')}
          </Button>
        </div>
      </div>
    </>,
    document.body,
  );
}

export type ImportPeekResult = {
  kind: 'file' | 'project';
  filePath?: string;
  sourceProjectId?: ProjectId;
  projectName: string;
  storylines: StorylineListItem[];
  sourceProject: Project;
};

export type ImportSourceSelection =
  | { kind: 'file'; filePath: string; fileName: string }
  | { kind: 'project'; sourceProjectId: ProjectId };

type ImportSourceModalProps = {
  open: boolean;
  /** false — только импорт из файла (полный импорт проекта). */
  canImportFromProject: boolean;
  projects: { id: ProjectId; name: string; fileName: string }[];
  currentProjectId: ProjectId | null;
  pickFile: () => Promise<{ canceled: true } | { canceled: false; filePath: string }>;
  onClose: () => void;
  onContinue: (selection: ImportSourceSelection) => Promise<void>;
};

export function ImportSourceModal({
  open,
  canImportFromProject,
  projects,
  currentProjectId,
  pickFile,
  onClose,
  onContinue,
}: ImportSourceModalProps) {
  const { t } = useEditorI18n();
  const [importKind, setImportKind] = useState<'project' | 'file'>('file');
  const [sourceProjectId, setSourceProjectId] = useState<ProjectId | null>(null);
  const [pickedFile, setPickedFile] = useState<{ path: string; name: string } | null>(null);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<string | null>(null);

  const availableProjects = useMemo(
    () => projects.filter((p) => p.id !== currentProjectId),
    [currentProjectId, projects],
  );

  useEffect(() => {
    if (!open) return;
    setImportKind(canImportFromProject && availableProjects.length > 0 ? 'project' : 'file');
    setSourceProjectId(availableProjects[0]?.id ?? null);
    setPickedFile(null);
    setSubmitting(false);
    setError(null);
  }, [availableProjects, canImportFromProject, 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;

  const canContinue =
    !submitting &&
    (importKind === 'project'
      ? canImportFromProject && sourceProjectId !== null
      : pickedFile !== 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}>
        <div className={styles.modalHeader}>
          <div className={styles.modalTitle}>{t('importSource.title')}</div>
          <button type="button" aria-label={t('common.close')} onClick={onClose} className={styles.modalClose}>
            ×
          </button>
        </div>

        <div className={styles.fieldGrid}>
          {canImportFromProject ? (
            <>
              <div className={styles.fieldLabel}>{t('importSource.type')}</div>
              <select
                className={styles.selectInput}
                value={importKind}
                onChange={(e) => setImportKind(e.target.value as 'project' | 'file')}
                disabled={submitting}
              >
                <option value="project">{t('importSource.fromProject')}</option>
                <option value="file">{t('importSource.fromFile')}</option>
              </select>
            </>
          ) : (
            <div className={styles.muted}>{t('importSource.fileOnlyHint')}</div>
          )}

          {importKind === 'project' && canImportFromProject ? (
            <>
              <div className={styles.fieldLabel}>{t('importSource.project')}</div>
              <select
                className={styles.selectInput}
                value={sourceProjectId ?? ''}
                onChange={(e) => setSourceProjectId((e.target.value as ProjectId) || null)}
                disabled={availableProjects.length === 0 || submitting}
              >
                {availableProjects.map((p) => (
                  <option key={p.id} value={p.id}>
                    {p.name} ({p.fileName})
                  </option>
                ))}
              </select>
              {availableProjects.length === 0 ? (
                <div className={styles.muted}>{t('importSource.noOtherProjects')}</div>
              ) : null}
            </>
          ) : (
            <>
              <div className={styles.fieldLabel}>{t('importSource.file')}</div>
              <div className={styles.importFileRow}>
                <Button
                  disabled={submitting}
                  onClick={() => {
                    void (async () => {
                      setError(null);
                      const res = await pickFile();
                      if (res.canceled) return;
                      const name = res.filePath.split(/[/\\]/).pop() ?? res.filePath;
                      setPickedFile({ path: res.filePath, name });
                    })();
                  }}
                >
                  {t('importSource.chooseFile')}
                </Button>
                <span className={styles.muted}>
                  {pickedFile ? pickedFile.name : t('importSource.noFileSelected')}
                </span>
              </div>
            </>
          )}
        </div>

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

        <div className={styles.modalFooter}>
          <Button onClick={onClose} disabled={submitting}>
            {t('common.cancel')}
          </Button>
          <Button
            variant="primary"
            disabled={!canContinue}
            onClick={() => {
              void (async () => {
                setSubmitting(true);
                setError(null);
                try {
                  if (importKind === 'project' && canImportFromProject && sourceProjectId) {
                    await onContinue({ kind: 'project', sourceProjectId });
                  } else if (pickedFile) {
                    await onContinue({ kind: 'file', filePath: pickedFile.path, fileName: pickedFile.name });
                  }
                } catch (e) {
                  setError(e instanceof Error ? e.message : String(e));
                } finally {
                  setSubmitting(false);
                }
              })();
            }}
          >
            {t('importStoryline.continue')}
          </Button>
        </div>
      </div>
    </>,
    document.body,
  );
}

type ImportStorylinesModalProps = {
  open: boolean;
  sourceName: string;
  storylines: StorylineListItem[];
  onClose: () => void;
  onContinue: (selections: StorylineSelection[]) => void;
};

export function ImportStorylinesModal({
  open,
  sourceName,
  storylines,
  onClose,
  onContinue,
}: ImportStorylinesModalProps) {
  const { t } = useEditorI18n();
  const [selectedKeys, setSelectedKeys] = useState<Set<string>>(new Set());

  useEffect(() => {
    if (!open) return;
    setSelectedKeys(new Set());
  }, [open, sourceName]);

  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;

  const toggleKey = (key: string, disabled?: boolean) => {
    if (disabled) return;
    setSelectedKeys((prev) => {
      const next = new Set(prev);
      if (next.has(key)) next.delete(key);
      else next.add(key);
      return next;
    });
  };

  const selectedSelections = storylines
    .filter((item) => selectedKeys.has(storylineSelectionKey(item.selection)))
    .map((item) => item.selection);

  const canContinue = selectedKeys.size > 0;

  return createPortal(
    <>
      <button type="button" aria-label={t('common.close')} onClick={onClose} className={styles.modalBackdrop} />
      <div role="dialog" aria-modal="true" className={styles.modalDialog}>
        <div className={styles.modalHeader}>
          <div className={styles.modalTitle}>{t('importStoryline.title')}</div>
          <button type="button" aria-label={t('common.close')} onClick={onClose} className={styles.modalClose}>
            ×
          </button>
        </div>

        <div className={styles.fieldGrid}>
          <div className={styles.fieldLabel}>{t('importStoryline.source')}</div>
          <div>{sourceName}</div>

          <div className={styles.fieldLabel}>{t('storyline.section')}</div>
          {storylines.length === 0 ? (
            <div className={styles.muted}>{t('storyline.empty')}</div>
          ) : (
            <div className={styles.storylineChecklist}>
              {storylines.map((item) => {
                const key = storylineSelectionKey(item.selection);
                const disabled = item.disabled === true;
                return (
                  <label key={key} className={disabled ? styles.storylineCheckDisabled : styles.storylineCheck}>
                    <input
                      type="checkbox"
                      checked={selectedKeys.has(key)}
                      disabled={disabled}
                      onChange={() => toggleKey(key, disabled)}
                    />
                    <span>{item.label}</span>
                    {disabled && item.disabledReason === 'main_exists' ? (
                      <span className={styles.muted}> — {t('storyline.mainExistsHint')}</span>
                    ) : null}
                  </label>
                );
              })}
            </div>
          )}
        </div>

        <div className={styles.modalFooter}>
          <Button onClick={onClose}>{t('common.cancel')}</Button>
          <Button
            variant="primary"
            disabled={!canContinue}
            onClick={() => onContinue(selectedSelections)}
          >
            {t('importStoryline.continue')}
          </Button>
        </div>
      </div>
    </>,
    document.body,
  );
}

type SceneConflictModalProps = {
  open: boolean;
  conflicts: SceneTitleConflict[];
  onClose: () => void;
  onConfirm: (resolutions: SceneImportResolution[]) => void;
};

export function SceneConflictModal({ open, conflicts, onClose, onConfirm }: SceneConflictModalProps) {
  const { t } = useEditorI18n();
  const [choices, setChoices] = useState<Record<string, 'create' | SceneId>>({});

  useEffect(() => {
    if (!open) return;
    const init: Record<string, 'create' | SceneId> = {};
    for (const c of conflicts) {
      init[c.sourceSceneId] = c.matches[0]?.sceneId ?? 'create';
    }
    setChoices(init);
  }, [conflicts, 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} ${styles.modalDialogWide}`}>
        <div className={styles.modalHeader}>
          <div className={styles.modalTitle}>{t('importStoryline.conflictsTitle')}</div>
          <button type="button" aria-label={t('common.close')} onClick={onClose} className={styles.modalClose}>
            ×
          </button>
        </div>

        <p className={styles.muted}>{t('importStoryline.conflictsHint')}</p>

        <div className={styles.conflictList}>
          {conflicts.map((c) => (
            <div key={c.sourceSceneId} className={styles.conflictRow}>
              <div className={styles.conflictTitle}>{c.sourceTitle}</div>
              <select
                className={styles.selectInput}
                value={choices[c.sourceSceneId] ?? 'create'}
                onChange={(e) => {
                  const v = e.target.value;
                  setChoices((prev) => ({
                    ...prev,
                    [c.sourceSceneId]: v === 'create' ? 'create' : (v as SceneId),
                  }));
                }}
              >
                <option value="create">{t('importStoryline.createNewScene')}</option>
                {c.matches.map((m) => (
                  <option key={m.sceneId} value={m.sceneId}>
                    {t('importStoryline.useExistingScene', { title: m.title })}
                  </option>
                ))}
              </select>
            </div>
          ))}
        </div>

        <div className={styles.modalFooter}>
          <Button onClick={onClose}>{t('common.cancel')}</Button>
          <Button
            variant="primary"
            onClick={() => {
              const resolutions: SceneImportResolution[] = conflicts.map((c) => {
                const choice = choices[c.sourceSceneId] ?? 'create';
                if (choice === 'create') return { sourceSceneId: c.sourceSceneId, mode: 'create' };
                return { sourceSceneId: c.sourceSceneId, mode: 'use', targetSceneId: choice };
              });
              onConfirm(resolutions);
            }}
          >
            {t('importStoryline.import')}
          </Button>
        </div>
      </div>
    </>,
    document.body,
  );
}

type ImportReportModalProps = {
  open: boolean;
  report: StorylineImportMergeReport | null;
  onClose: () => void;
};

export function ImportReportModal({ open, report, onClose }: ImportReportModalProps) {
  const { t } = useEditorI18n();

  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 || !report) 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}>
        <div className={styles.modalHeader}>
          <div className={styles.modalTitle}>{t('importStoryline.reportTitle')}</div>
          <button type="button" aria-label={t('common.close')} onClick={onClose} className={styles.modalClose}>
            ×
          </button>
        </div>

        <ul className={styles.reportList}>
          <li>{t('importStoryline.reportLines', { count: report.storylinesImported })}</li>
          <li>{t('importStoryline.reportScenesCreated', { count: report.scenesCreated })}</li>
          <li>{t('importStoryline.reportScenesReused', { count: report.scenesReused })}</li>
          <li>{t('importStoryline.reportNodes', { count: report.graphNodesAdded })}</li>
          <li>{t('importStoryline.reportEdges', { count: report.edgesAdded })}</li>
          <li>{t('importStoryline.reportAssetsCopied', { count: report.assetsCopied })}</li>
          <li>{t('importStoryline.reportAssetsReused', { count: report.assetsReused })}</li>
          {report.renamedSideTitles.length > 0 ? (
            <li>
              {t('importStoryline.reportRenamedSides', { names: report.renamedSideTitles.join(', ') })}
            </li>
          ) : null}
        </ul>

        <div className={styles.modalFooter}>
          <Button variant="primary" onClick={onClose}>
            {t('common.close')}
          </Button>
        </div>
      </div>
    </>,
    document.body,
  );
}

export function buildSceneResolutionsForImport(
  targetProject: Project,
  sourceProject: Project,
  selections: StorylineSelection[],
  conflicts: SceneTitleConflict[],
  userResolutions: SceneImportResolution[],
): SceneImportResolution[] {
  const sceneIds = collectSceneIdsForSelections(sourceProject, selections);
  const conflictIds = new Set(conflicts.map((c) => c.sourceSceneId));
  const bySource = new Map(userResolutions.map((r) => [r.sourceSceneId, r]));
  const out: SceneImportResolution[] = [];
  for (const sid of sceneIds) {
    if (conflictIds.has(sid)) {
      const r = bySource.get(sid);
      if (r) out.push(r);
      continue;
    }
    out.push({ sourceSceneId: sid, mode: 'create' });
  }
  return out;
}

export function computeImportConflicts(
  targetProject: Project,
  sourceProject: Project,
  selections: StorylineSelection[],
): SceneTitleConflict[] {
  const sceneIds = collectSceneIdsForSelections(sourceProject, selections);
  return findSceneTitleConflicts(targetProject, sourceProject, sceneIds);
}

export function useStorylineLabels(): StorylineLabels {
  const { t } = useEditorI18n();
  return useMemo(
    () => ({
      main: t('storyline.main'),
      untitled: t('graph.untitled'),
    }),
    [t],
  );
}
