/**
 * React hooks for API data fetching with caching.
 */

import { useEffect, useState, useCallback, useRef } from 'react';
import { api } from '../lib/api';
import type {
  NetworkStats,
  ExplorerBlock,
  ExplorerTransaction,
  AddressInfo,
  UTXO,
  DagVisualization,
  PaginatedResponse,
} from '../lib/types';

interface UseQueryResult<T> {
  data: T | null;
  isLoading: boolean;
  error: Error | null;
  refetch: () => Promise<void>;
}

function useQuery<T>(
  fetcher: () => Promise<T>,
  deps: unknown[] = []
): UseQueryResult<T> {
  const [data, setData] = useState<T | null>(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  // Use ref to always have access to the latest fetcher without
  // including it in useCallback deps (prevents stale closures)
  const fetcherRef = useRef(fetcher);
  fetcherRef.current = fetcher;

  const doFetch = useCallback(async () => {
    setIsLoading(true);
    setError(null);
    try {
      const result = await fetcherRef.current();
      setData(result);
    } catch (e) {
      setError(e instanceof Error ? e : new Error(String(e)));
    } finally {
      setIsLoading(false);
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, deps);

  useEffect(() => {
    doFetch();
  }, [doFetch]);

  return { data, isLoading, error, refetch: doFetch };
}

export function useStats(refreshInterval?: number): UseQueryResult<NetworkStats> {
  const result = useQuery(() => api.getStats(), []);

  useEffect(() => {
    if (!refreshInterval) return;
    const interval = setInterval(result.refetch, refreshInterval);
    return () => clearInterval(interval);
  }, [refreshInterval, result.refetch]);

  return result;
}

export function useBlocks(
  page: number,
  limit = 25
): UseQueryResult<PaginatedResponse<ExplorerBlock>> {
  return useQuery(() => api.getBlocks(page, limit), [page, limit]);
}

export function useBlock(hash: string, includeTxs = true): UseQueryResult<ExplorerBlock> {
  return useQuery(() => api.getBlock(hash, includeTxs), [hash, includeTxs]);
}

export function useTransaction(txId: string): UseQueryResult<ExplorerTransaction> {
  return useQuery(() => api.getTransaction(txId), [txId]);
}

export function useAddress(address: string): UseQueryResult<AddressInfo> {
  return useQuery(() => api.getAddress(address), [address]);
}

export function useAddressUtxos(address: string): UseQueryResult<UTXO[]> {
  return useQuery(() => api.getAddressUtxos(address), [address]);
}

export function useMempool(
  page: number,
  limit = 25
): UseQueryResult<PaginatedResponse<ExplorerTransaction>> {
  return useQuery(() => api.getMempool(page, limit), [page, limit]);
}

export function useDag(depth = 10): UseQueryResult<DagVisualization> {
  return useQuery(() => api.getDag(depth), [depth]);
}

export function useTips(): UseQueryResult<string[]> {
  return useQuery(() => api.getTips(), []);
}

// Gas estimation hooks
import type { GasCosts, GasEstimateRequest, GasEstimateResponse } from '../lib/types';

export function useGasCosts(): UseQueryResult<GasCosts> {
  return useQuery(() => api.getGasCosts(), []);
}

interface UseGasEstimateOptions {
  /** Enable or disable the query */
  enabled?: boolean;
}

export function useGasEstimate(
  request: GasEstimateRequest | null,
  options: UseGasEstimateOptions = {}
): UseQueryResult<GasEstimateResponse> {
  const { enabled = true } = options;

  return useQuery(
    async () => {
      if (!request || !enabled) {
        throw new Error('Gas estimation disabled');
      }
      return api.estimateGas(request);
    },
    [request?.to, request?.from, request?.data, request?.value, enabled]
  );
}
