import React, { useCallback, useEffect, useMemo, useState } from 'react';

import { ipcChannels, type SessionState } from '../../shared/ipc/contracts';
import { buildNpcGroupForest, type NpcGroupTreeNode } from '../../shared/npcs/npcGroups';
import type { NpcGroupId, NpcId, ProjectNpc } from '../../shared/types';
import { useEditorI18n } from '../editor/i18n/EditorI18nContext';
import { sanitizeSceneDescriptionHtml } from '../editor/sceneDescriptionHtml';
import { getDndApi } from '../shared/dndApi';
import { useNpcsOverlayState } from '../shared/npcs/useNpcsOverlayState';
import { Button, Input } from '../shared/ui/controls';
import { useAssetUrl } from '../shared/useAssetImageUrl';

import styles from './NpcsApp.module.css';

function ZoomInIcon() {
  return (
    <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden>
      <circle cx="10.5" cy="10.5" r="6.25" fill="none" stroke="currentColor" strokeWidth="1.8" />
      <path d="M15.2 15.2 20 20" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
      <path
        d="M10.5 7.8v5.4M7.8 10.5h5.4"
        fill="none"
        stroke="currentColor"
        strokeWidth="1.8"
        strokeLinecap="round"
      />
    </svg>
  );
}

function ZoomOutIcon() {
  return (
    <svg viewBox="0 0 24 24" width="20" height="20" aria-hidden>
      <circle cx="10.5" cy="10.5" r="6.25" fill="none" stroke="currentColor" strokeWidth="1.8" />
      <path d="M15.2 15.2 20 20" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
      <path d="M7.8 10.5h5.4" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" />
    </svg>
  );
}

/** Убрать пустые ветки групп (удобно при поиске). */
function pruneEmptyGroupNodes(nodes: NpcGroupTreeNode[]): NpcGroupTreeNode[] {
  const out: NpcGroupTreeNode[] = [];
  for (const node of nodes) {
    const children = pruneEmptyGroupNodes(node.children);
    if (node.npcs.length === 0 && children.length === 0) continue;
    out.push({ ...node, children });
  }
  return out;
}

function RuntimeNpcTile({
  npc,
  selected,
  active,
  accentColor,
  onActivate,
}: {
  npc: ProjectNpc;
  selected: boolean;
  active: boolean;
  accentColor?: string | null;
  onActivate: () => void;
}) {
  const url = useAssetUrl(npc.avatarAssetId);
  return (
    <button
      type="button"
      className={[styles.tile, selected ? styles.tileSelected : '', active ? styles.tileActive : '']
        .filter(Boolean)
        .join(' ')}
      style={accentColor ? { borderLeftColor: accentColor, borderLeftWidth: 3 } : undefined}
      onClick={onActivate}
    >
      <div className={styles.tileAvatar}>
        {url ? <img className={styles.tileAvatarImg} src={url} alt="" draggable={false} /> : null}
      </div>
      <div className={styles.tileName}>{npc.name}</div>
    </button>
  );
}

function RuntimeGroupSection({
  node,
  depth,
  isExpanded,
  onToggleExpanded,
  selectedId,
  activeId,
  onActivate,
}: {
  node: NpcGroupTreeNode;
  depth: number;
  isExpanded: (id: NpcGroupId) => boolean;
  onToggleExpanded: (id: NpcGroupId) => void;
  selectedId: NpcId | null;
  activeId: NpcId | null;
  onActivate: (id: NpcId) => void;
}) {
  const g = node.group;
  const expanded = isExpanded(g.id);

  return (
    <div className={styles.groupBlock} style={{ paddingLeft: depth > 0 ? 12 : 0 }}>
      <div className={styles.groupHeader}>
        <button
          type="button"
          className={styles.groupToggle}
          onClick={() => onToggleExpanded(g.id)}
          aria-expanded={expanded}
        >
          {expanded ? '▾' : '▸'}
        </button>
        <span className={styles.groupColorDot} style={{ background: g.color }} aria-hidden />
        <span className={styles.groupTitle}>{g.name}</span>
      </div>
      {expanded ? (
        <div className={styles.groupBody}>
          {node.npcs.map((n) => (
            <RuntimeNpcTile
              key={n.id}
              npc={n}
              selected={n.id === selectedId}
              active={n.id === activeId}
              accentColor={g.color}
              onActivate={() => onActivate(n.id)}
            />
          ))}
          {node.children.map((child) => (
            <RuntimeGroupSection
              key={child.group.id}
              node={child}
              depth={depth + 1}
              isExpanded={isExpanded}
              onToggleExpanded={onToggleExpanded}
              selectedId={selectedId}
              activeId={activeId}
              onActivate={onActivate}
            />
          ))}
        </div>
      ) : null}
    </div>
  );
}

