"""Bayesian Online Change Point Detection pipeline functions.

Pipeline functions:
    bocpd_detector - Real-time Bayesian change point detection
"""

from __future__ import annotations

from typing import Any, Callable

from horizon._horizon import BocpdDetector
from horizon.context import Context


def bocpd_detector(
    feed: str,
    hazard_rate: float = 250.0,
    threshold: float = 0.5,
) -> Callable[[Context], dict[str, Any]]:
    """Create a BOCPD change point detection pipeline function.

    Uses Bayesian online change point detection with Normal-Inverse-Gamma
    conjugate priors to detect structural breaks in price series.

    Args:
        feed: Feed name to read price data from.
        hazard_rate: Expected run length (higher = less sensitive).
        threshold: Change probability threshold for triggering alerts.

    Returns:
        Pipeline function: (Context) -> dict with keys:
            change_prob, run_length, is_changepoint
    """
    from horizon._horizon import auth_require_pro
    auth_require_pro()

    detectors: dict[str, BocpdDetector] = {}

    def _bocpd_detector(ctx: Context) -> dict[str, Any]:
        market_id = ctx.market.id if ctx.market else "__default__"
        fd = ctx.feeds.get(feed)

        if market_id not in detectors:
            detectors[market_id] = BocpdDetector(hazard_rate)

        detector = detectors[market_id]

        result = {
            "change_prob": detector.change_probability(),
            "run_length": detector.most_likely_run_length(),
            "is_changepoint": False,
        }

        if fd is not None and fd.price > 0:
            state = detector.update(fd.price)
            result["change_prob"] = state.change_prob
            result["run_length"] = state.most_likely_run_length
            result["is_changepoint"] = state.change_prob >= threshold

        return result

    _bocpd_detector.__name__ = "bocpd_detector"
    return _bocpd_detector
