/*
 * W12 시스템관리 — 조직(부서) 관리. WISE 조직도(UIWS system/dept·org) 이식.
 *   좌: 부서 계층 트리(상위→하위 들여쓰기·펼침/접힘·정렬/사용 배지·선택 강조·통합검색·자동펼침).
 *   우: 선택 부서 상세(부서명·ID·상위경로·정렬·사용·하위 수) + CRUD(하위 추가/수정/삭제, 최상위 추가).
 * 정본 계약(불변): GET/POST /api/admin/depts · DELETE /{id} (DeptController). 트리 응답(children 중첩), upsert 저장.
 *   표현만 트리 조직도화 — 삭제 가드(하위 존재 시 클라 선검사 + 서버 권위).
 */
import { useEffect, useMemo, useRef, useState, type FormEvent, type ReactNode } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { EmptyState, ErrorState, Skeleton } from '../../components/ui/States';
import { Button } from '../../components/ui/Button';
import {
  IconPlus, IconUsers, IconSearch, IconChevronRight, IconChevronDown,
} from '../../components/ui/icons';
import { errMessage, useToast } from '../work/workShared';
import { deptApi, type DeptNode, type DeptSaveBody } from './systemBasicsApi';
import './admin-modules.css';
import './dept-admin.css';

// ── 트리 유틸 ─────────────────────────────────────────────
/** id → 노드 인덱스(상세·경로·부모명 조회). */
function indexNodes(nodes: DeptNode[]): Map<string, DeptNode> {
  const map = new Map<string, DeptNode>();
  const walk = (ns: DeptNode[]) => {
    for (const n of ns) { map.set(n.id, n); walk(n.children ?? []); }
  };
  walk(nodes);
  return map;
}

/** 전위순회 평면화 — 부모 후보 select·정렬값 산출. */
function flattenNodes(nodes: DeptNode[], acc: DeptNode[] = []): DeptNode[] {
  for (const n of nodes) { acc.push(n); flattenNodes(n.children ?? [], acc); }
  return acc;
}

/** 모든 노드 id(전체 펼침용). */
function collectIds(nodes: DeptNode[], acc: string[] = []): string[] {
  for (const n of nodes) { acc.push(n.id); collectIds(n.children ?? [], acc); }
  return acc;
}

interface FlatRow { node: DeptNode; depth: number; hasChildren: boolean }
/** 펼침 상태를 반영해 가시 행만 평탄화. */
function flattenVisible(nodes: DeptNode[], expanded: Set<string>, depth = 0, acc: FlatRow[] = []): FlatRow[] {
  for (const node of nodes) {
    const hasChildren = (node.children?.length ?? 0) > 0;
    acc.push({ node, depth, hasChildren });
    if (hasChildren && expanded.has(node.id)) flattenVisible(node.children!, expanded, depth + 1, acc);
  }
  return acc;
}

/** 부서명 매칭 id 집합 + 조상(자동 펼침용). */
function computeMatches(nodes: DeptNode[], q: string): { matched: Set<string>; ancestors: Set<string> } {
  const matched = new Set<string>();
  const ancestors = new Set<string>();
  const lc = q.toLowerCase();
  const walk = (node: DeptNode, chain: string[]) => {
    if (node.deptName.toLowerCase().includes(lc)) {
      matched.add(node.id);
      chain.forEach((a) => ancestors.add(a));
    }
    (node.children ?? []).forEach((c) => walk(c, [...chain, node.id]));
  };
  nodes.forEach((n) => walk(n, []));
  return { matched, ancestors };
}

/** 선택 부서의 전체 경로(루트 › … › 부서). */
function deptPath(id: string, index: Map<string, DeptNode>): string {
  const parts: string[] = [];
  let cur: DeptNode | undefined = index.get(id);
  const guard = new Set<string>();
  while (cur && !guard.has(cur.id)) {
    guard.add(cur.id);
    parts.unshift(cur.deptName);
    cur = cur.parentId ? index.get(cur.parentId) : undefined;
  }
  return parts.join(' › ');
}

/** 자손 id 집합(부모 후보 순환 제외용). */
function descendantsOf(node: DeptNode): Set<string> {
  const set = new Set<string>();
  const walk = (n: DeptNode) => (n.children ?? []).forEach((c) => { set.add(c.id); walk(c); });
  walk(node);
  return set;
}

/** 검색어 하이라이트. */
function Highlight({ text, term }: { text: string; term: string }): ReactNode {
  if (!term) return text;
  const idx = text.toLowerCase().indexOf(term.toLowerCase());
  if (idx < 0) return text;
  return (
    <>
      {text.slice(0, idx)}
      <mark className="kx-dept-mark">{text.slice(idx, idx + term.length)}</mark>
      {text.slice(idx + term.length)}
    </>
  );
}

