/*
 * 공용 Offcanvas(슬라이드 드로어) — 우측(기본)/좌측에서 슬라이드 인·아웃하는 상세 패널.
 * 레퍼런스: Nifty offcanvas 패턴(docs/DESIGN_SYSTEM_NIFTY.md §2.6).
 *
 * 특징
 *  - 백드롭(반투명·클릭 닫기) · 헤더(제목·닫기 X) · 바디(scroll)
 *  - Esc 닫기 · 포커스 트랩(Tab 순환) · 열림 시 패널 포커스, 닫힘 시 이전 포커스 복원
 *  - 슬라이드 트랜지션 — CSS에서 `prefers-reduced-motion` 존중(즉시 표시)
 *  - role=dialog · aria-modal · aria-label(title)
 *  - kx 토큰 기반, 다크 대응(색은 CSS 변수 상속)
 *
 * 사용:
 *   <Offcanvas open={open} onClose={() => setOpen(false)} title="배정 상세">
 *     …상세 내용…
 *   </Offcanvas>
 */
import { useCallback, useEffect, useRef, useState, type ReactNode } from 'react';
import { IconClose } from './icons';
import './offcanvas.css';

export interface OffcanvasProps {
  /** 열림 여부. */
  open: boolean;
  /** 닫기 콜백(백드롭 클릭·Esc·닫기 버튼). */
  onClose: () => void;
  /** 헤더 제목(aria-label에도 사용). */
  title: string;
  /** 슬라이드 방향. 기본 'right'. */
  side?: 'right' | 'left';
  /** 패널 너비(CSS 길이). 기본 420px. */
  width?: number | string;
  /** 바디 내용. */
  children: ReactNode;
  /** 헤더 하단(제목 옆) 부가 액션 슬롯(선택). */
  headerExtra?: ReactNode;
  /** 하단 고정 푸터(선택). */
  footer?: ReactNode;
}

/** 슬라이드 아웃 유지 시간(ms) — CSS transition 과 정렬. reduced-motion 이어도 안전. */
const EXIT_MS = 240;

export function Offcanvas({
  open,
  onClose,
  title,
  side = 'right',
  width = 420,
  children,
  headerExtra,
  footer,
}: OffcanvasProps) {
  // mounted: DOM 유지 여부(닫힘 트랜지션 동안 true 유지) / visible: is-open 클래스(transform 구동)
  const [mounted, setMounted] = useState(open);
  const [visible, setVisible] = useState(false);
  const panelRef = useRef<HTMLDivElement>(null);
  const prevFocusRef = useRef<HTMLElement | null>(null);

  // 열림/닫힘 → mount·visible 전환
  useEffect(() => {
    if (open) {
      prevFocusRef.current = document.activeElement as HTMLElement | null;
      setMounted(true);
      // 다음 프레임에 is-open 부여 → 슬라이드 인
      const id = requestAnimationFrame(() => setVisible(true));
      return () => cancelAnimationFrame(id);
    }
    setVisible(false);
    const timer = window.setTimeout(() => setMounted(false), EXIT_MS);
    return () => window.clearTimeout(timer);
  }, [open]);

  // 열린 뒤 패널로 포커스 이동 · 닫히면 이전 포커스 복원
  useEffect(() => {
    if (visible) {
      panelRef.current?.focus();
    } else if (prevFocusRef.current) {
      prevFocusRef.current.focus?.();
    }
  }, [visible]);

  // 열려있는 동안 body 스크롤 잠금
  useEffect(() => {
    if (!mounted) return;
    const prev = document.body.style.overflow;
    document.body.style.overflow = 'hidden';
    return () => {
      document.body.style.overflow = prev;
    };
  }, [mounted]);

  // Esc 닫기 + 포커스 트랩
  const onKeyDown = useCallback(
    (e: React.KeyboardEvent<HTMLDivElement>) => {
      if (e.key === 'Escape') {
        e.stopPropagation();
        onClose();
        return;
      }
      if (e.key !== 'Tab') return;
      const panel = panelRef.current;
      if (!panel) return;
      const focusables = panel.querySelectorAll<HTMLElement>(
        'a[href], button:not([disabled]), textarea, input, select, [tabindex]:not([tabindex="-1"])',
      );
      if (focusables.length === 0) {
        e.preventDefault();
        panel.focus();
        return;
      }
      const first = focusables[0];
      const last = focusables[focusables.length - 1];
      const active = document.activeElement;
      if (e.shiftKey) {
        if (active === first || active === panel) {
          e.preventDefault();
          last.focus();
        }
      } else if (active === last) {
        e.preventDefault();
        first.focus();
      }
    },
    [onClose],
  );

  if (!mounted) return null;

  return (
    <div className={`kx-oc${visible ? ' is-open' : ''}`}>
      <div className="kx-oc__backdrop" onClick={onClose} aria-hidden="true" />
      <div
        ref={panelRef}
        className={`kx-oc__panel kx-oc__panel--${side}`}
        style={{ width: typeof width === 'number' ? `${width}px` : width }}
        role="dialog"
        aria-modal="true"
        aria-label={title}
        tabIndex={-1}
        onKeyDown={onKeyDown}
      >
        <header className="kx-oc__head">
          <h2 className="kx-oc__title">{title}</h2>
          {headerExtra && <div className="kx-oc__headextra">{headerExtra}</div>}
          <button type="button" className="kx-oc__close" aria-label={title ? `${title} 닫기` : '닫기'} onClick={onClose}>
            <IconClose size={20} />
          </button>
        </header>
        <div className="kx-oc__body">{children}</div>
        {footer && <footer className="kx-oc__foot">{footer}</footer>}
      </div>
    </div>
  );
}
