/*
 * M18 메뉴 관리 (SCR-A4 · §5B). MenuController 실배선.
 * 정본: GET /api/admin/menus → MenuNode[] (flat) · POST /api/admin/menus(upsert) · DELETE /api/admin/menus/{id}.
 * UI: parentId 로 트리를 구성해 깊이 들여쓰기 표로 표시. 추가/수정 모달(상위 메뉴 선택·경로·역할·정렬·사용).
 */
import { useMemo, useState } 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 } from '../../components/ui/icons';
import { errMessage, useToast } from '../work/workShared';
import { menuApi, MENU_PRG_TYPES, type MenuNode, type MenuSaveBody } from './adminCrudApi';
import './admin-modules.css';

interface FlatMenu extends MenuNode {
  depth: number;
}

/** 서버 응답(flat 또는 nested)을 parentId 기준 깊이 정렬 평면 목록으로 정규화. */
function toDepthList(nodes: MenuNode[]): FlatMenu[] {
  // children 이 있으면 우선 평탄화
  const flat: MenuNode[] = [];
  const walk = (arr: MenuNode[]) => {
    for (const n of arr) {
      flat.push(n);
      if (n.children && n.children.length > 0) walk(n.children);
    }
  };
  walk(nodes);
  const byId = new Map(flat.map((n) => [n.id, n]));
  const childrenOf = new Map<string | null, MenuNode[]>();
  for (const n of flat) {
    const pid = n.parentId && byId.has(n.parentId) ? n.parentId : null;
    const arr = childrenOf.get(pid) ?? [];
    arr.push(n);
    childrenOf.set(pid, arr);
  }
  const out: FlatMenu[] = [];
  const emit = (pid: string | null, depth: number) => {
    const arr = (childrenOf.get(pid) ?? []).sort((a, b) => a.sortOrder - b.sortOrder);
    for (const n of arr) {
      out.push({ ...n, depth });
      emit(n.id, depth + 1);
    }
  };
  emit(null, 0);
  return out;
}