// ── 메인 ──────────────────────────────────────────────────
type ModalState =
  | { mode: 'create'; parentId: string | null }
  | { mode: 'edit'; node: DeptNode }
  | null;

export function DeptAdminPage() {
  const { t } = useTranslation();
  const qc = useQueryClient();
  const { show, node: toast } = useToast();
  const q = useQuery({ queryKey: ['admin-depts'], queryFn: deptApi.tree, retry: false });

  const [expanded, setExpanded] = useState<Set<string>>(new Set());
  const [selectedId, setSelectedId] = useState<string | null>(null);
  const [query, setQuery] = useState('');
  const [debouncedQ, setDebouncedQ] = useState('');
  const [modal, setModal] = useState<ModalState>(null);
  const bootRef = useRef(false);

  const tree = q.data ?? [];
  const index = useMemo(() => indexNodes(tree), [tree]);
  const flat = useMemo(() => flattenNodes(tree), [tree]);

  // 최초 로드 시 전체 펼침 + 첫 부서 선택.
  useEffect(() => {
    if (!q.data || bootRef.current) return;
    bootRef.current = true;
    setExpanded(new Set(collectIds(q.data)));
    if (q.data.length > 0) setSelectedId(q.data[0].id);
  }, [q.data]);

  // 검색 디바운스 250ms.
  useEffect(() => {
    const id = window.setTimeout(() => setDebouncedQ(query.trim()), 250);
    return () => window.clearTimeout(id);
  }, [query]);

  const searching = debouncedQ.length > 0;
  const matches = useMemo(() => (searching ? computeMatches(tree, debouncedQ) : null), [searching, tree, debouncedQ]);
  useEffect(() => {
    if (!matches) return;
    setExpanded((prev) => {
      const next = new Set(prev);
      matches.ancestors.forEach((a) => next.add(a));
      matches.matched.forEach((d) => next.add(d));
      return next;
    });
  }, [matches]);

  const rows = useMemo(() => flattenVisible(tree, expanded), [tree, expanded]);
  const selectedNode = selectedId ? index.get(selectedId) ?? null : null;
  const nextSort = useMemo(() => flat.reduce((m, d) => Math.max(m, d.sortOrder ?? 0), 0) + 1, [flat]);

  const toggle = (id: string) => setExpanded((prev) => {
    const next = new Set(prev);
    if (next.has(id)) next.delete(id); else next.add(id);
    return next;
  });

  const saveM = useMutation({
    mutationFn: (body: DeptSaveBody) => deptApi.save(body),
    onSuccess: () => { show(t('dept.saved')); setModal(null); qc.invalidateQueries({ queryKey: ['admin-depts'] }); },
    onError: (e) => show(errMessage(e)),
  });
  const delM = useMutation({
    mutationFn: (id: string) => deptApi.remove(id),
    onSuccess: (_r, id) => {
      show(t('dept.deleted'));
      if (selectedId === id) setSelectedId(index.get(id)?.parentId ?? null);
      qc.invalidateQueries({ queryKey: ['admin-depts'] });
    },
    onError: (e) => show(errMessage(e)),
  });

  const onDelete = (node: DeptNode) => {
    if ((node.children?.length ?? 0) > 0) { show(t('dept.errHasChildren')); return; }
    if (window.confirm(t('dept.deleteConfirm', { name: node.deptName }))) delM.mutate(node.id);
  };

  return (
    <div className="kx-page kx-adm">
      <header className="kx-adm__head">
        <div>
          <h1 className="kx-adm__title">{t('dept.title')}</h1>
          <p className="kx-adm__subtitle">{t('dept.subtitle')}</p>
        </div>
        <div className="kx-dept-headActions">
          <Button variant="ghost" onClick={() => setExpanded(new Set(collectIds(tree)))}>{t('dept.expandAll')}</Button>
          <Button variant="ghost" onClick={() => setExpanded(new Set())}>{t('dept.collapseAll')}</Button>
          <Button leadingIcon={<IconPlus size={16} />} onClick={() => setModal({ mode: 'create', parentId: null })}>
            {t('dept.addRoot')}
          </Button>
        </div>
      </header>

      {q.isLoading && <Skeleton height={360} radius={12} />}
      {q.isError && !q.isLoading && <ErrorState message={t('dept.loadError')} onRetry={() => q.refetch()} />}

      {!q.isLoading && !q.isError && (
        <>
          <div className="kx-dept-toolbar">
            <span className="kx-dept-search">
              <IconSearch size={16} className="kx-dept-search__icon" />
              <input
                className="kx-dept-search__input"
                type="search"
                value={query}
                placeholder={t('dept.searchPlaceholder')}
                onChange={(e) => setQuery(e.target.value)}
                aria-label={t('dept.searchPlaceholder')}
              />
            </span>
          </div>

          {flat.length === 0 ? (
            <section className="kx-card">
              <EmptyState title={t('dept.emptyTitle')} description={t('dept.emptyDesc')} />
            </section>
          ) : (
            <div className="kx-dept-body">
              {/* 좌 — 부서 트리 */}
              <aside className="kx-dept-pane kx-dept-tree-pane">
                <div className="kx-dept-tree" role="tree" aria-label={t('dept.treeAria')}>
                  {rows.length === 0 ? (
                    <p className="kx-dept-tree__empty">{t('dept.searchNoResult')}</p>
                  ) : rows.map(({ node, depth, hasChildren }) => {
                    const selected = node.id === selectedId;
                    const open = expanded.has(node.id);
                    return (
                      <div
                        key={node.id}
                        role="treeitem"
                        aria-level={depth + 1}
                        aria-expanded={hasChildren ? open : undefined}
                        aria-selected={selected}
                        tabIndex={selected ? 0 : -1}
                        className={`kx-dept-tree__row${selected ? ' is-selected' : ''}`}
                        style={{ paddingLeft: depth * 18 + 8 }}
                        onClick={() => { setSelectedId(node.id); setQuery(''); }}
                      >
                        {hasChildren ? (
                          <button
                            type="button"
                            className="kx-dept-tree__toggle"
                            aria-label={open ? t('dept.collapse') : t('dept.expand')}
                            tabIndex={-1}
                            onClick={(e) => { e.stopPropagation(); toggle(node.id); }}
                          >
                            {open ? <IconChevronDown size={16} /> : <IconChevronRight size={16} />}
                          </button>
                        ) : (
                          <span className="kx-dept-tree__toggle kx-dept-tree__toggle--empty" aria-hidden="true" />
                        )}
                        <IconUsers size={16} className="kx-dept-tree__icon" />
                        <span className="kx-dept-tree__name"><Highlight text={node.deptName} term={debouncedQ} /></span>
                        <span className="kx-dept-tree__sort">#{node.sortOrder ?? 0}</span>
                        {node.useYn === 'N' && <span className="kx-dept-tree__off">{t('sysadmin.noUse')}</span>}
                      </div>
                    );
                  })}
                </div>
              </aside>

              {/* 우 — 선택 부서 상세/편집 */}
              <section className="kx-dept-pane kx-dept-detail-pane" aria-live="polite">
                {!selectedNode ? (
                  <div className="kx-dept-detail__empty">
                    <IconUsers size={40} />
                    <p>{t('dept.selectHint')}</p>
                  </div>
                ) : (
                  <>
                    <div className="kx-dept-detail__head">
                      <div className="kx-dept-detail__headMain">
                        <h2 className="kx-dept-detail__title">{selectedNode.deptName}</h2>
                        <p className="kx-dept-detail__path">{deptPath(selectedNode.id, index)}</p>
                      </div>
                      <div className="kx-dept-detail__actions">
                        <Button variant="ghost" leadingIcon={<IconPlus size={14} />}
                                onClick={() => setModal({ mode: 'create', parentId: selectedNode.id })}>
                          {t('dept.addChild')}
                        </Button>
                        <Button variant="ghost" onClick={() => setModal({ mode: 'edit', node: selectedNode })}>
                          {t('sysadmin.edit')}
                        </Button>
                        <Button variant="ghost" className="kx-adm-del" disabled={delM.isPending}
                                onClick={() => onDelete(selectedNode)}>
                          {t('sysadmin.delete')}
                        </Button>
                      </div>
                    </div>
                    <dl className="kx-dept-detail__list">
                      <div className="kx-dept-detail__item">
                        <dt>{t('dept.name')}</dt><dd>{selectedNode.deptName}</dd>
                      </div>
                      <div className="kx-dept-detail__item">
                        <dt>{t('dept.colId')}</dt><dd><code className="kx-dept-code">{selectedNode.id}</code></dd>
                      </div>
                      <div className="kx-dept-detail__item">
                        <dt>{t('dept.colParent')}</dt>
                        <dd>{selectedNode.parentId
                          ? `${index.get(selectedNode.parentId)?.deptName ?? selectedNode.parentId}`
                          : t('dept.topLevel')}</dd>
                      </div>
                      <div className="kx-dept-detail__item">
                        <dt>{t('sysadmin.sortOrder')}</dt><dd>#{selectedNode.sortOrder ?? 0}</dd>
                      </div>
                      <div className="kx-dept-detail__item">
                        <dt>{t('sysadmin.useYn')}</dt>
                        <dd>{selectedNode.useYn === 'N' ? t('sysadmin.noUse') : t('sysadmin.use')}</dd>
                      </div>
                      <div className="kx-dept-detail__item">
                        <dt>{t('dept.colChildCount')}</dt>
                        <dd>{t('dept.childCountValue', { n: selectedNode.children?.length ?? 0 })}</dd>
                      </div>
                    </dl>
                  </>
                )}
              </section>
            </div>
          )}
        </>
      )}

      {modal && (
        <DeptModal
          state={modal}
          allDepts={flat}
          nextSort={nextSort}
          saving={saveM.isPending}
          onClose={() => setModal(null)}
          onSubmit={(b) => saveM.mutate(b)}
        />
      )}
      {toast}
    </div>
  );
}

