import { Fragment, useEffect, useState, useCallback, useMemo } from 'react';
import {
  Loader2, Save, Plus, Trash2, RefreshCw, ChevronRight, ChevronDown,
  ExternalLink, Layers,
} from 'lucide-react';
import { Card, CardContent } from '@shared/components/ui/card';
import { Badge } from '@shared/components/ui/badge';
import { Button } from '@shared/components/ui/button';
import { Input } from '@shared/components/ui/input';
import {
  fetchRoles, fetchPermTree, createRole, deleteRole, updateRolePermissions,
  type RoleWithPermissions, type PermTreeNode, type PermEntry,
} from '@/services/adminApi';
import {
  resolveSingleRoleEffective, OPERATIONS,
  type Operation, type TreeNode, type PermRow,
} from '@/lib/permission/permResolver';
import { useAuth } from '@/app/auth/AuthContext';
import { useSettingsStore } from '@stores/settingsStore';
import { getRoleBadgeStyle, ROLE_DEFAULT_PALETTE } from '@shared/constants/userRoles';
import { ColorPicker } from '@shared/components/common/ColorPicker';
import { updateRole as apiUpdateRole } from '@/services/adminApi';
import { useTranslation } from 'react-i18next';

/**
 * 트리 기반 권한 관리 패널 (wing 패턴).
 *
 * - 좌측: 역할 목록
 * - 우측: 권한 트리 + R/C/U/D/E 체크박스 매트릭스
 *
 * 셀 상태 (4가지):
 *   • explicit-granted (✓ 파랑) - 명시적 Y
 *   • explicit-denied (— 빨강)  - 명시적 N
 *   • inherited-granted (✓ 연파랑) - 부모로부터 상속
 *   • forced-denied (회색) - 부모 READcandid가 N → 강제 거부
 *
 * 클릭 사이클: explicit-granted → explicit-denied → 미지정(상속) → ...
 *
 * 권한:
 *   - admin:role-management (READ): 역할 목록 조회
 *   - admin:role-management (CREATE/DELETE): 역할 생성/삭제
 *   - admin:permission-management (UPDATE): 권한 매트릭스 갱신
 */

type DraftPerms = Map<string, 'Y' | 'N' | null>;  // null = 명시 권한 제거

function makeKey(rsrcCd: string, operCd: string) { return `${rsrcCd}::${operCd}`; }

