/**
 * useForexRate.js
 *
 * Exact FX mirror of useMarketRate.js.
 * Fetches live rates from your backend and feeds them into useForexMarkets
 * as the external anchor. The simulation ticks at tickMs; the fetch polls
 * at pollMs.
 *
 * Usage — identical pattern to useMarketRate:
 *   const { markets, get, rates, getRate, loadingRates } = useForexRate({
 *     assets: [
 *       { pair: "EURUSD", price: 1.0850, fallback: 1.0850 },
 *       { pair: "GBPJPY", price: 185.40, fallback: 185.40 },
 *     ],
 *   });
 *
 *   const eur = get("EURUSD");
 *   // eur.price  eur.bid  eur.ask  eur.spreadPips  eur.changePips  eur.ticks
 */

import axios from "axios";
import { useEffect, useMemo, useState, useCallback } from "react";
import { useForexMarkets } from "./useForexMarkets";

// ─────────────────────────────────────────────────────────────────────────────
// Helpers  (identical to useMarketRate)
// ─────────────────────────────────────────────────────────────────────────────

function safeNum(value, fallback = 0) {
  const n = Number(value);
  return Number.isFinite(n) ? n : fallback;
}

function mapsEqual(a, b) {
  if (a.size !== b.size) return false;
  for (const [k, v] of a) {
    const bv = b.get(k);
    if (!bv) return false;
    if (v.price !== bv.price || v.bid !== bv.bid || v.ask !== bv.ask) return false;
  }
  return true;
}

// ─────────────────────────────────────────────────────────────────────────────
// Hook
// ─────────────────────────────────────────────────────────────────────────────

/**
 * @param {object}   opts
 *
 * @param {object[]} opts.assets
 *   Array of pair descriptors — same shape as useMarketRate assets but with
 *   `pair` instead of `symbol`:
 *   { pair, price?, fallback?, dailyVol?, basePipSpread?, carryDrift?,
 *     low24h?, high24h? }
 *
 * @param {"all"|"pairs"} opts.type
 *   "all"   — fetch all pairs from the endpoint (default)
 *   "pairs" — pass ?pairs=EURUSD,GBPJPY to filter server-side
 *
 * @param {number} opts.pollMs  Backend poll interval ms (default 5 000)
 * @param {number} opts.tickMs  Simulation tick interval ms (default 5 000)
 */
const useForexRate = ({
  assets  = [],
  type    = "all",
  pollMs  = 5_000,
  tickMs  = 5_000,
} = {}) => {

  const [rates,        setRates]        = useState(new Map());
  const [loadingRates, setLoadingRates] = useState(false);
  const [rateError,    setRateError]    = useState(null);


  // ── Stable pairs string — avoids new array ref every render ───────────────
  // Mirror of symbolsKey in useMarketRate, but reading `pair` field.

  const pairsKey = useMemo(
    () => assets
      .map((a) => String(a.pair ?? "").toUpperCase())
      .filter(Boolean)
      .join(","),
    [assets],
  );

  const pairsList = useMemo(
    () => (pairsKey ? pairsKey.split(",") : []),
    [pairsKey],
  );
  
  // ── Live rate fetch ───────────────────────────────────────────────────────

  useEffect(() => {
    if (!pairsList.length) return;

    let cancelled = false;

    const fetchRates = async () => {
      setLoadingRates(true);
      setRateError(null);
      try {
        const url = type === "pairs"
          ? `http://localhost:5001/api/v1/latest/rates/forex?pairs=${pairsKey}`
          : `http://localhost:5001/api/v1/latest/rates/forex`;

        const res  = await axios.get(url);
        const data = res?.data?.data?.rates ?? res?.data?.rates ?? [];

        if (cancelled) return;

        setRates((prev) => {
          const next = new Map(
            data.map((d) => [
              String(d.pair ?? d.symbol ?? "").toUpperCase(),
              d,
            ]),
          );
          return mapsEqual(prev, next) ? prev : next;
        });
      } catch (e) {
        if (!cancelled) {
          console.warn("[useForexRate] Rates fetch failed:", e.message);
          setRateError(e.message);
        }
      } finally {
        if (!cancelled) setLoadingRates(false);
      }
    };

    // Immediate first fetch, then poll
    fetchRates();
    const id = setInterval(fetchRates, pollMs);
    return () => {
      cancelled = true;
      clearInterval(id);
    };
  }, [pairsKey, type, pollMs]);

  // ── Build ratesByPair for the engine anchor ───────────────────────────────
  // Mirror of ratesBySymbol in useMarketRate.
  // Shape expected by useForexMarkets:
  //   { [PAIR]: { price, fallback, bid, ask, dailyVol, basePipSpread, ... } }

  const ratesByPair = useMemo(() => {
    const map = {};
    for (const asset of assets) {
      const pair = String(asset.pair ?? "").toUpperCase();
      if (!pair) continue;

      const rate = rates.get(pair);

      // Live price from API; fall back to asset.price then asset.fallback
      const livePrice = safeNum(rate?.price, null) ?? safeNum(rate?.mid,  null) ?? 0;
      const liveBid   = safeNum(rate?.bid,   null) ?? 0;
      const liveAsk   = safeNum(rate?.ask,   null) ?? 0;

      const price    = livePrice || safeNum(asset.price, 0) || safeNum(asset.fallback, 1.0);
      const bid      = liveBid   || price;
      const ask      = liveAsk   || price;
      const fallback = safeNum(asset.fallback, price) || safeNum(asset.price, 1.0);

      map[pair] = {
        price,
        bid,
        ask,
        fallback,
        low24h:        safeNum(rate?.low24h,  null) ?? safeNum(asset.low24h,  0),
        high24h:       safeNum(rate?.high24h, null) ?? safeNum(asset.high24h, 0),
        dailyVol:      asset.dailyVol      ?? null,
        basePipSpread: asset.basePipSpread ?? null,
        carryDrift:    asset.carryDrift    ?? null,
      };
    }
    return map;
  }, [assets, rates]);

  // ── Simulation ────────────────────────────────────────────────────────────
  // Identical call shape to useHybridMarkets in useMarketRate.

  const { markets, get, resetBaseline, setFairValue, engineRef, baselineRef } =
    useForexMarkets({
      pairs:                    pairsList,
      ratesByPair,
      tickMs,
      persistBaseline:          true,
      baselineMode:             "session",
      resetBaselineOnPairReadd: false,
      anchor: {
        enabled:        true,
        internalWeight: 0.05,
        externalWeight: 0.95,
      },
    });

  // ── Convenience: raw rate record from last API poll ───────────────────────

  const getRate = useCallback(
    (pair) => rates.get(String(pair ?? "").toUpperCase()) ?? null,
    [rates],
  );

  return {
    // ── Simulation state ──────────────────────────────────────────────────
    markets,       // { [pair]: ForexMarketSnapshot }
    get,           // (pair) => ForexMarketSnapshot | null

    // ── Raw rate data ──────────────────────────────────────────────────────
    rates,         // Map<pair, RateRecord>  — raw API response
    getRate,       // (pair) => RateRecord | null
    ratesByPair,   // plain object fed into engine (debugging)

    // ── Status ────────────────────────────────────────────────────────────
    loadingRates,
    rateError,

    // ── Controls ──────────────────────────────────────────────────────────
    resetBaseline, // (pair?) => void
    setFairValue,  // (pair, price) => void

    // ── Advanced ──────────────────────────────────────────────────────────
    engineRef,
    baselineRef,
  };
};

export default useForexRate;