#!/usr/bin/env python3
"""
Berechnet den Markttrend für SPY/QQQ und schreibt:
- EMA200 / Distanz / Position vs EMA
- Serien für SPY/QQQ (close-Werte)
- Datenqualitäts-Infos (letzter Balken + Status) nach data/market/market.json
"""

import json
from pathlib import Path
from datetime import datetime, timezone


ROOT = Path(__file__).resolve().parents[1]
MARKET_DIR = ROOT / "data" / "market"
MARKET_DIR.mkdir(parents=True, exist_ok=True)
OUT_FILE = MARKET_DIR / "market.json"


def load_series_from_file(filename):
    """
    Lädt Kursdaten aus data/market/<filename> und gibt eine Liste von
    {"t": ISO-UTC-String oder None, "close": float} zurück.
    Versucht, verschiedene typische Formate robust zu erkennen.
    """
    path = MARKET_DIR / filename
    if not path.exists():
        return []

    try:
        with path.open("r", encoding="utf-8") as f:
            data = json.load(f)
    except Exception:
        return []

    # Fall 1: Polygon-Aggs-Format: {"results":[{"c":..., "t":...}, ...]}
    if isinstance(data, dict) and isinstance(data.get("results"), list):
        out = []
        for bar in data["results"]:
            if not isinstance(bar, dict):
                continue
            close = bar.get("c")
            t = bar.get("t")  # Epoch ms
            if close is None:
                continue
            ts_iso = None
            if isinstance(t, (int, float)):
                ts_iso = (
                    datetime.fromtimestamp(t / 1000.0, tz=timezone.utc)
                    .replace(microsecond=0)
                    .isoformat()
                )
            elif isinstance(t, str):
                ts_iso = t
            out.append({"t": ts_iso, "close": float(close)})
        return out

    # Fall 2: Dein aktuelles Format: {"symbol": "...", "daily": [ { "c":..., "t":... }, ... ]}
    if isinstance(data, dict) and isinstance(data.get("daily"), list):
        out = []
        for bar in data["daily"]:
            if not isinstance(bar, dict):
                continue
            close = bar.get("close") or bar.get("c")
            if close is None:
                continue

            t = bar.get("t")
            ts_iso = None
            if isinstance(t, (int, float)):
                ts_iso = (
                    datetime.fromtimestamp(t / 1000.0, tz=timezone.utc)
                    .replace(microsecond=0)
                    .isoformat()
                )
            elif isinstance(t, str):
                ts_iso = t

            out.append({"t": ts_iso, "close": float(close)})
        return out

    # Fall 3: einfache Liste von Balken: [{"close":..., "t":...} ...] oder mit "date"
    if isinstance(data, list):
        out = []
        for bar in data:
            if not isinstance(bar, dict):
                continue
            close = bar.get("close") or bar.get("c")
            if close is None:
                continue

            ts_iso = None
            if "t" in bar:
                t = bar["t"]
                if isinstance(t, (int, float)):
                    ts_iso = (
                        datetime.fromtimestamp(t / 1000.0, tz=timezone.utc)
                        .replace(microsecond=0)
                        .isoformat()
                    )
                elif isinstance(t, str):
                    ts_iso = t
            elif "date" in bar:
                # z.B. "2025-11-20" -> 2025-11-20T00:00:00Z
                try:
                    d = datetime.fromisoformat(bar["date"])
                    ts_iso = d.replace(tzinfo=timezone.utc, microsecond=0).isoformat()
                except Exception:
                    ts_iso = None

            out.append({"t": ts_iso, "close": float(close)})
        return out

    # Unbekanntes Format -> leere Liste
    return []


def calc_ema(values, period=200):
    """Einfache EMA-Berechnung über eine Liste von Zahlen."""
    if not values:
        return []

    k = 2.0 / (period + 1)
    ema_vals = []
    ema_val = values[0]
    for price in values:
        ema_val = price * k + ema_val * (1 - k)
        ema_vals.append(ema_val)
    return ema_vals