// ── 추가/수정 모달(상위 부서 선택 포함) ──
function DeptModal({ state, allDepts, nextSort, saving, onClose, onSubmit }: {
  state: NonNullable<ModalState>;
  allDepts: DeptNode[];
  nextSort: number;
  saving: boolean;
  onClose: () => void;
  onSubmit: (b: DeptSaveBody) => void;
}) {
  const { t } = useTranslation();
  const isEdit = state.mode === 'edit';
  const node = isEdit ? state.node : null;
  const [deptName, setName] = useState(node?.deptName ?? '');
  const [parentId, setParentId] = useState<string | null>(isEdit ? node!.parentId : state.parentId);
  const [sortOrder, setSort] = useState(node?.sortOrder ?? nextSort);
  const [useYn, setUseYn] = useState(node?.useYn ?? 'Y');
  const [error, setError] = useState<string | null>(null);

  // 상위 후보: 수정 시 자기 자신 + 자손 배제(순환 방지).
  const parentCandidates = useMemo(() => {
    if (!isEdit) return allDepts;
    const excluded = descendantsOf(node!);
    excluded.add(node!.id);
    return allDepts.filter((d) => !excluded.has(d.id));
  }, [isEdit, allDepts, node]);

  function submit(e: FormEvent) {
    e.preventDefault();
    if (!deptName.trim()) { setError(t('dept.errRequired')); return; }
    onSubmit({ id: node?.id ?? null, deptName: deptName.trim(), parentId, sortOrder, useYn });
  }

  return (
    <div className="kx-adm-modal" role="dialog" aria-modal="true">
      <div className="kx-adm-modal__backdrop" onClick={onClose} />
      <form className="kx-adm-modal__panel" onSubmit={submit}>
        <h2 className="kx-adm-modal__title">{isEdit ? t('dept.edit') : (state.parentId ? t('dept.addChild') : t('dept.add'))}</h2>
        <div className="kx-adm-modal__grid">
          <label className="kx-adm-field kx-adm-field--full">
            <span className="kx-adm-field__label">{t('dept.colParent')}</span>
            <select className="kx-adm-field__select" value={parentId ?? ''} onChange={(e) => setParentId(e.target.value || null)}>
              <option value="">{t('dept.topLevel')}</option>
              {parentCandidates.map((d) => (
                <option key={d.id} value={d.id}>{d.deptName}</option>
              ))}
            </select>
          </label>
          <label className="kx-adm-field kx-adm-field--full">
            <span className="kx-adm-field__label">{t('dept.name')}</span>
            <input className="kx-adm-field__input" value={deptName} onChange={(e) => setName(e.target.value)} />
          </label>
          <label className="kx-adm-field">
            <span className="kx-adm-field__label">{t('sysadmin.sortOrder')}</span>
            <input className="kx-adm-field__input" type="number" value={sortOrder}
                   onChange={(e) => setSort(Number(e.target.value))} />
          </label>
          <label className="kx-adm-field">
            <span className="kx-adm-field__label">{t('sysadmin.useYn')}</span>
            <select className="kx-adm-field__select" value={useYn} onChange={(e) => setUseYn(e.target.value)}>
              <option value="Y">{t('sysadmin.use')}</option>
              <option value="N">{t('sysadmin.noUse')}</option>
            </select>
          </label>
        </div>
        {error && <p className="kx-adm-modal__error" role="alert">{error}</p>}
        <div className="kx-adm-modal__actions">
          <Button variant="ghost" type="button" onClick={onClose}>{t('sysadmin.cancel')}</Button>
          <Button type="submit" disabled={saving}>{saving ? t('sysadmin.saving') : t('sysadmin.save')}</Button>
        </div>
      </form>
    </div>
  );
}
