/**
 * useHistoryLoader
 *
 * 차트를 왼쪽으로 스크롤할 때 과거 캔들 데이터를 추가로 불러오는 훅.
 *
 * 데이터 소스 우선순위:
 *   1. 백엔드 GET /api/candles/history (Ta4jStorage + 업비트 REST 프록시 + RSI 포함)
 *   2. 백엔드 호출 실패 시 → 업비트 REST 직접 폴백
 *
 * 중복 방지 락(Lock):
 *   loadingRef = true 인 동안에는 동일 타임스탬프 중복 요청을 원천 차단.
 *
 * 사용 방법:
 *   1. 훅에서 반환한 { isLoadingMore, loadMoreRef } 를 받는다.
 *   2. ChartManager.subscribeVisibleLogicalRange 콜백 안에서
 *      `r.from < LOAD_MORE_TRIGGER` 조건 시 `loadMoreRef.current()` 를 호출한다.
 */
import { useState, useEffect, useRef, useCallback } from 'react';
import type { RefObject, MutableRefObject } from 'react';
import type { ChartManager } from '../utils/ChartManager';
import { fetchUpbitCandlesBeforeCached } from '../utils/requestCache';
import type { OHLCVBar, Timeframe } from '../types';

/** 이 값(바 인덱스)보다 왼쪽에 가시 범위의 시작이 오면 추가 로드 트리거 */
export const LOAD_MORE_TRIGGER = 30;

/** 한 번에 불러올 추가 캔들 수 */
const LOAD_BATCH = 200;

/** 연속 스크롤 중 불필요한 요청을 막는 디바운스 (ms) */
const DEBOUNCE_MS = 300;

/** 백엔드 API 기본 URL */
import { API_BASE } from '../utils/backendApi';

interface UseHistoryLoaderOptions {
  symbol:      string;
  timeframe:   Timeframe;
  isUpbit:     boolean;
  managerRef:  RefObject<ChartManager | null>;
}

interface UseHistoryLoaderReturn {
  isLoadingMore: boolean;
  /** onManagerReady 의 subscribeVisibleLogicalRange 콜백에서 직접 호출 */
  loadMoreRef:   MutableRefObject<() => void>;
}

// ── 백엔드 /api/candles/history 호출 ──────────────────────────────────────────

/**
 * 백엔드에서 과거 캔들을 가져온다.
 * 응답에 rsi 필드가 포함되어 있으며, 시간 오름차순으로 정렬된다.
 *
 * @param market    업비트 마켓 코드 (e.g. "KRW-BTC")
 * @param type      캔들 타입 (e.g. "1m", "1d")
 * @param to        기준 시간 이전 ISO-8601 (e.g. "2026-05-20T10:00:00")
 * @param count     요청 캔들 수
 */
async function fetchHistoryFromBackend(
  market: string,
  type: string,
  to: string,
  count: number,
): Promise<OHLCVBar[]> {
  const params = new URLSearchParams({
    market,
    type,
    to,
    count: String(count),
  });
  const res = await fetch(`${API_BASE}/candles/history?${params.toString()}`);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  const data: any[] = await res.json();
  // 백엔드 CandleBarDto → OHLCVBar 변환 (rsi 는 extra 필드로 보존)
  return data.map(d => ({
    time:   d.time   as number,
    open:   d.open   as number,
    high:   d.high   as number,
    low:    d.low    as number,
    close:  d.close  as number,
    volume: d.volume as number,
  }));
}

// ── 훅 본체 ───────────────────────────────────────────────────────────────────

export function useHistoryLoader({
  symbol,
  timeframe,
  isUpbit,
  managerRef,
}: UseHistoryLoaderOptions): UseHistoryLoaderReturn {
  const [isLoadingMore, setIsLoadingMore] = useState(false);

  /**
   * 중복 요청 방지 락(Lock):
   *   ref 를 사용하여 re-render 없이 동기적으로 상태를 업데이트한다.
   *   true 인 동안에는 스크롤 이벤트가 아무리 발생해도 API 를 재호출하지 않는다.
   */
  const loadingRef    = useRef(false);
  /** false 가 되면 더 이상 API 를 호출하지 않음 */
  const hasMoreRef    = useRef(true);
  const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);

  // 심볼·타임프레임이 바뀌면 상태 초기화
  useEffect(() => {
    hasMoreRef.current = true;
    loadingRef.current = false;
    if (debounceTimer.current) {
      clearTimeout(debounceTimer.current);
      debounceTimer.current = null;
    }
  }, [symbol, timeframe]);

  const doLoadMore = useCallback(async () => {
    if (!isUpbit)           return;   // 시뮬레이션 모드는 건너뜀
    if (loadingRef.current) return;   // ★ 중복 방지 락: 이미 로딩 중이면 즉시 리턴
    if (!hasMoreRef.current) return;  // 더 이상 데이터 없음

    const mgr = managerRef.current;
    if (!mgr || mgr.getRawBarsLength() === 0) return;

    const oldestTime = mgr.getOldestBarTime();
    if (oldestTime === null) return;

    // Unix 타임스탬프(초) → ISO 8601 UTC ('Z' 없이 전달, 백엔드에서 처리)
    const beforeIso = new Date(oldestTime * 1000)
      .toISOString()
      .replace('.000Z', '');   // '2026-05-20T10:00:00'

    // ★ 락 설정: 이 시점부터 동일 타임스탬프 중복 요청 차단
    loadingRef.current = true;
    setIsLoadingMore(true);

    try {
      let olderBars: OHLCVBar[];

      // ── 1. 백엔드 /api/candles/history 우선 시도 ──────────────────────────
      try {
        olderBars = await fetchHistoryFromBackend(symbol, timeframe, beforeIso, LOAD_BATCH);
        console.debug(`[HistoryLoader] 백엔드에서 ${olderBars.length}개 수신`);
      } catch (backendErr) {
        // ── 2. 백엔드 실패 시 업비트 REST 직접 폴백 ───────────────────────
        console.warn('[HistoryLoader] 백엔드 실패, 업비트 직접 폴백:', backendErr);
        olderBars = await fetchUpbitCandlesBeforeCached(
          symbol, timeframe, LOAD_BATCH, beforeIso,
        );
      }

      if (olderBars.length === 0) {
        hasMoreRef.current = false;   // 더 이상 데이터 없음
      } else {
        // ── 3. 차트 앞부분에 부드럽게 병합 (Prepend) ──────────────────────
        await mgr.prependBars(olderBars);
      }
    } catch (e) {
      console.warn('[HistoryLoader] 과거 데이터 로드 실패:', e);
    } finally {
      // ★ 락 해제: 다음 스크롤 이벤트부터 재요청 허용
      loadingRef.current = false;
      setIsLoadingMore(false);
    }
  }, [symbol, timeframe, isUpbit, managerRef]);

  /**
   * 디바운스 래퍼 — subscribeVisibleLogicalRange 는 스크롤 중 매 프레임 호출되므로
   * 사용자가 잠시 멈췄을 때 한 번만 실제 API 요청이 나가도록 한다.
   */
  const loadMoreRef = useRef<() => void>(() => {});
  loadMoreRef.current = useCallback(() => {
    if (debounceTimer.current) clearTimeout(debounceTimer.current);
    debounceTimer.current = setTimeout(() => {
      debounceTimer.current = null;
      doLoadMore();
    }, DEBOUNCE_MS);
  }, [doLoadMore]);

  return { isLoadingMore, loadMoreRef };
}
