"""pattern_miner.py — SQL-based win/loss pattern discovery.

Phase 3: discards high-win-rate feature combinations from trading_brain.db
and computes per-feature importance by bucketing values and comparing win rates.

Usage::

    from src.learning.pattern_miner import mine_win_patterns, compute_feature_importance
    patterns = mine_win_patterns(min_samples=10, min_win_rate=0.55)
    importance = compute_feature_importance()
"""

from __future__ import annotations

import hashlib
import json
from typing import Any

# Features to mine (must match trade_events column names)
_NUMERIC_FEATURES = [
    "confidence", "adx_value", "atr_value", "vol_ratio", "spread",
    "agent_confidence", "strategy_win_rate_20", "strategy_profit_factor",
    "daily_loss_pct",
]

_CATEGORICAL_FEATURES = [
    "side", "regime", "session", "agent_direction", "agent_aligned",
    "macro_authority", "strategy_id", "risk_level",
]

# Bucket edges for numeric features (low / mid / high)
_BUCKETS = {
    "confidence": [(0.0, 0.60), (0.60, 0.80), (0.80, 1.01)],
    "adx_value": [(0.0, 20.0), (20.0, 30.0), (30.0, 100.0)],
    "vol_ratio": [(0.0, 0.85), (0.85, 1.25), (1.25, 10.0)],
    "agent_confidence": [(0.0, 0.55), (0.55, 0.75), (0.75, 1.01)],
    "strategy_win_rate_20": [(0.0, 0.40), (0.40, 0.60), (0.60, 1.01)],
    "strategy_profit_factor": [(0.0, 1.0), (1.0, 2.0), (2.0, 100.0)],
}


def _get_brain():
    try:
        from src.learning.trading_brain import get_brain
        return get_brain()
    except Exception:
        return None


def mine_win_patterns(
    min_samples: int = 10,
    min_win_rate: float = 0.55,
    min_rate_gap: float = 0.15,
) -> list[dict[str, Any]]:
    """Discover high-win-rate feature combinations from the learning brain.

    Strategy: For each categorical feature, find values with win rates
    significantly above baseline. Then combine with numeric bucket conditions
    that further improve win rate.

    Returns list of patterns, each with:
        {conditions, sample_size, win_rate, avg_pnl_atr, confidence}
    """
    brain = _get_brain()
    if brain is None:
        return []

    all_trades = brain.query_recent_trades(1000)
    if len(all_trades) < min_samples * 2:
        return []

    baseline_wr = sum(1 for t in all_trades if t.get("is_win")) / max(1, len(all_trades))
    patterns: list[dict[str, Any]] = []
    seen_hashes: set[str] = set()

    # ── Stage 1: Categorical conditions ──
    for feature in _CATEGORICAL_FEATURES:
        values: set[str] = set()
        for t in all_trades:
            v = str(t.get(feature, "") or "").strip()
            if v:
                values.add(v)

        for val in values:
            if not val:
                continue
            stats = brain.query_win_rate(**{feature: val})
            if stats["n"] >= min_samples and stats["win_rate"] >= min_win_rate:
                if stats["win_rate"] - baseline_wr >= min_rate_gap:
                    conds = [{"feature": feature, "op": "=", "value": val}]
                    ph = _pattern_hash(conds)
                    if ph not in seen_hashes:
                        seen_hashes.add(ph)
                        patterns.append({
                            "conditions": conds,
                            "sample_size": stats["n"],
                            "win_rate": stats["win_rate"],
                            "avg_pnl_atr": stats["avg_pnl_atr"],
                            "confidence": round(stats["win_rate"] * min(stats["n"] / 20.0, 1.0), 4),
                        })

    # ── Stage 2: Pairwise (categorical × categorical) ──
    top_cats = sorted(patterns, key=lambda p: p["win_rate"], reverse=True)[:5]
    for p in top_cats:
        base = p["conditions"][0]
        for feature2 in _CATEGORICAL_FEATURES:
            if feature2 == base["feature"]:
                continue
            values2: set[str] = set()
            for t in all_trades:
                v = str(t.get(feature2, "") or "").strip()
                if v and str(t.get(base["feature"], "") or "").strip() == base["value"]:
                    values2.add(v)
            for val2 in values2:
                if not val2:
                    continue
                stats = brain.query_win_rate(**{base["feature"]: base["value"], feature2: val2})
                if stats["n"] >= min_samples and stats["win_rate"] >= min_win_rate + 0.05:
                    conds = [
                        {"feature": base["feature"], "op": "=", "value": base["value"]},
                        {"feature": feature2, "op": "=", "value": val2},
                    ]
                    ph = _pattern_hash(conds)
                    if ph not in seen_hashes:
                        seen_hashes.add(ph)
                        patterns.append({
                            "conditions": conds,
                            "sample_size": stats["n"],
                            "win_rate": stats["win_rate"],
                            "avg_pnl_atr": stats["avg_pnl_atr"],
                            "confidence": round(stats["win_rate"] * min(stats["n"] / 25.0, 1.0), 4),
                        })

    # ── Write discovered patterns to knowledge base ──
    for p in patterns[:5]:  # top 5 only
        try:
            from src.learning.knowledge_writer import write_pattern_note
            write_pattern_note(p)
        except Exception:
            pass

    return sorted(patterns, key=lambda p: p["win_rate"] * p["confidence"], reverse=True)


def compute_feature_importance() -> list[dict[str, Any]]:
    """Compute per-feature importance by bucketing values and comparing win rates.

    Returns list of {feature, importance_score, buckets: [{range, n, win_rate}]}
    sorted by importance_score descending.
    """
    brain = _get_brain()
    if brain is None:
        return []

    results: list[dict[str, Any]] = []

    for feature in _NUMERIC_FEATURES:
        buckets = _BUCKETS.get(feature)
        if not buckets:
            # Default: 3 equal-width buckets
            buckets = [(0.0, 0.4), (0.4, 0.7), (0.7, 100.0)]

        bucket_stats = []
        wr_values = []
        for low, high in buckets:
            stats = brain.query_win_rate_by_bucket(feature, low, high)
            bucket_stats.append({
                "range": f"{low}-{high}",
                "n": stats["n"],
                "win_rate": stats["win_rate"],
            })
            if stats["n"] >= 3:
                wr_values.append(stats["win_rate"])

        if len(wr_values) >= 2:
            importance = round((max(wr_values) - min(wr_values)) * 100, 1)
        else:
            importance = 0.0

        results.append({
            "feature": feature,
            "importance_score": importance,
            "buckets": bucket_stats,
            "is_significant": importance >= 10.0,
        })

    return sorted(results, key=lambda r: r["importance_score"], reverse=True)


def _pattern_hash(conditions: list[dict[str, Any]]) -> str:
    raw = json.dumps(sorted(conditions, key=lambda c: c["feature"]), sort_keys=True)
    return hashlib.sha256(raw.encode()).hexdigest()[:16]