export function MenuAdminPage() {
  const { t } = useTranslation();
  const qc = useQueryClient();
  const { show, node: toast } = useToast();

  const q = useQuery({ queryKey: ['admin-menus'], queryFn: menuApi.all, retry: false });
  const [editing, setEditing] = useState<MenuNode | null | undefined>(undefined);

  const flat = useMemo(() => toDepthList(q.data ?? []), [q.data]);

  const saveM = useMutation({
    mutationFn: (body: MenuSaveBody) => menuApi.save(body),
    onSuccess: () => {
      show(t('menu.saved'));
      setEditing(undefined);
      qc.invalidateQueries({ queryKey: ['admin-menus'] });
    },
    onError: (e) => show(errMessage(e)),
  });
  const delM = useMutation({
    mutationFn: (id: string) => menuApi.remove(id),
    onSuccess: () => {
      show(t('menu.deleted'));
      qc.invalidateQueries({ queryKey: ['admin-menus'] });
    },
    onError: (e) => show(errMessage(e)),
  });

  return (
    <div className="kx-page kx-adm">
      <header className="kx-adm__head">
        <div>
          <h1 className="kx-adm__title">{t('menu.title')}</h1>
          <p className="kx-adm__subtitle">{t('menu.subtitle')}</p>
        </div>
        <Button leadingIcon={<IconPlus size={16} />} onClick={() => setEditing(null)}>{t('menu.addMenu')}</Button>
      </header>

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

      {!q.isLoading && !q.isError && (
        <section className="kx-card kx-adm-table-card" aria-label={t('menu.treeAria')}>
          {flat.length === 0 ? (
            <EmptyState title={t('menu.emptyTitle')} description={t('menu.emptyDesc')} />
          ) : (
            <div className="kx-table-scroll">
              <table className="kx-table kx-table--zebra kx-adm-tbl">
                <thead>
                  <tr>
                    <th>{t('menu.colName')}</th>
                    <th>{t('menu.colPath')}</th>
                    <th>{t('menu.colType')}</th>
                    <th>{t('menu.colRole')}</th>
                    <th className="kx-num">{t('menu.colSort')}</th>
                    <th>{t('menu.colUse')}</th>
                    <th style={{ textAlign: 'right' }}>{t('menu.colManage')}</th>
                  </tr>
                </thead>
                <tbody>
                  {flat.map((m) => (
                    <tr key={m.id}>
                      <td>
                        <span className="kx-adm-menu__name">
                          {m.depth > 0 && (
                            <span className="kx-adm-menu__depth" aria-hidden="true">
                              {'  '.repeat(m.depth)}└
                            </span>
                          )}
                          <span className="kx-adm-tbl__strong">{m.menuName}</span>
                        </span>
                      </td>
                      <td className="kx-adm-mono">{m.menuPath ?? '—'}</td>
                      <td className="kx-adm-muted">{m.prgType ?? '—'}</td>
                      <td className="kx-adm-muted">{m.requiredRole ?? t('menu.all')}</td>
                      <td className="kx-num tnum">{m.sortOrder}</td>
                      <td>
                        <span className={`kx-adm-pill kx-adm-pill--${m.useYn === 'N' ? 'neutral' : 'success'}`}>
                          {m.useYn === 'N' ? t('menu.unused') : t('menu.used')}
                        </span>
                      </td>
                      <td>
                        <div className="kx-adm-tbl__actions">
                          <Button variant="ghost" onClick={() => setEditing(m)}>{t('menu.edit')}</Button>
                          <Button
                            variant="ghost"
                            className="kx-adm-del"
                            disabled={delM.isPending}
                            onClick={() => {
                              if (window.confirm(t('menu.deleteConfirm', { name: m.menuName })))
                                delM.mutate(m.id);
                            }}
                          >
                            {t('menu.delete')}
                          </Button>
                        </div>
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          )}
        </section>
      )}

      {editing !== undefined && (
        <MenuModal
          menu={editing}
          options={flat}
          saving={saveM.isPending}
          onClose={() => setEditing(undefined)}
          onSubmit={(body) => saveM.mutate(body)}
        />
      )}
      {toast}
    </div>
  );
}

function MenuModal({
  menu,
  options,
  saving,
  onClose,
  onSubmit,
}: {
  menu: MenuNode | null;
  options: FlatMenu[];
  saving: boolean;
  onClose: () => void;
  onSubmit: (body: MenuSaveBody) => void;
}) {
  const { t } = useTranslation();
  const isNew = menu === null;
  const [id, setId] = useState(menu?.id ?? '');
  const [menuName, setMenuName] = useState(menu?.menuName ?? '');
  const [parentId, setParentId] = useState(menu?.parentId ?? '');
  const [menuPath, setMenuPath] = useState(menu?.menuPath ?? '');
  const [prgType, setPrgType] = useState(menu?.prgType ?? 'SCREEN');
  const [requiredRole, setRequiredRole] = useState(menu?.requiredRole ?? '');
  const [sortOrder, setSortOrder] = useState(String(menu?.sortOrder ?? 0));
  const [useYn, setUseYn] = useState(menu?.useYn ?? 'Y');
  const [error, setError] = useState<string | null>(null);

  // 자기 자신은 상위 후보에서 제외(순환 방지 — 하위까지 엄격 제외는 서버 검증 위임)
  const parentOptions = options.filter((o) => o.id !== menu?.id);

  function submit(e: React.FormEvent) {
    e.preventDefault();
    setError(null);
    if (!menuName.trim()) {
      setError(t('menu.errName'));
      return;
    }
    if (isNew && !id.trim()) {
      setError(t('menu.errId'));
      return;
    }
    if (isNew && options.some((o) => o.id === id.trim())) {
      setError(t('menu.errDupId'));
      return;
    }
    onSubmit({
      id: isNew ? id.trim() : (menu?.id ?? null),
      parentId: parentId || null,
      menuName: menuName.trim(),
      menuPath: menuPath.trim() || null,
      prgType,
      requiredRole: requiredRole.trim() || null,
      sortOrder: Number(sortOrder) || 0,
      useYn,
    });
  }

  return (
    <div className="kx-adm-modal" role="dialog" aria-modal="true" aria-labelledby="menu-modal-title">
      <div className="kx-adm-modal__backdrop" onClick={onClose} />
      <form className="kx-adm-modal__panel" onSubmit={submit}>
        <h2 id="menu-modal-title" className="kx-adm-modal__title">{isNew ? t('menu.addMenu') : t('menu.editMenu')}</h2>
        <div className="kx-adm-modal__grid">
          {isNew && (
            <label className="kx-adm-field kx-adm-field--full">
              <span className="kx-adm-field__label">{t('menu.menuId')}</span>
              <input className="kx-adm-field__input kx-adm-mono" value={id} placeholder={t('menu.menuIdPh')} onChange={(e) => setId(e.target.value)} />
            </label>
          )}
          <label className="kx-adm-field">
            <span className="kx-adm-field__label">{t('menu.menuName')}</span>
            <input className="kx-adm-field__input" value={menuName} onChange={(e) => setMenuName(e.target.value)} />
          </label>
          <label className="kx-adm-field">
            <span className="kx-adm-field__label">{t('menu.parent')}</span>
            <select className="kx-adm-field__select" value={parentId} onChange={(e) => setParentId(e.target.value)}>
              <option value="">{t('menu.topLevel')}</option>
              {parentOptions.map((o) => (
                <option key={o.id} value={o.id}>{'—'.repeat(o.depth)} {o.menuName}</option>
              ))}
            </select>
          </label>
          <label className="kx-adm-field kx-adm-field--full">
            <span className="kx-adm-field__label">{t('menu.path')}</span>
            <input className="kx-adm-field__input kx-adm-mono" value={menuPath} placeholder={t('menu.pathPh')} onChange={(e) => setMenuPath(e.target.value)} />
          </label>
          <label className="kx-adm-field">
            <span className="kx-adm-field__label">{t('menu.type')}</span>
            <select className="kx-adm-field__select" value={prgType ?? 'SCREEN'} onChange={(e) => setPrgType(e.target.value)}>
              {MENU_PRG_TYPES.map((pt) => (
                <option key={pt} value={pt}>{pt}</option>
              ))}
            </select>
          </label>
          <label className="kx-adm-field">
            <span className="kx-adm-field__label">{t('menu.accessRole')}</span>
            <input className="kx-adm-field__input kx-adm-mono" value={requiredRole} placeholder={t('menu.accessRolePh')} onChange={(e) => setRequiredRole(e.target.value)} />
          </label>
          <label className="kx-adm-field">
            <span className="kx-adm-field__label">{t('menu.sort')}</span>
            <input className="kx-adm-field__input" type="number" value={sortOrder} onChange={(e) => setSortOrder(e.target.value)} />
          </label>
          <label className="kx-adm-field">
            <span className="kx-adm-field__label">{t('menu.useYn')}</span>
            <select className="kx-adm-field__select" value={useYn} onChange={(e) => setUseYn(e.target.value)}>
              <option value="Y">{t('menu.used')}</option>
              <option value="N">{t('menu.unused')}</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('menu.cancel')}</Button>
          <Button type="submit" disabled={saving}>{saving ? t('menu.saving') : t('menu.save')}</Button>
        </div>
      </form>
    </div>
  );
}
