"""Binance funding rate data fetcher and cache.

Downloads historical funding rates from Binance Futures API and caches
as a single parquet file per symbol.

Funding rates are published every 8 hours (00:00, 08:00, 16:00 UTC).
Positive rate = longs pay shorts (bullish crowding).
Negative rate = shorts pay longs (bearish crowding).

Usage::

    from vanta.data.funding import load_funding

    funding = load_funding("BTCUSDT", "2025-02-15", "2026-01-01")
    # → dict of {unix_timestamp: funding_rate}
"""

from __future__ import annotations

import json
import logging
import time
from datetime import datetime, timezone
from pathlib import Path

logger = logging.getLogger(__name__)

_API_URL = "https://fapi.binance.com/fapi/v1/fundingRate"
_PAGE_SIZE = 1000  # Binance max per request


def fetch_funding(
    symbol: str = "BTCUSDT",
    start: str = "2025-02-15",
    end: str = "2026-01-01",
    cache_dir: str = ".cache/backtest",
) -> list[dict]:
    """Fetch funding rates from Binance, with local caching.

    Returns list of ``{"ts": unix_ts, "rate": float, "mark_price": float}``.
    """
    cache_path = Path(cache_dir) / "binance" / symbol / "funding" / f"{start}_{end}.json"

    if cache_path.exists():
        with open(cache_path) as f:
            return json.load(f)

    import requests

    start_ms = int(datetime.strptime(start, "%Y-%m-%d").replace(tzinfo=timezone.utc).timestamp() * 1000)
    end_ms = int(datetime.strptime(end, "%Y-%m-%d").replace(tzinfo=timezone.utc).timestamp() * 1000)

    all_records: list[dict] = []
    cursor = start_ms

    while cursor < end_ms:
        resp = requests.get(
            _API_URL,
            params={
                "symbol": symbol,
                "startTime": cursor,
                "endTime": end_ms,
                "limit": _PAGE_SIZE,
            },
            timeout=30,
        )
        resp.raise_for_status()
        batch = resp.json()

        if not batch:
            break

        for r in batch:
            ts_ms = int(r["fundingTime"])
            if ts_ms > end_ms:
                break
            all_records.append({
                "ts": ts_ms // 1000,
                "rate": float(r["fundingRate"]),
                "mark_price": float(r.get("markPrice", 0)),
            })

        # Move cursor past last record
        cursor = int(batch[-1]["fundingTime"]) + 1

        if len(batch) < _PAGE_SIZE:
            break

        time.sleep(0.2)  # rate limit courtesy

    logger.info("Fetched %d funding records for %s (%s → %s)", len(all_records), symbol, start, end)

    # Cache
    cache_path.parent.mkdir(parents=True, exist_ok=True)
    with open(cache_path, "w") as f:
        json.dump(all_records, f)

    return all_records


def load_funding(
    symbol: str = "BTCUSDT",
    start: str = "2025-02-15",
    end: str = "2026-01-01",
    cache_dir: str = ".cache/backtest",
) -> dict[int, float]:
    """Load funding rates as a dict of ``{unix_ts: rate}``.

    Automatically fetches from Binance if not cached.
    """
    records = fetch_funding(symbol, start, end, cache_dir)
    return {r["ts"]: r["rate"] for r in records}
