from __future__ import annotations

import math
import statistics
from typing import Any


def _as_floats(values: list[Any]) -> list[float]:
    out: list[float] = []
    for v in values:
        out.append(float(v))
    return out


def describe(values: list[Any]) -> dict[str, Any]:
    """n, min, max, mean, median, stdev (sample, n>=2)."""
    xs = _as_floats(values)
    n = len(xs)
    if n == 0:
        return {"ok": False, "error": "empty values"}
    out: dict[str, Any] = {
        "ok": True,
        "n": n,
        "min": min(xs),
        "max": max(xs),
        "mean": statistics.fmean(xs),
        "median": statistics.median(xs),
    }
    if n >= 2:
        out["stdev"] = statistics.stdev(xs)
    else:
        out["stdev"] = None
    return out


def percentile(values: list[Any], p: float) -> dict[str, Any]:
    """Linear interpolation percentile p in [0,100]."""
    if not 0 <= p <= 100:
        return {"ok": False, "error": "p must be in [0,100]"}
    xs = sorted(_as_floats(values))
    n = len(xs)
    if n == 0:
        return {"ok": False, "error": "empty values"}
    if n == 1:
        return {"ok": True, "percentile": p, "value": xs[0]}
    k = (p / 100) * (n - 1)
    f = math.floor(k)
    c = math.ceil(k)
    if f == c:
        return {"ok": True, "percentile": p, "value": xs[int(k)]}
    d0 = xs[f] * (c - k)
    d1 = xs[c] * (k - f)
    return {"ok": True, "percentile": p, "value": d0 + d1}


def histogram(values: list[Any], bins: int = 10) -> dict[str, Any]:
    """Equal-width histogram; bins >= 1."""
    xs = _as_floats(values)
    n = len(xs)
    if n == 0:
        return {"ok": False, "error": "empty values"}
    b = max(1, int(bins))
    lo, hi = min(xs), max(xs)
    if lo == hi:
        return {
            "ok": True,
            "bin_low": [lo],
            "bin_high": [hi],
            "counts": [n],
            "n_bins": 1,
        }
    width = (hi - lo) / b
    counts = [0] * b
    lows = [lo + i * width for i in range(b)]
    highs = [lo + (i + 1) * width for i in range(b)]
    highs[-1] = hi
    for x in xs:
        if x >= hi:
            counts[-1] += 1
            continue
        idx = min(int((x - lo) / width), b - 1)
        counts[idx] += 1
    return {
        "ok": True,
        "bin_low": lows,
        "bin_high": highs,
        "counts": counts,
        "n_bins": b,
    }


def correlation_pearson(x: list[Any], y: list[Any]) -> dict[str, Any]:
    """Pearson r; requires equal length >= 2."""
    a = _as_floats(x)
    b = _as_floats(y)
    if len(a) != len(b):
        return {"ok": False, "error": "x and y length mismatch"}
    if len(a) < 2:
        return {"ok": False, "error": "need at least 2 pairs"}
    try:
        r = statistics.correlation(a, b)
    except statistics.StatisticsError as e:
        return {"ok": False, "error": str(e)}
    return {"ok": True, "n": len(a), "pearson_r": r}


def run_operation(operation: str, payload: dict[str, Any]) -> dict[str, Any]:
    op = operation.strip().lower()
    if op == "describe":
        return describe(payload.get("values") or [])
    if op == "percentile":
        return percentile(payload.get("values") or [], float(payload.get("p", 50)))
    if op == "histogram":
        return histogram(payload.get("values") or [], int(payload.get("bins", 10)))
    if op == "correlation":
        return correlation_pearson(payload.get("x") or [], payload.get("y") or [])
    return {"ok": False, "error": f"unknown operation: {operation}"}
