"""Correlation regime detection pipeline functions."""

from __future__ import annotations

import logging
from typing import Any, Callable

from horizon._horizon import (
    live_correlation_matrix,
    detect_regime_shift,
    contagion_score,
    find_decorrelated,
    incremental_correlation_update,
)

logger = logging.getLogger("horizon")


def correlation_matrix(
    feed_names: list[str],
    window: int = 50,
) -> Callable:
    """Compute live correlation matrix across feeds."""
    from horizon._horizon import auth_require_ultra
    auth_require_ultra()

    price_histories: dict[str, list[float]] = {}

    def _fn(ctx) -> dict[str, Any]:
        market_ids = []
        returns_matrix = []

        for name in feed_names:
            feed = ctx.feeds.get(name)
            if not feed or feed.price <= 0:
                continue

            hist = price_histories.setdefault(name, [])
            hist.append(feed.price)
            if len(hist) > window + 1:
                hist.pop(0)

            if len(hist) >= 3:
                returns = [hist[i] / hist[i-1] - 1 for i in range(1, len(hist))]
                returns_matrix.append(returns)
                market_ids.append(name)

        if len(market_ids) < 2:
            return {"correlations": [], "num_markets": len(market_ids)}

        entries = live_correlation_matrix(market_ids, returns_matrix)
        corr_list = [
            {"a": e.market_a, "b": e.market_b, "corr": e.correlation, "n": e.num_observations}
            for e in entries
        ]

        return {
            "correlations": corr_list,
            "num_markets": len(market_ids),
            "mean_correlation": sum(abs(e.correlation) for e in entries) / max(len(entries), 1),
        }

    _fn.__name__ = "correlation_matrix"
    return _fn


def regime_shift(
    feed_names: list[str],
    lookback: int = 100,
    recent_window: int = 20,
    threshold: float = 2.0,
) -> Callable:
    """Detect correlation regime shifts."""
    from horizon._horizon import auth_require_ultra
    auth_require_ultra()

    corr_history: list[float] = []

    def _fn(ctx) -> dict[str, Any]:
        # Compute current mean abs correlation
        market_ids = []
        returns_matrix = []
        price_histories: dict[str, list[float]] = {}

        for name in feed_names:
            feed = ctx.feeds.get(name)
            if not feed or feed.price <= 0:
                continue
            hist = price_histories.setdefault(name, [])
            hist.append(feed.price)
            if len(hist) >= 3:
                returns = [hist[i] / hist[i-1] - 1 for i in range(1, len(hist))]
                returns_matrix.append(returns)
                market_ids.append(name)

        if len(market_ids) >= 2:
            entries = live_correlation_matrix(market_ids, returns_matrix)
            mean_corr = sum(abs(e.correlation) for e in entries) / max(len(entries), 1)
            corr_history.append(mean_corr)
            if len(corr_history) > lookback + recent_window:
                corr_history.pop(0)

        if len(corr_history) < lookback:
            return {"shifted": False, "confidence": 0.0}

        hist_slice = corr_history[:lookback]
        recent_slice = corr_history[-recent_window:]
        result = detect_regime_shift(hist_slice, recent_slice, threshold)

        return {
            "shifted": result.shifted,
            "confidence": result.confidence,
            "old_mean": result.old_mean_corr,
            "new_mean": result.new_mean_corr,
            "change": result.change_magnitude,
        }

    _fn.__name__ = "regime_shift"
    return _fn


def contagion_alert(
    feed_names: list[str],
    correlation_threshold: float = 0.5,
    window: int = 50,
) -> Callable:
    """Monitor for contagion risk across markets."""
    from horizon._horizon import auth_require_ultra
    auth_require_ultra()

    price_histories: dict[str, list[float]] = {}

    def _fn(ctx) -> dict[str, Any]:
        market_ids = []
        returns_matrix = []
        price_changes = []

        for name in feed_names:
            feed = ctx.feeds.get(name)
            if not feed or feed.price <= 0:
                continue

            hist = price_histories.setdefault(name, [])
            hist.append(feed.price)
            if len(hist) > window + 1:
                hist.pop(0)

            if len(hist) >= 3:
                returns = [hist[i] / hist[i-1] - 1 for i in range(1, len(hist))]
                returns_matrix.append(returns)
                market_ids.append(name)
                price_changes.append(returns[-1] if returns else 0.0)

        if len(market_ids) < 2:
            return {"alerts": [], "max_contagion": 0.0}

        entries = live_correlation_matrix(market_ids, returns_matrix)
        alerts = []
        max_score = 0.0

        for source in market_ids:
            corrs = []
            for entry in entries:
                if entry.market_a == source or entry.market_b == source:
                    corrs.append(entry.correlation)
            if not corrs:
                continue

            # Pad to match market_ids length
            full_corrs = [0.0] * len(market_ids)
            idx = 0
            for entry in entries:
                if entry.market_a == source:
                    j = market_ids.index(entry.market_b) if entry.market_b in market_ids else -1
                    if j >= 0:
                        full_corrs[j] = entry.correlation
                elif entry.market_b == source:
                    j = market_ids.index(entry.market_a) if entry.market_a in market_ids else -1
                    if j >= 0:
                        full_corrs[j] = entry.correlation

            alert = contagion_score(
                source, market_ids, full_corrs, price_changes, correlation_threshold
            )
            if alert.contagion_score > 0:
                alerts.append({
                    "source": alert.source_market,
                    "score": alert.contagion_score,
                    "affected": alert.affected_markets,
                })
                max_score = max(max_score, alert.contagion_score)

        return {
            "alerts": alerts,
            "max_contagion": max_score,
            "num_markets": len(market_ids),
        }

    _fn.__name__ = "contagion_alert"
    return _fn


def decorrelation_finder(
    feed_names: list[str],
    top_n: int = 5,
    window: int = 50,
) -> Callable:
    """Find most decorrelated markets for diversification."""
    from horizon._horizon import auth_require_ultra
    auth_require_ultra()

    price_histories: dict[str, list[float]] = {}

    def _fn(ctx) -> dict[str, Any]:
        market_ids = []
        returns_matrix = []

        for name in feed_names:
            feed = ctx.feeds.get(name)
            if not feed or feed.price <= 0:
                continue

            hist = price_histories.setdefault(name, [])
            hist.append(feed.price)
            if len(hist) > window + 1:
                hist.pop(0)

            if len(hist) >= 3:
                returns = [hist[i] / hist[i-1] - 1 for i in range(1, len(hist))]
                returns_matrix.append(returns)
                market_ids.append(name)

        if len(market_ids) < 2:
            return {"decorrelated": [], "num_analyzed": 0}

        results = find_decorrelated(market_ids, returns_matrix, top_n)
        decorr = [
            {"market": r.market_id, "avg_corr": r.avg_correlation, "div_score": r.diversification_score}
            for r in results
        ]

        return {
            "decorrelated": decorr,
            "num_analyzed": len(market_ids),
        }

    _fn.__name__ = "decorrelation_finder"
    return _fn
