"""Yahoo Finance adapter — free daily adjusted closes. Implements the DataProvider port.

Bakes in a hard-won lesson: drop today's INCOMPLETE intraday bar (it corrupts vol/trend signals
when the platform runs mid-session). Strategies here are daily-CLOSE strategies.
"""
from __future__ import annotations

import datetime as dt
import json
import urllib.request

import numpy as np

from fxhnt.domain.models import Market, PriceSeries

_BASE = "https://query1.finance.yahoo.com/v8/finance/chart"


def _build_series(
    market: Market,
    ts: list[int],
    adjclose: list[float | None] | None,
    raw_close: list[float | None] | None,
    raw_high: list[float | None] | None,
    raw_low: list[float | None] | None,
    today: str,
    start: str | None,
    end: str | None,
) -> PriceSeries:
    """Pure transform of Yahoo arrays -> PriceSeries. Adjusted close is canonical; raw high/low are
    scaled by the per-row adjclose/raw_close ratio. Missing high/low (or no adjclose) default to that
    bar's adjusted close — a row is dropped only when its adjusted close is None (unchanged policy)."""
    dates: list[str] = []
    close: list[float] = []
    high: list[float] = []
    low: list[float] = []
    for i, t in enumerate(ts):
        ac = adjclose[i] if adjclose is not None else None
        rc = raw_close[i] if raw_close is not None else None
        c = ac if ac is not None else rc           # fall back to raw close when no adjclose
        if c is None:
            continue                               # drop only when there is no close at all
        d = dt.datetime.fromtimestamp(t, dt.timezone.utc).strftime("%Y-%m-%d")
        if d == today or (start and d < start) or (end and d > end):
            continue
        c = float(c)
        ratio = (c / float(rc)) if (rc is not None and rc != 0) else 1.0
        rh = raw_high[i] if raw_high is not None else None
        rl = raw_low[i] if raw_low is not None else None
        dates.append(d)
        close.append(c)
        high.append(float(rh) * ratio if rh is not None else c)
        low.append(float(rl) * ratio if rl is not None else c)
    return PriceSeries(market=market, dates=tuple(dates), close=np.array(close, dtype=float),
                       high=np.array(high, dtype=float), low=np.array(low, dtype=float))


class YahooDataProvider:
    name = "yahoo"

    def __init__(self, default_range: str = "25y", timeout: int = 30) -> None:
        self._range = default_range
        self._timeout = timeout

    def fetch(self, market: Market, start: str | None = None, end: str | None = None) -> PriceSeries:
        url = f"{_BASE}/{market.symbol}?interval=1d&range={self._range}"
        req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
        payload = json.loads(urllib.request.urlopen(req, timeout=self._timeout).read())
        res = payload["chart"]["result"][0]
        ts = res["timestamp"]
        ind = res["indicators"]
        quote = ind["quote"][0]
        adjclose = ind.get("adjclose", [{}])[0].get("adjclose")
        today = dt.datetime.now(dt.timezone.utc).strftime("%Y-%m-%d")
        return _build_series(market, ts, adjclose, quote.get("close"), quote.get("high"),
                             quote.get("low"), today, start, end)