export function PermissionsPanel() {
  const { t: tc } = useTranslation('common');
  const { hasPermission } = useAuth();
  const canCreateRole = hasPermission('admin:role-management', 'CREATE');
  const canDeleteRole = hasPermission('admin:role-management', 'DELETE');
  const canUpdatePerm = hasPermission('admin:permission-management', 'UPDATE');

  const [roles, setRoles] = useState<RoleWithPermissions[]>([]);
  const [tree, setTree] = useState<PermTreeNode[]>([]);
  const [selectedRoleSn, setSelectedRoleSn] = useState<number | null>(null);
  const [loading, setLoading] = useState(false);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState('');

  const [draftPerms, setDraftPerms] = useState<DraftPerms>(new Map());
  const [expanded, setExpanded] = useState<Set<string>>(new Set());

  const [showCreate, setShowCreate] = useState(false);
  const [newRoleCd, setNewRoleCd] = useState('');
  const [newRoleNm, setNewRoleNm] = useState('');
  const [newRoleColor, setNewRoleColor] = useState<string>(ROLE_DEFAULT_PALETTE[0]);
  const [editingColor, setEditingColor] = useState<string | null>(null);

  const load = useCallback(async () => {
    setLoading(true); setError('');
    try {
      const [r, t] = await Promise.all([fetchRoles(), fetchPermTree()]);
      setRoles(r);
      setTree(t);
      if (r.length > 0 && selectedRoleSn === null) {
        setSelectedRoleSn(r[0].roleSn);
      }
      // Level 0 노드 자동 펼침
      setExpanded(new Set(t.filter((n) => n.rsrcLevel === 0).map((n) => n.rsrcCd)));
    } catch (e: unknown) {
      setError(e instanceof Error ? e.message : 'unknown');
    } finally {
      setLoading(false);
    }
  }, [selectedRoleSn]);

  // eslint-disable-next-line react-hooks/exhaustive-deps
  useEffect(() => { load(); /* 최초 1회만 로드 */ }, []);

  // 역할 선택 시 draft 초기화
  const selectedRole = useMemo(
    () => roles.find((r) => r.roleSn === selectedRoleSn) ?? null,
    [roles, selectedRoleSn],
  );

  useEffect(() => {
    if (!selectedRole) return;
    const m: DraftPerms = new Map();
    for (const p of selectedRole.permissions) {
      m.set(makeKey(p.rsrcCd, p.operCd), p.grantYn as 'Y' | 'N');
    }
    setDraftPerms(m);
  }, [selectedRole]);

  // 트리 → 트리 인덱싱 (parent → children), nav_sort 기반 정렬 (메뉴 순서 일치)
  const childrenMap = useMemo(() => {
    const m = new Map<string | null, PermTreeNode[]>();
    for (const n of tree) {
      if (n.useYn !== 'Y') continue;
      const arr = m.get(n.parentCd) ?? [];
      arr.push(n);
      m.set(n.parentCd, arr);
    }
    // nav_sort > 0 우선 (메뉴 표시 항목), 그 다음 sort_ord — 좌측 메뉴 순서와 일치
    for (const [, arr] of m.entries()) {
      arr.sort((a, b) => {
        const aSort = (a.navSort > 0) ? a.navSort : 10000 + a.sortOrd;
        const bSort = (b.navSort > 0) ? b.navSort : 10000 + b.sortOrd;
        return aSort - bSort;
      });
    }
    return m;
  }, [tree]);

  // draft 기반 effective 권한 해석 (PermResolver TS 미러)
  const effective = useMemo(() => {
    const treeNodes: TreeNode[] = tree.map((n) => ({
      rsrcCd: n.rsrcCd, parentCd: n.parentCd, rsrcNm: n.rsrcNm,
      rsrcLevel: n.rsrcLevel, sortOrd: n.sortOrd, useYn: n.useYn,
    }));
    const perms: PermRow[] = [];
    draftPerms.forEach((v, k) => {
      if (v === 'Y' || v === 'N') {
        const [rsrcCd, operCd] = k.split('::');
        perms.push({ rsrcCd, operCd, grantYn: v });
      }
    });
    return resolveSingleRoleEffective(treeNodes, perms);
  }, [tree, draftPerms]);

  const cellState = useCallback((rsrcCd: string, operCd: Operation, parentCd: string | null) => {
    const key = makeKey(rsrcCd, operCd);
    const explicit = draftPerms.get(key);

    // 1) 부모 노드의 effective READ가 거부되면 자식의 모든 작업 강제 거부
    let parentReadDenied = false;
    if (parentCd) {
      const parentEff = effective.get(parentCd);
      parentReadDenied = !parentEff || !parentEff.has('READ');
    }
    if (parentReadDenied) return 'forced-denied';

    // 2) 같은 노드의 READ가 effective로 거부되면 C/U/D/E도 강제 거부
    //    (READ가 안 되면 그 페이지/리소스 자체에 접근 못 하므로 다른 작업 권한도 의미 없음)
    if (operCd !== 'READ') {
      const ownEff = effective.get(rsrcCd);
      const ownReadGranted = ownEff?.has('READ') ?? false;
      if (!ownReadGranted) return 'forced-denied';
    }

    if (explicit === 'Y') return 'explicit-granted';
    if (explicit === 'N') return 'explicit-denied';

    // 상속 체크
    const eff = effective.get(rsrcCd);
    if (eff?.has(operCd)) return 'inherited-granted';
    return 'inherited-denied';
  }, [draftPerms, effective]);

  const isDirty = useMemo(() => {
    if (!selectedRole) return false;
    const original = new Map<string, 'Y' | 'N'>();
    for (const p of selectedRole.permissions) {
      original.set(makeKey(p.rsrcCd, p.operCd), p.grantYn as 'Y' | 'N');
    }
    if (original.size !== Array.from(draftPerms.values()).filter((v) => v !== null).length) {
      return true;
    }
    for (const [k, v] of draftPerms) {
      if (v === null) {
        if (original.has(k)) return true;
      } else if (original.get(k) !== v) {
        return true;
      }
    }
    return false;
  }, [selectedRole, draftPerms]);

  // 셀 클릭: explicit Y → explicit N → 미지정(상속) → ...
  const handleCellClick = (rsrcCd: string, operCd: Operation) => {
    if (!canUpdatePerm) return;
    const key = makeKey(rsrcCd, operCd);
    setDraftPerms((prev) => {
      const next = new Map(prev);
      const cur = next.get(key);
      if (cur === 'Y') next.set(key, 'N');
      else if (cur === 'N') next.set(key, null);  // 명시 권한 제거
      else next.set(key, 'Y');
      return next;
    });
  };

  const handleSave = async () => {
    if (!selectedRole || !canUpdatePerm) return;
    setSaving(true); setError('');
    try {
      const original = new Map<string, 'Y' | 'N'>();
      for (const p of selectedRole.permissions) {
        original.set(makeKey(p.rsrcCd, p.operCd), p.grantYn as 'Y' | 'N');
      }

      // 변경된 셀만 수집
      const changes: PermEntry[] = [];
      const allKeys = new Set([...original.keys(), ...draftPerms.keys()]);
      for (const k of allKeys) {
        const [rsrcCd, operCd] = k.split('::');
        const oldVal = original.get(k);
        const newVal = draftPerms.get(k);
        if (newVal === null && oldVal !== undefined) {
          changes.push({ rsrcCd, operCd, grantYn: null });
        } else if ((newVal === 'Y' || newVal === 'N') && newVal !== oldVal) {
          changes.push({ rsrcCd, operCd, grantYn: newVal });
        }
      }

      if (changes.length === 0) {
        setSaving(false);
        return;
      }

      await updateRolePermissions(selectedRole.roleSn, changes);
      await load();  // 새로 가져와서 동기화
      alert(`${tc('success.permissionUpdated')} (${changes.length})`);
    } catch (e: unknown) {
      setError(e instanceof Error ? e.message : 'unknown');
    } finally {
      setSaving(false);
    }
  };

  const handleCreateRole = async () => {
    if (!newRoleCd || !newRoleNm) return;
    try {
      await createRole({ roleCd: newRoleCd, roleNm: newRoleNm, colorHex: newRoleColor });
      setShowCreate(false);
      setNewRoleCd(''); setNewRoleNm('');
      setNewRoleColor(ROLE_DEFAULT_PALETTE[0]);
      await load();
    } catch (e: unknown) {
      alert(tc('error.createFailed', { msg: e instanceof Error ? e.message : 'unknown' }));
    }
  };

  const handleUpdateColor = async (roleSn: number, hex: string) => {
    try {
      await apiUpdateRole(roleSn, { colorHex: hex });
      await load();
      setEditingColor(null);
    } catch (e: unknown) {
      alert(tc('error.updateFailed', { msg: e instanceof Error ? e.message : 'unknown' }));
    }
  };

  const handleDeleteRole = async () => {
    if (!selectedRole) return;
    if (selectedRole.builtinYn === 'Y') {
      alert(tc('message.builtinRoleCannotDelete'));
      return;
    }
    if (!confirm(`"${selectedRole.roleNm}" ${tc('dialog.deleteRole')}`)) return;
    try {
      await deleteRole(selectedRole.roleSn);
      setSelectedRoleSn(null);
      await load();
    } catch (e: unknown) {
      alert(tc('error.deleteFailed', { msg: e instanceof Error ? e.message : 'unknown' }));
    }
  };

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

  const renderTreeRow = (node: PermTreeNode, depth: number): React.ReactNode => {
    const children = childrenMap.get(node.rsrcCd) ?? [];
    const hasChildren = children.length > 0;
    const isExpanded = expanded.has(node.rsrcCd);

    // DB labels JSONB에서 현재 언어 라벨 사용, 없으면 rsrcNm 폴백
    const lang = useSettingsStore.getState().language;
    const displayName = node.labels?.[lang] || node.labels?.ko || node.rsrcNm;

    return (
      <Fragment key={node.rsrcCd}>
        <tr className="border-t border-border hover:bg-surface-overlay/30">
          <td className="py-1.5 pl-2" style={{ paddingLeft: 8 + depth * 20 }}>
            <div className="flex items-center gap-1">
              {hasChildren ? (
                <button type="button" onClick={() => toggleExpand(node.rsrcCd)}
                        className="p-0.5 text-hint hover:text-heading">
                  {isExpanded ? <ChevronDown className="w-3 h-3" /> : <ChevronRight className="w-3 h-3" />}
                </button>
              ) : <span className="w-4" />}
              {/* 페이지/패널 구분 아이콘 */}
              {node.urlPath
                ? <span title="별도 페이지"><ExternalLink className="w-3 h-3 text-cyan-500/60 shrink-0" /></span>
                : depth > 0 ? <span title="페이지 내 패널"><Layers className="w-3 h-3 text-amber-500/50 shrink-0" /></span> : null
              }
              <span className="text-[11px] text-heading font-medium">{displayName}</span>
              <span className="text-[9px] text-hint font-mono">({node.rsrcCd})</span>
              {node.urlPath && <span className="text-[8px] text-cyan-500/70 font-mono">{node.urlPath}</span>}
            </div>
          </td>
          {OPERATIONS.map((op) => {
            const state = cellState(node.rsrcCd, op as Operation, node.parentCd);
            const cls =
              state === 'explicit-granted' ? 'bg-blue-500 text-white border-blue-400 font-bold'
              : state === 'inherited-granted' ? 'bg-blue-500/30 text-blue-300 border-blue-500/40'
              : state === 'explicit-denied' ? 'bg-red-500/40 text-red-300 border-red-500/50 font-bold'
              : state === 'forced-denied' ? 'bg-gray-700/40 text-gray-600 border-gray-700/40 cursor-not-allowed'
              : 'bg-surface-overlay text-hint border-border';
            const icon =
              state === 'explicit-granted' || state === 'inherited-granted' ? '✓'
              : state === 'explicit-denied' ? '—'
              : state === 'forced-denied' ? '×'
              : '·';
            return (
              <td key={op} className="text-center py-1.5">
                <button
                  type="button"
                  disabled={!canUpdatePerm || state === 'forced-denied'}
                  onClick={() => handleCellClick(node.rsrcCd, op as Operation)}
                  className={`w-7 h-6 rounded border text-[11px] transition-colors ${cls} ${canUpdatePerm && state !== 'forced-denied' ? 'hover:opacity-80 cursor-pointer' : ''}`}
                  title={`${op} - ${state}`}
                >
                  {icon}
                </button>
              </td>
            );
          })}
        </tr>
        {isExpanded && children.map((c) => renderTreeRow(c, depth + 1))}
      </Fragment>
    );
  };

  return (
    <div className="space-y-3">
      <div className="flex items-center justify-between">
        <div>
          <h2 className="text-base font-bold text-heading">권한 관리 (트리 RBAC)</h2>
          <p className="text-[10px] text-hint mt-0.5">
            좌측 역할 선택 → 우측 트리 매트릭스에서 셀 클릭 (Y → N → 상속) → 저장
          </p>
        </div>
        <div className="flex items-center gap-1">
          <Button
            variant="ghost"
            size="sm"
            onClick={load}
            aria-label={tc('aria.refresh')}
            icon={<RefreshCw className="w-3.5 h-3.5" />}
          />
        </div>
      </div>

      {error && <div className="text-xs text-heading">{tc('error.errorPrefix', { msg: error })}</div>}

      {loading && <div className="flex items-center justify-center py-12 text-muted-foreground"><Loader2 className="w-5 h-5 animate-spin" /></div>}

      {!loading && (
        <div className="grid grid-cols-12 gap-3">
          {/* 좌측: 역할 목록 */}
          <Card className="col-span-3">
            <CardContent className="p-3">
              <div className="flex items-center justify-between mb-2">
                <div className="text-xs text-label font-bold">역할</div>
                <div className="flex items-center gap-1">
                  {canCreateRole && (
                    <Button
                      variant="ghost"
                      size="sm"
                      onClick={() => setShowCreate(!showCreate)}
                      aria-label="신규 역할"
                      title="신규 역할"
                      icon={<Plus className="w-3.5 h-3.5" />}
                    />
                  )}
                  {canDeleteRole && selectedRole && selectedRole.builtinYn !== 'Y' && (
                    <Button
                      variant="ghost"
                      size="sm"
                      onClick={handleDeleteRole}
                      aria-label="역할 삭제"
                      title="역할 삭제"
                      icon={<Trash2 className="w-3.5 h-3.5" />}
                    />
                  )}
                </div>
              </div>

              {showCreate && (
                <div className="mb-2 p-2 bg-surface-overlay rounded space-y-1.5">
                  <Input
                    aria-label={tc('aria.roleCode')}
                    size="sm"
                    value={newRoleCd}
                    onChange={(e) => setNewRoleCd(e.target.value.toUpperCase())}
                    placeholder="ROLE_CD (대문자)"
                  />
                  <Input
                    aria-label={tc('aria.roleName')}
                    size="sm"
                    value={newRoleNm}
                    onChange={(e) => setNewRoleNm(e.target.value)}
                    placeholder={tc('aria.roleName')}
                  />
                  <ColorPicker label="배지 색상" value={newRoleColor} onChange={setNewRoleColor} />
                  <div className="flex gap-1 pt-1">
                    <Button variant="primary" size="sm" onClick={handleCreateRole} disabled={!newRoleCd || !newRoleNm} className="flex-1">
                      생성
                    </Button>
                    <Button variant="secondary" size="sm" onClick={() => setShowCreate(false)} className="flex-1">
                      취소
                    </Button>
                  </div>
                </div>
              )}

              <div className="space-y-1">
                {roles.map((r) => {
                  const selected = r.roleSn === selectedRoleSn;
                  const isEditingColor = editingColor === String(r.roleSn);
                  return (
                    <div
                      key={r.roleSn}
                      className={`px-2 py-1.5 rounded border transition-colors ${
                        selected
                          ? 'bg-blue-600/20 border-blue-500/40 text-heading'
                          : 'bg-surface-overlay border-border text-muted-foreground hover:text-heading hover:bg-surface-overlay/80'
                      }`}
                    >
                      <div className="flex items-center justify-between">
                        <button
                          type="button"
                          onClick={() => setSelectedRoleSn(r.roleSn)}
                          className="flex items-center gap-1.5 cursor-pointer"
                          title="역할 선택"
                        >
                          <Badge size="sm" style={getRoleBadgeStyle(r.roleCd)}>
                            {r.roleCd}
                          </Badge>
                        </button>
                        <div className="flex items-center gap-1">
                          {r.builtinYn === 'Y' && <span className="text-[8px] text-hint">BUILT-IN</span>}
                          {canUpdatePerm && (
                            <button
                              type="button"
                              onClick={() => setEditingColor(isEditingColor ? null : String(r.roleSn))}
                              className="text-[8px] text-hint hover:text-label"
                              title="색상 변경"
                            >
                              ●
                            </button>
                          )}
                        </div>
                      </div>
                      <button type="button" onClick={() => setSelectedRoleSn(r.roleSn)} className="w-full text-left">
                        <div className="text-[10px] mt-0.5">{r.roleNm}</div>
                        <div className="text-[9px] text-hint mt-0.5">권한 {r.permissions.length}건</div>
                      </button>
                      {isEditingColor && (
                        <div className="mt-2 p-2 bg-background rounded border border-border">
                          <ColorPicker
                            label="배지 색상"
                            value={r.colorHex}
                            onChange={(hex) => handleUpdateColor(r.roleSn, hex)}
                          />
                        </div>
                      )}
                    </div>
                  );
                })}
              </div>
            </CardContent>
          </Card>

          {/* 우측: 권한 매트릭스 */}
          <Card className="col-span-9">
            <CardContent className="p-3">
              <div className="flex items-center justify-between mb-3">
                <div>
                  <div className="text-xs text-label font-bold">
                    {selectedRole ? `${selectedRole.roleNm} (${selectedRole.roleCd})` : '역할 선택'}
                  </div>
                  <div className="text-[10px] text-hint mt-0.5">
                    셀 의미: <span className="text-label">✓ 명시 허용</span> /
                    <span className="text-blue-300/80 ml-1">✓ 상속 허용</span> /
                    <span className="text-heading ml-1">— 명시 거부</span> /
                    <span className="text-gray-500 ml-1">× 강제 거부</span> /
                    <span className="text-hint ml-1">· 미지정</span>
                  </div>
                </div>
                {canUpdatePerm && selectedRole && (
                  <Button
                    variant="primary"
                    size="sm"
                    onClick={handleSave}
                    disabled={!isDirty || saving}
                    icon={saving ? <Loader2 className="w-3.5 h-3.5 animate-spin" /> : <Save className="w-3.5 h-3.5" />}
                  >
                    저장 {isDirty && <span className="text-yellow-300">●</span>}
                  </Button>
                )}
              </div>

              {selectedRole && (
                <div className="overflow-x-auto">
                  <table className="w-full text-xs">
                    <thead className="sticky top-0 bg-surface-overlay">
                      <tr className="border-b border-border">
                        <th className="text-left py-2 pl-2 text-hint font-medium">리소스</th>
                        {OPERATIONS.map((op) => (
                          <th key={op} className="w-16 text-center py-2 text-hint font-medium">{op[0]}</th>
                        ))}
                      </tr>
                    </thead>
                    <tbody>
                      {(childrenMap.get(null) ?? []).map((root) => renderTreeRow(root, 0))}
                    </tbody>
                  </table>
                </div>
              )}
            </CardContent>
          </Card>
        </div>
      )}
    </div>
  );
}
