"""urllib DailyBarClient — licensed Massive (formerly Polygon) daily closes with TOTAL-RETURN
reconstruction. Drop-in for YahooDailyClient on the equity sleeves (sixtyforty/multistrat/gd), which
compound daily TOTAL returns of dividend-paying ETFs (SPY/IEF/GLD/...).

Why reconstruction is required: Massive aggregates with `adjusted=true` are SPLIT-ONLY — dividends are
NOT folded in (a dividend-day price drop appears in full). So we rebuild a Yahoo-adjclose-equivalent
back-adjusted series ourselves from the split-adjusted closes + the dividends endpoint.

Method (standard back-adjustment): for each ex-dividend date D with cash C, multiply ALL closes on
dates strictly BEFORE D by factor (1 - C / close_{trading day immediately before D}). Factors compound
across dividends. The resulting day-over-day return on the ex-div day = raw_close_D / adj_close_{D-1} - 1
= price_return + C/prev_close, i.e. the dividend is added back (matches Yahoo's adjclose semantics).

Plan/history note: our Massive plan only entitles ~2 years of history; multi-year ranges 403. We request
a bounded RECENT window (default last ~500 calendar days, safely inside 2y) — far more than any sleeve's
signal lookback. Auth: Bearer with POLYGON_API_KEY (env). Host default https://api.massive.com.
"""
from __future__ import annotations

import datetime as dt
import json
import os
import time
import urllib.parse
import urllib.request
from typing import Any

_AGGS = "{base}/v2/aggs/ticker/{sym}/range/1/day/{frm}/{to}?adjusted=true&sort=asc&limit=50000"
_DIVS = "{base}/v3/reference/dividends?ticker={sym}&ex_dividend_date.gte={frm}&ex_dividend_date.lte={to}&limit=1000"


class MassiveDailyClient:
    def __init__(
        self,
        api_key: str | None = None,
        lookback_days: int = 500,
        base_url: str = "https://api.massive.com",
    ) -> None:
        self._key = api_key if api_key is not None else os.environ["POLYGON_API_KEY"]
        self._lookback_days = lookback_days
        self._base = base_url.rstrip("/")

    def _get(self, url: str, tries: int = 4) -> dict[str, Any]:
        for a in range(tries):
            try:
                req = urllib.request.Request(url, headers={"Authorization": f"Bearer {self._key}"})
                data: dict[str, Any] = json.loads(urllib.request.urlopen(req, timeout=30).read())
                return data
            except Exception:  # noqa: BLE001 — transient HTTP; retry then re-raise
                if a == tries - 1:
                    raise
                time.sleep(2 * (a + 1))
        raise RuntimeError("unreachable")

    def _window(self) -> tuple[str, str]:
        to = dt.date.today()
        frm = to - dt.timedelta(days=self._lookback_days)
        return frm.isoformat(), to.isoformat()

    def adj_closes(self, symbol: str) -> dict[str, float]:
        frm, to = self._window()
        sym = urllib.parse.quote(symbol)

        aggs = self._get(_AGGS.format(base=self._base, sym=sym, frm=frm, to=to))
        # split-adjusted closes keyed by date (tz-aware; midnight-UTC epoch ms)
        closes: dict[str, float] = {}
        for r in aggs.get("results") or []:
            d = dt.datetime.fromtimestamp(r["t"] / 1000, dt.timezone.utc).strftime("%Y-%m-%d")
            closes[d] = float(r["c"])
        if not closes:
            return {}

        divs = self._get(_DIVS.format(base=self._base, sym=sym, frm=frm, to=to))
        dividends: list[tuple[str, float]] = [
            (r["ex_dividend_date"], float(r["cash_amount"]))
            for r in (divs.get("results") or [])
            if r.get("cash_amount")
        ]

        return self._total_return(closes, dividends)

    @staticmethod
    def _total_return(closes: dict[str, float], dividends: list[tuple[str, float]]) -> dict[str, float]:
        """Back-adjust split-adjusted closes into a total-return series (Yahoo adjclose equivalent).

        Accumulate one cumulative factor per date: for each ex-div date D (within the close window) with
        cash C, every date strictly before D is multiplied by (1 - C / close_{trading day before D}).
        """
        dates = sorted(closes)
        factor: dict[str, float] = {d: 1.0 for d in dates}

        for ex_date, cash in dividends:
            # the last trading day strictly before the ex-div date
            prior = [d for d in dates if d < ex_date]
            if not prior:
                continue  # dividend predates our close window — no in-window dates to adjust
            prev = prior[-1]
            prev_close = closes[prev]
            if prev_close <= 0:
                continue
            f = 1.0 - cash / prev_close
            for d in prior:
                factor[d] *= f

        return {d: closes[d] * factor[d] for d in dates}
