"use client";

import {
  useState,
  useEffect,
  useRef,
  createContext,
  useContext,
  useMemo,
} from "react";
import type { Dispatch, SetStateAction, ReactNode } from "react";

// ─── TSP (Tab State Persistence) 중앙 관리 훅 ───
// 모든 컴포넌트의 UI 상태를 sessionStorage에 자동 보존/복원
// useState 대신 usePersistedState를 사용하면 탭 상태 보존이 자동 적용됨

const TSP_PREFIX = "tsp-";

// ─── Context ───

interface TSPContextValue {
  screenId?: number;
  componentId?: string;
}

const TSPContext = createContext<TSPContextValue>({});

export function TSPProvider({
  screenId,
  componentId,
  children,
}: {
  screenId?: number;
  componentId?: string;
  children: ReactNode;
}) {
  const value = useMemo(
    () => ({ screenId, componentId }),
    [screenId, componentId],
  );
  return <TSPContext.Provider value={value}>{children}</TSPContext.Provider>;
}

// ─── 직렬화 (Set, Map, Date 지원) ───

function serialize(value: unknown): string {
  return JSON.stringify(value, (_key, val) => {
    if (val instanceof Set) return { __tsp: "Set", d: Array.from(val) };
    if (val instanceof Map) return { __tsp: "Map", d: Array.from(val.entries()) };
    if (val instanceof Date) return { __tsp: "Date", d: val.toISOString() };
    return val;
  });
}

function deserialize(raw: string): unknown {
  return JSON.parse(raw, (_key, val) => {
    if (val && typeof val === "object" && val.__tsp === "Set") return new Set(val.d);
    if (val && typeof val === "object" && val.__tsp === "Map") return new Map(val.d);
    if (val && typeof val === "object" && val.__tsp === "Date") return new Date(val.d);
    return val;
  });
}

// ─── 메인 훅 ───

interface PersistedStateOptions {
  /** sessionStorage 저장 지연 시간 (기본 300ms) */
  debounce?: number;
}

/**
 * useState와 동일한 인터페이스로 탭 상태를 자동 보존하는 훅
 *
 * @param key - 상태 식별 키 (같은 컴포넌트 내에서 고유해야 함)
 * @param defaultValue - 캐시가 없을 때 사용할 기본값
 * @param options - debounce 등 옵션
 *
 * @example
 * const [selectedRow, setSelectedRow] = usePersistedState('selectedRow', null);
 * const [expanded, setExpanded] = usePersistedState('expanded', new Set<string>());
 * const [scroll, setScroll] = usePersistedState('scrollTop', 0, { debounce: 100 });
 */
export function usePersistedState<T>(
  key: string,
  defaultValue: T,
  options?: PersistedStateOptions,
): [T, Dispatch<SetStateAction<T>>] {
  const { screenId, componentId } = useContext(TSPContext);
  const debounceMs = options?.debounce ?? 300;

  // tsp-{screenId}-{componentId}-{key}
  const cacheKey =
    screenId != null && componentId
      ? `${TSP_PREFIX}${screenId}-${componentId}-${key}`
      : null;

  // 안정적인 참조 (cacheKey가 렌더 중 변하지 않도록)
  const cacheKeyRef = useRef(cacheKey);
  cacheKeyRef.current = cacheKey;

  const [state, setState] = useState<T>(() => {
    if (!cacheKey || typeof window === "undefined") return defaultValue;
    try {
      const raw = sessionStorage.getItem(cacheKey);
      if (raw !== null) return deserialize(raw) as T;
    } catch {
      /* 파싱 실패 시 기본값 사용 */
    }
    return defaultValue;
  });

  // debounce 저장
  const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const isFirstRender = useRef(true);
  const latestStateRef = useRef<T>(state);
  latestStateRef.current = state;

  useEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false;
      return;
    }

    const ck = cacheKeyRef.current;
    if (!ck) return;

    if (timerRef.current) clearTimeout(timerRef.current);
    timerRef.current = setTimeout(() => {
      try {
        sessionStorage.setItem(ck, serialize(state));
      } catch {
        /* 용량 초과 무시 */
      }
      timerRef.current = null;
    }, debounceMs);

    return () => {
      if (timerRef.current) clearTimeout(timerRef.current);
    };
  }, [state, debounceMs]);

  // unmount 시 미저장 상태 flush (탭 전환 중 데이터 유실 방지)
  useEffect(() => {
    return () => {
      if (timerRef.current) {
        clearTimeout(timerRef.current);
        const ck = cacheKeyRef.current;
        if (ck) {
          try { sessionStorage.setItem(ck, serialize(latestStateRef.current)); } catch {}
        }
      }
    };
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  return [state, setState];
}

// ─── 캐시 정리 유틸리티 ───

/**
 * 특정 화면(또는 화면+컴포넌트)의 TSP 캐시를 일괄 삭제
 *
 * @param screenId - 화면 ID
 * @param componentId - (선택) 특정 컴포넌트만 삭제할 때
 */
export function clearTSPCache(screenId: number, componentId?: string) {
  const prefix = componentId
    ? `${TSP_PREFIX}${screenId}-${componentId}-`
    : `${TSP_PREFIX}${screenId}-`;

  const toRemove: string[] = [];
  for (let i = 0; i < sessionStorage.length; i++) {
    const k = sessionStorage.key(i);
    if (k?.startsWith(prefix)) toRemove.push(k);
  }
  toRemove.forEach((k) => sessionStorage.removeItem(k));
}

/**
 * 모든 TSP 캐시 삭제 (로그아웃 등)
 */
export function clearAllTSPCache() {
  const toRemove: string[] = [];
  for (let i = 0; i < sessionStorage.length; i++) {
    const k = sessionStorage.key(i);
    if (k?.startsWith(TSP_PREFIX)) toRemove.push(k);
  }
  toRemove.forEach((k) => sessionStorage.removeItem(k));
}