export function NpcsApp() {
  const { t } = useEditorI18n();
  const api = getDndApi();
  const [session, setSession] = useState<SessionState | null>(null);
  const [overlay, overlayApi] = useNpcsOverlayState();
  const [selectedId, setSelectedId] = useState<NpcId | null>(null);
  const [query, setQuery] = useState('');
  const [collapsedGroups, setCollapsedGroups] = useState<Set<NpcGroupId>>(() => new Set());

  useEffect(() => {
    void api.invoke(ipcChannels.project.get, {}).then(({ project }) => {
      setSession({ project, currentSceneId: project?.currentSceneId ?? null });
      const list = project?.npcs ?? [];
      setSelectedId(list[0]?.id ?? null);
    });
    return api.on(ipcChannels.session.stateChanged, ({ state }) => {
      setSession(state);
    });
  }, [api]);

  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') {
        if (overlay?.activeNpcId) {
          void overlayApi.dispatch({ kind: 'hide' });
          return;
        }
        if (overlay?.zoomTool) {
          void overlayApi.dispatch({ kind: 'zoomTool.set', tool: null });
          return;
        }
        window.close();
      }
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [overlay?.activeNpcId, overlay?.zoomTool, overlayApi]);

  const npcs = useMemo(() => session?.project?.npcs ?? [], [session?.project?.npcs]);
  const npcGroups = useMemo(() => session?.project?.npcGroups ?? [], [session?.project?.npcGroups]);
  const relations = useMemo(() => session?.project?.npcRelations ?? [], [session?.project?.npcRelations]);
  const activeId = overlay?.activeNpcId ?? null;
  const zoomTool = overlay?.zoomTool ?? null;
  const effectiveSelectedId =
    selectedId && npcs.some((n) => n.id === selectedId) ? selectedId : (npcs[0]?.id ?? null);
  const selected = npcs.find((n) => n.id === effectiveSelectedId) ?? null;

  const filteredNpcs = useMemo(() => {
    const q = query.trim().toLowerCase();
    if (!q) return npcs;
    return npcs.filter((n) => n.name.toLowerCase().includes(q));
  }, [npcs, query]);

  const searching = query.trim().length > 0;

  const { roots, ungrouped } = useMemo(() => {
    const forest = buildNpcGroupForest(npcGroups, filteredNpcs);
    if (!searching) return forest;
    return {
      roots: pruneEmptyGroupNodes(forest.roots),
      ungrouped: forest.ungrouped,
    };
  }, [filteredNpcs, npcGroups, searching]);

  const isExpanded = useCallback(
    (id: NpcGroupId) => {
      if (searching) return true;
      return !collapsedGroups.has(id);
    },
    [collapsedGroups, searching],
  );

  const toggleExpanded = useCallback(
    (id: NpcGroupId) => {
      if (searching) return;
      setCollapsedGroups((prev) => {
        const next = new Set(prev);
        if (next.has(id)) next.delete(id);
        else next.add(id);
        return next;
      });
    },
    [searching],
  );

  const relationsForSelected = useMemo(() => {
    if (!selected) return [];
    return relations
      .filter((r) => r.sourceNpcId === selected.id)
      .map((r) => {
        const other = npcs.find((n) => n.id === r.targetNpcId);
        return { id: r.id, text: `${r.label} ${other?.name ?? '—'}` };
      });
  }, [npcs, relations, selected]);

  const onSelectTile = useCallback(
    (id: NpcId) => {
      setSelectedId(id);
      void overlayApi.dispatch({ kind: 'toggle', npcId: id });
    },
    [overlayApi],
  );

  const safeHtml = selected ? sanitizeSceneDescriptionHtml(selected.description) : '';

  return (
    <div className={styles.page}>
      <div className={styles.toolbar}>
        <div className={styles.toolbarRow}>
          <Button
            variant={zoomTool === 'zoomIn' ? 'primary' : 'ghost'}
            iconOnly
            title={t('npcs.zoomIn')}
            ariaLabel={t('npcs.zoomIn')}
            tooltipPlacement="bottom"
            onClick={() => {
              void overlayApi.dispatch({
                kind: 'zoomTool.set',
                tool: zoomTool === 'zoomIn' ? null : 'zoomIn',
              });
            }}
          >
            <ZoomInIcon />
          </Button>
          <Button
            variant={zoomTool === 'zoomOut' ? 'primary' : 'ghost'}
            iconOnly
            title={t('npcs.zoomOut')}
            ariaLabel={t('npcs.zoomOut')}
            tooltipPlacement="bottom"
            onClick={() => {
              void overlayApi.dispatch({
                kind: 'zoomTool.set',
                tool: zoomTool === 'zoomOut' ? null : 'zoomOut',
              });
            }}
          >
            <ZoomOutIcon />
          </Button>
          {activeId ? (
            <Button
              title={t('npcs.closeOverlay')}
              ariaLabel={t('npcs.closeOverlay')}
              tooltipPlacement="bottom"
              onClick={() => {
                void overlayApi.dispatch({ kind: 'hide' });
              }}
            >
              {t('npcs.closeOverlay')}
            </Button>
          ) : null}
        </div>
        <div className={styles.toolbarHint}>
          {zoomTool === 'zoomIn'
            ? t('npcs.zoomInHint')
            : zoomTool === 'zoomOut'
              ? t('npcs.zoomOutHint')
              : t('npcs.zoomIdleHint')}
        </div>
      </div>

      <div className={styles.body}>
        <div className={styles.detail}>
          {selected ? (
            <>
              <div className={styles.detailName}>{selected.name}</div>
              {safeHtml ? (
                <div>
                  <div className={styles.detailSectionTitle}>{t('npcs.description')}</div>
                  <div className={styles.detailDesc} dangerouslySetInnerHTML={{ __html: safeHtml }} />
                </div>
              ) : (
                <div className={styles.muted}>{t('npcs.descriptionEmpty')}</div>
              )}
              {relationsForSelected.length > 0 ? (
                <div>
                  <div className={styles.detailSectionTitle}>{t('npcs.relations')}</div>
                  <div className={styles.relationsList}>
                    {relationsForSelected.map((r) => (
                      <div key={r.id} className={styles.relationItem}>
                        {r.text}
                      </div>
                    ))}
                  </div>
                </div>
              ) : null}
            </>
          ) : (
            <div className={styles.detailEmpty}>{t('npcs.windowEmpty')}</div>
          )}
        </div>

        <div className={styles.listCol}>
          <Input value={query} onChange={setQuery} placeholder={t('npcs.search')} />
          <div className={styles.list}>
            {npcGroups.length === 0 ? (
              filteredNpcs.map((n) => (
                <RuntimeNpcTile
                  key={n.id}
                  npc={n}
                  selected={n.id === effectiveSelectedId}
                  active={n.id === activeId}
                  onActivate={() => onSelectTile(n.id)}
                />
              ))
            ) : (
              <>
                {roots.map((node) => (
                  <RuntimeGroupSection
                    key={node.group.id}
                    node={node}
                    depth={0}
                    isExpanded={isExpanded}
                    onToggleExpanded={toggleExpanded}
                    selectedId={effectiveSelectedId}
                    activeId={activeId}
                    onActivate={onSelectTile}
                  />
                ))}
                {ungrouped.length > 0 || !searching ? (
                  <div className={styles.ungroupedSection}>
                    <div className={styles.ungroupedHeader}>{t('npcs.ungrouped')}</div>
                    <div className={styles.groupBody}>
                      {ungrouped.map((n) => (
                        <RuntimeNpcTile
                          key={n.id}
                          npc={n}
                          selected={n.id === effectiveSelectedId}
                          active={n.id === activeId}
                          onActivate={() => onSelectTile(n.id)}
                        />
                      ))}
                    </div>
                  </div>
                ) : null}
              </>
            )}

            {npcs.length === 0 ? <div className={styles.muted}>{t('npcs.windowEmpty')}</div> : null}
            {npcs.length > 0 && filteredNpcs.length === 0 ? (
              <div className={styles.muted}>{t('npcs.searchEmpty')}</div>
            ) : null}
          </div>
        </div>
      </div>
    </div>
  );
}
