"""Structured explanations for fund state, decisions, and risk."""

from __future__ import annotations

import logging
import time
from typing import Any

logger = logging.getLogger("horizon.fund.explainability")


class Explainer:
    """Generates structured explanations for fund state, strategy performance,
    decision rationale, and risk posture.

    All methods return plain dicts suitable for MCP tool JSON responses.
    The Explainer is stateless -- it reads from the fund's subsystems on
    each call.
    """

    def explain_fund_state(self, fund: Any) -> dict[str, Any]:
        """Full fund state explanation.

        Returns sections: overview, regime, risk, strategies, decisions, alerts.
        """
        result: dict[str, Any] = {"timestamp": time.time()}

        # Overview
        try:
            status = fund.status()
            result["overview"] = status
        except Exception as e:
            result["overview"] = {"error": str(e)}

        # Regime
        try:
            regime_snap = fund.regime_detector.current_regime()
            if regime_snap is not None:
                result["regime"] = {
                    "current": regime_snap.regime,
                    "confidence": round(regime_snap.confidence, 3),
                    "volatility": round(regime_snap.volatility, 4),
                    "trend_strength": round(regime_snap.trend_strength, 4),
                    "mean_reversion_score": round(regime_snap.mean_reversion_score, 4),
                }
                history = fund.regime_detector.regime_history(limit=5)
                result["regime"]["recent_transitions"] = [
                    {"regime": h.regime, "confidence": round(h.confidence, 3)}
                    for h in history
                ]
            else:
                result["regime"] = {"current": "unknown", "confidence": 0.0}
        except Exception:
            result["regime"] = {"current": "unknown"}

        # Risk summary
        try:
            result["risk"] = fund.risk_dashboard()
        except Exception as e:
            result["risk"] = {"error": str(e)}

        # Strategies
        try:
            statuses = fund.controller.all_statuses()
            result["strategies"] = [
                {
                    "name": s.name,
                    "state": s.state.value,
                    "pnl": round(s.pnl, 2),
                    "drawdown_pct": round(s.drawdown_pct, 2),
                    "allocated_capital": round(s.allocated_capital, 2),
                    "uptime_days": round(s.uptime_secs / 86400, 1),
                    "open_orders": s.open_orders,
                    "active_positions": s.active_positions,
                }
                for s in statuses
            ]
        except Exception as e:
            result["strategies"] = {"error": str(e)}

        try:
            agent = getattr(fund, "_agent", None)
            if agent is not None:
                decisions = agent.decision_log(limit=10)
                result["recent_decisions"] = [
                    {
                        "id": d.decision_id,
                        "action": d.action.value,
                        "confidence": round(d.confidence, 3),
                        "outcome": d.outcome,
                        "reasoning": d.reasoning[:200],
                    }
                    for d in decisions
                ]
            else:
                result["recent_decisions"] = []
        except Exception:
            result["recent_decisions"] = []

        # Recent alerts
        try:
            alerts = fund.alert_manager.recent_alerts(limit=10)
            result["recent_alerts"] = [a.to_dict() for a in alerts]
        except Exception:
            result["recent_alerts"] = []

        # Hypotheses
        try:
            hypotheses = fund.hypothesis_manager.active_hypotheses()
            result["active_hypotheses"] = [
                {
                    "id": h.hypothesis_id,
                    "market": h.market_id,
                    "state": h.state.value,
                    "confidence": round(h.confidence, 3),
                    "edge": round(h.edge_estimate, 4),
                    "direction": h.direction,
                }
                for h in hypotheses[:10]
            ]
        except Exception:
            result["active_hypotheses"] = []

        # Alpha model
        try:
            factors = fund.alpha_model.factor_report()
            result["alpha_factors"] = [
                {
                    "name": f.name,
                    "ic": round(f.ic, 4),
                    "ic_ir": round(f.ic_ir, 4),
                    "significant": f.is_significant,
                }
                for f in factors
            ]
        except Exception:
            result["alpha_factors"] = []

        return result

    def explain_strategy(self, fund: Any, name: str) -> dict[str, Any]:
        """Detailed explanation of a single strategy."""
        result: dict[str, Any] = {"strategy_name": name, "timestamp": time.time()}

        # Performance
        try:
            status = fund.controller.status(name)
            result["performance"] = {
                "state": status.state.value,
                "pnl": round(status.pnl, 2),
                "drawdown_pct": round(status.drawdown_pct, 2),
                "allocated_capital": round(status.allocated_capital, 2),
                "current_nav": round(status.current_nav, 2),
                "uptime_days": round(status.uptime_secs / 86400, 1),
                "open_orders": status.open_orders,
                "active_positions": status.active_positions,
                "roi_pct": round(status.pnl / max(status.allocated_capital, 1) * 100, 2),
            }
        except Exception as e:
            result["performance"] = {"error": str(e)}

        # Execution quality
        try:
            metrics = fund.exec_quality.strategy_metrics(name)
            result["execution"] = metrics
        except Exception:
            result["execution"] = {}

        # Alpha decay
        try:
            profile = fund.alpha_decay.fit_decay(name)
            if profile is not None:
                result["alpha_decay"] = {
                    "current_edge": round(profile.current_edge, 4),
                    "initial_edge": round(profile.initial_edge, 4),
                    "half_life_days": round(profile.half_life_days, 1) if profile.half_life_days else None,
                    "edge_type": profile.edge_type,
                    "r_squared": round(profile.r_squared, 3),
                }
            else:
                result["alpha_decay"] = {"status": "insufficient_data"}
        except Exception:
            result["alpha_decay"] = {}

        # Promotion stage
        try:
            if fund.promotion_manager is not None:
                stage = fund.promotion_manager.stage(name)
                result["promotion"] = {"stage": stage.value}
            else:
                result["promotion"] = {"stage": "not_tracked"}
        except Exception:
            result["promotion"] = {}

        # Related hypotheses
        try:
            hypotheses = fund.hypothesis_manager.active_hypotheses()
            related = [h for h in hypotheses if name in (h.market_id or "")]
            result["hypotheses"] = [
                {
                    "id": h.hypothesis_id,
                    "state": h.state.value,
                    "confidence": round(h.confidence, 3),
                    "edge": round(h.edge_estimate, 4),
                }
                for h in related[:5]
            ]
        except Exception:
            result["hypotheses"] = []

        return result

    def explain_decision(self, decision: Any) -> dict[str, Any]:
        """Explain a specific decision's rationale and guardrails."""
        result: dict[str, Any] = {
            "decision_id": decision.decision_id,
            "timestamp": decision.timestamp,
        }

        result["what"] = {
            "action": decision.action.value if hasattr(decision.action, "value") else str(decision.action),
            "parameters": decision.parameters,
        }

        result["why"] = {
            "reasoning": decision.reasoning,
            "confidence": round(decision.confidence, 3),
        }

        result["outcome"] = {
            "status": decision.outcome,
            "error": decision.error,
            "execution_time_ms": decision.execution_time_ms,
        }

        result["guardrails"] = {
            "confidence_met": decision.outcome != "escalated" or decision.error is None,
            "rate_limited": "rate limit" in (decision.error or ""),
            "mode_restriction": decision.outcome == "pending",
        }

        return result

    def explain_risk(self, fund: Any) -> dict[str, Any]:
        """Full risk analysis explanation."""
        result: dict[str, Any] = {"timestamp": time.time()}

        # Tail risk
        try:
            tail = fund.risk_analytics.tail_risk()
            if tail is not None:
                result["tail_risk"] = {
                    "historical_var": round(tail.historical_var, 4),
                    "historical_cvar": round(tail.historical_cvar, 4),
                    "cf_var": round(tail.cf_var, 4),
                    "cf_cvar": round(tail.cf_cvar, 4),
                    "skewness": round(tail.skewness, 3),
                    "kurtosis": round(tail.kurtosis, 3),
                    "tail_ratio": round(tail.tail_ratio, 3),
                }
            else:
                result["tail_risk"] = {"status": "insufficient_data"}
        except Exception:
            result["tail_risk"] = {}

        # Portfolio Greeks
        try:
            greeks = fund.risk_analytics._greeks_cache
            if greeks is not None:
                result["greeks"] = {
                    "total_delta": round(greeks.total_delta, 4),
                    "total_gamma": round(greeks.total_gamma, 4),
                    "total_theta": round(greeks.total_theta, 4),
                    "total_vega": round(greeks.total_vega, 4),
                    "per_strategy": greeks.per_strategy,
                }
            else:
                result["greeks"] = {"status": "not_computed"}
        except Exception:
            result["greeks"] = {}

        # Correlations
        try:
            matrix = fund.correlation_monitor.correlation_matrix()
            result["correlations"] = matrix
        except Exception:
            result["correlations"] = {}

        # VaR budget
        try:
            var_snapshot = fund.var_budget.snapshot()
            result["var_budget"] = var_snapshot
        except Exception:
            result["var_budget"] = {}

        # Stress test results
        try:
            stress_result = fund.stress_test()
            result["stress_test"] = stress_result.summary()
        except Exception:
            result["stress_test"] = {}

        # Dynamic limits
        try:
            regime_snap = fund.regime_detector.current_regime()
            if regime_snap is not None:
                limits = fund.risk_analytics.dynamic_limits(regime_snap.regime)
                result["dynamic_limits"] = {
                    "position_scale": limits.position_scale,
                    "max_position_pct": round(limits.max_position_pct, 4),
                    "var_limit": round(limits.var_limit, 4),
                    "spread_multiplier": limits.spread_multiplier,
                    "regime": limits.regime,
                }
        except Exception:
            pass

        # Performance attribution
        try:
            snaps = fund.perf_attribution.recent_snapshots(limit=1)
            if snaps:
                snap = snaps[-1]
                result["attribution"] = {
                    "alpha": round(snap.alpha_beta.alpha, 4),
                    "beta": round(snap.alpha_beta.beta, 3),
                    "r_squared": round(snap.alpha_beta.r_squared, 3),
                    "information_ratio": round(snap.alpha_beta.information_ratio, 3),
                    "cost_pct": round(snap.cost.cost_pct, 4),
                }
        except Exception:
            pass

        return result
