"""Causal monthly-seasonality signal (pure domain function). The signal for (year Y, month M) uses
ONLY prior years' month-M returns (yy < Y) — strictly causal, zero until min_prior_years of history."""
from __future__ import annotations

from collections import defaultdict

import numpy as np

from fxhnt.domain.models import PriceSeries


def seasonal_signal(prices: PriceSeries, *, min_prior_years: int = 3) -> tuple[tuple[str, ...], np.ndarray]:
    """For each day in (year Y, month M): the mean daily return in month M across ALL PRIOR years
    (< Y). Zero until min_prior_years of history — no lookahead."""
    dates = prices.dates
    ym = [(int(d[:4]), int(d[5:7])) for d in dates]
    c = np.asarray(prices.close, float)
    r = np.zeros(len(c))
    r[1:] = c[1:] / c[:-1] - 1.0
    r = np.nan_to_num(r)

    bucket: dict[tuple[int, int], list[float]] = defaultdict(list)
    for i, key in enumerate(ym):
        bucket[key].append(r[i])
    monthly_mean = {k: float(np.mean(v)) for k, v in bucket.items()}
    years = sorted({y for y, _ in ym})

    sig = np.zeros(len(c))
    for i, (y, m) in enumerate(ym):
        prior = [monthly_mean[(yy, m)] for yy in years if yy < y and (yy, m) in monthly_mean]
        if len(prior) >= min_prior_years:
            sig[i] = float(np.mean(prior))
    return tuple(dates), sig
