"""
VYNDR Evolution Engine — Python Microservice
PELT changepoint detection for player metric evolution.
Port 5001.
"""

import json
import sys
from flask import Flask, request, jsonify

app = Flask(__name__)

# Graceful import — ruptures may not be installed
try:
    import ruptures as rpt
    HAS_RUPTURES = True
except ImportError:
    HAS_RUPTURES = False
    print("[evolution-engine] WARNING: ruptures not installed. Using fallback.", file=sys.stderr)

import numpy as np


def detect_changepoints_pelt(values, min_size=5, penalty=3.0):
    """Use PELT algorithm from ruptures library."""
    if not HAS_RUPTURES:
        return fallback_detect(values)
    
    signal = np.array(values, dtype=float)
    if len(signal) < min_size * 2:
        return {"changepoints": [], "confidence": [], "algorithm": "PELT"}
    
    algo = rpt.Pelt(model="rbf", min_size=min_size).fit(signal)
    result = algo.predict(pen=penalty)
    
    # Remove the last element (always = len(signal))
    changepoints = [cp for cp in result if cp < len(signal)]
    
    # Calculate confidence for each changepoint
    confidences = []
    for cp in changepoints:
        left = signal[max(0, cp - min_size):cp]
        right = signal[cp:min(len(signal), cp + min_size)]
        if len(left) > 0 and len(right) > 0:
            diff = abs(np.mean(right) - np.mean(left))
            std = max(np.std(signal), 0.01)
            conf = min(diff / std, 1.0)
            confidences.append(round(conf, 3))
        else:
            confidences.append(0.0)
    
    return {
        "changepoints": changepoints,
        "confidence": confidences,
        "algorithm": "PELT",
    }


def fallback_detect(values):
    """Simple window-based fallback when ruptures unavailable."""
    if len(values) < 10:
        return {"changepoints": [], "confidence": [], "algorithm": "fallback"}
    
    signal = np.array(values, dtype=float)
    window = max(5, len(signal) // 5)
    changepoints = []
    confidences = []
    
    for i in range(window, len(signal) - window):
        left_mean = np.mean(signal[i - window:i])
        right_mean = np.mean(signal[i:i + window])
        std = max(np.std(signal), 0.01)
        diff = abs(right_mean - left_mean)
        if diff / std > 1.5:
            changepoints.append(i)
            confidences.append(min(round(diff / std / 3.0, 3), 1.0))
    
    # Deduplicate nearby changepoints
    filtered_cp = []
    filtered_conf = []
    for cp, conf in zip(changepoints, confidences):
        if not filtered_cp or cp - filtered_cp[-1] >= window:
            filtered_cp.append(cp)
            filtered_conf.append(conf)
    
    return {
        "changepoints": filtered_cp,
        "confidence": filtered_conf,
        "algorithm": "fallback",
    }


@app.route("/health", methods=["GET"])
def health():
    return jsonify({
        "status": "ok",
        "ruptures_available": HAS_RUPTURES,
    })


@app.route("/detect-changepoints", methods=["POST"])
def detect_changepoints():
    data = request.get_json()
    if not data:
        return jsonify({"error": "JSON body required"}), 400
    
    values = data.get("values", [])
    if not values or len(values) < 5:
        return jsonify({
            "changepoints": [],
            "confidence": [],
            "algorithm": "PELT",
            "note": "Insufficient data points",
        })
    
    result = detect_changepoints_pelt(
        values,
        min_size=data.get("min_size", 5),
        penalty=data.get("penalty", 3.0),
    )
    result["player_id"] = data.get("player_id")
    result["metric"] = data.get("metric")
    
    return jsonify(result)


if __name__ == "__main__":
    print("[evolution-engine] Starting on port 5001...")
    app.run(host="0.0.0.0", port=5001, debug=False)
