"""
Funding Rate Arb — Complete Implementation.

Strategy:
  Funding rates on perpetual futures represent the cost of leverage.
  When funding is positive (longs pay shorts), short the perp and collect.
  When funding is negative (shorts pay longs), go long the perp and collect.

  The Hyperliquid API provides predicted funding rates via:
    - predictedFundings: current predicted rate for each interval
    - metaAndAssetCtxs: asset context including current funding

  Entry: |annualized_funding_rate| > threshold (5-10% APR)
  Exit:  |annualized_funding_rate| < threshold/2 or after N hours
  Size:  scales with rate — higher rate = larger size
"""

import requests
import time
import math
from typing import Optional

MAINNET_API = "https://api.hyperliquid.xyz/info"
TESTNET_API = "https://api.hyperliquid-testnet.xyz/info"

# Cache funding rates to avoid hitting API every tick
_funding_cache: dict = {}
_last_funding_fetch: float = 0
FUNDING_CACHE_TTL = 30  # seconds


def get_funding_rates(use_testnet: bool = False) -> dict[str, float]:
    """
    Fetch current predicted funding rates for supported coins.

    Uses Hyperliquid's predictedFundings endpoint which returns
    the current projected funding rate for each perpetual.

    Returns: {coin: funding_rate_annualized}
    """
    global _funding_cache, _last_funding_fetch

    now = time.time()
    if now - _last_funding_fetch < FUNDING_CACHE_TTL and _funding_cache:
        return _funding_cache

    api = TESTNET_API if use_testnet else MAINNET_API
    rates: dict[str, float] = {}

    # Method 1: Try metaAndAssetCtxs (most reliable)
    try:
        r = requests.post(MAINNET_API, json={"type": "metaAndAssetCtxs"}, timeout=10)
        data = r.json()
        if isinstance(data, list) and len(data) >= 2:
            universe = data[0].get("universe", [])
            ctxs = data[1]
            for i, u in enumerate(universe):
                name = u.get("name", "")
                if name in ("BTC", "ETH", "HYPE", "VVV", "SOL"):
                    try:
                        funding = float(ctxs[i].get("funding", 0))
                        # funding is the 8h rate; annualize: × 365 × (24/8) = × 1095
                        annual = funding * 1095
                        rates[name] = annual
                    except (IndexError, ValueError, TypeError):
                        pass
    except Exception:
        pass

    # Method 2: Fallback to predictedFundings
    if not rates:
        try:
            r = requests.post(MAINNET_API, json={"type": "predictedFundings"}, timeout=10)
            data = r.json()
            if isinstance(data, list):
                for coin_entry in data:
                    coin = coin_entry[0]
                    if coin not in ("BTC", "ETH", "HYPE", "VVV", "SOL"):
                        continue
                    for venue_entry in coin_entry[1]:
                        venue = venue_entry[0]
                        info = venue_entry[1]
                        rate_str = info.get("fundingRate", "0")
                        try:
                            rate = float(rate_str)
                        except (ValueError, TypeError):
                            rate = 0.0
                        interval_hours = info.get("fundingIntervalHours", 8)
                        annual = rate * (365 * 24 / interval_hours)
                        if coin not in rates or "HlPerp" in venue:
                            rates[coin] = annual
        except Exception:
            pass

    _funding_cache = rates
    _last_funding_fetch = now
    return rates


def funding_arb_signal(
    coin: str = "BTC",
    apr_threshold: float = 0.05,  # 5% APR minimum
    apr_exit: float = 0.02,       # 2% APR to exit
    current_position: int = 0,
) -> dict:
    """
    Generate funding rate arbitrage signal.

    Args:
        coin: Ticker to check.
        apr_threshold: Minimum annualized funding rate to enter (>0.05 = 5%).
        apr_exit: Rate below which to exit position.
        current_position: -1 (short), 0 (none), +1 (long).

    Returns:
        dict with signal, rate, annual_apr, reason.
    """
    rates = get_funding_rates()
    annual = rates.get(coin, 0)
    rate_8h = annual / 1095  # de-annualize

    signal = 0
    reason = ""

    if abs(annual) > apr_threshold and current_position == 0:
        signal = -1 if annual > 0 else +1  # short if funding positive, long if negative
        reason = f"funding_{annual*100:.1f}pct_apr"
    elif current_position != 0:
        # Exit condition: rate has dropped below exit threshold
        if abs(annual) < apr_exit:
            signal = -current_position
            reason = f"exit_funding_{annual*100:.2f}pct_apr"
        # Also exit if funding flips sign (we'd be paying instead of collecting)
        elif (current_position == -1 and annual < 0) or (current_position == 1 and annual > 0):
            signal = -current_position
            reason = f"exit_funding_flipped_{annual*100:.2f}pct_apr"

    return {
        "signal": signal,
        "rate_8h": rate_8h,
        "annual_apr": annual,
        "reason": reason,
    }