def classify_history_status(last_ts_iso):
    """
    Gibt (normierter_timestamp_iso, status) zurück.
    Status:
      - "aktuell"   -> Daten max. 2 Tage alt
      - "veraltet"  -> älter als 2 Tage
      - "unbekannt" -> Parsing nicht möglich
    """
    if not last_ts_iso:
        return None, "unbekannt"

    ts_str = str(last_ts_iso).replace("Z", "")
    try:
        dt = datetime.fromisoformat(ts_str)
        if dt.tzinfo is None:
            dt = dt.replace(tzinfo=timezone.utc)
        dt_norm = dt.astimezone(timezone.utc).replace(microsecond=0)
    except Exception:
        return last_ts_iso, "unbekannt"

    now_utc = datetime.now(timezone.utc)
    age_days = (now_utc - dt_norm).total_seconds() / 86400.0

    if age_days <= 2:
        status = "aktuell"
    else:
        status = "veraltet"

    return dt_norm.isoformat(), status


def main():
    spy_series = load_series_from_file("spy.json")
    qqq_series = load_series_from_file("qqq.json")

    now_utc = datetime.now(timezone.utc).replace(microsecond=0)
    trend_last_run_utc = now_utc.isoformat()

    if not spy_series:
        # Minimaler Fallback, damit Datei existiert
        payload = {
            "symbol": "SPY",
            "market_trend": "Unbekannt",
            "ema200": None,
            "last_close": None,
            "distance_pct": 0.0,
            "position_vs_ema": "nahe",
            "timestamp_utc": trend_last_run_utc,
            "mtf": {
                "D1": "Unbekannt",
                "H1": "Unbekannt",
                "M15": "Unbekannt",
            },
            "series": {
                "spy_daily": [],
                "qqq_daily": [],
            },
            "data_quality": {
                "spy_last_bar_utc": None,
                "qqq_last_bar_utc": None,
                "spy_history_status": "unbekannt",
                "qqq_history_status": "unbekannt",
                "trend_last_run_utc": trend_last_run_utc,
                "trend_status": "unbekannt",
            },
        }
        OUT_FILE.write_text(json.dumps(payload, indent=2), encoding="utf-8")
        print("[WARN] Markttrend: Keine SPY-Daten gefunden, minimaler Output geschrieben.")
        return

    # EMA200 + Distanz auf SPY
    spy_closes = [b["close"] for b in spy_series]
    ema_vals = calc_ema(spy_closes, period=200)
    ema200 = ema_vals[-1] if ema_vals else spy_closes[-1]
    last_close = spy_closes[-1]

    distance_pct = (last_close - ema200) / ema200 * 100.0 if ema200 else 0.0
    if distance_pct > 1:
        position_vs_ema = "oberhalb"
        market_trend = "Aufwärts"
    elif distance_pct < -1:
        position_vs_ema = "unterhalb"
        market_trend = "Abwärts"
    else:
        position_vs_ema = "nahe"
        market_trend = "Seitwärts"

    spy_last_ts = spy_series[-1].get("t") if spy_series else None
    qqq_last_ts = qqq_series[-1].get("t") if qqq_series else None

    spy_last_norm, spy_status = classify_history_status(spy_last_ts)
    qqq_last_norm, qqq_status = classify_history_status(qqq_last_ts)

    payload = {
        "symbol": "SPY",
        "market_trend": market_trend,
        "ema200": ema200,
        "last_close": last_close,
        "distance_pct": distance_pct,
        "position_vs_ema": position_vs_ema,
        "timestamp_utc": spy_last_norm or trend_last_run_utc,
        "mtf": {
            "D1": market_trend,
            "H1": market_trend,
            "M15": market_trend,
        },
        "series": {
            "spy_daily": spy_series,
            "qqq_daily": qqq_series,
        },
        "data_quality": {
            "spy_last_bar_utc": spy_last_norm,
            "qqq_last_bar_utc": qqq_last_norm,
            "spy_history_status": spy_status,
            "qqq_history_status": qqq_status,
            "trend_last_run_utc": trend_last_run_utc,
            "trend_status": "aktuell",
        },
    }

    OUT_FILE.write_text(json.dumps(payload, indent=2), encoding="utf-8")
    print("[OK] Markttrend aktualisiert - {}".format(trend_last_run_utc))


if __name__ == "__main__":
    main()
