#!/usr/bin/env python3
from __future__ import annotations

import json
import subprocess
from datetime import datetime, timezone
from decimal import Decimal, InvalidOperation
from pathlib import Path

PROJECT = Path('/home/agent/projects/CryptoTradingBot/Crypto_Agent')
BASELINE_FILE = PROJECT / 'runtime/live/tiny_autonomous_live/baseline_usdc.txt'
LEARNINGS_FILE = PROJECT / 'runtime/reports/tiny_live_learnings.md'
DEFAULT_BASELINE = Decimal('77.720946')


def D(x, default='0') -> Decimal:
    try:
        return Decimal(str(x))
    except (InvalidOperation, TypeError, ValueError):
        return Decimal(default)


def load_baseline() -> Decimal:
    try:
        txt = BASELINE_FILE.read_text(encoding='utf-8').strip()
        if txt:
            return D(txt, str(DEFAULT_BASELINE))
    except FileNotFoundError:
        pass
    BASELINE_FILE.parent.mkdir(parents=True, exist_ok=True)
    BASELINE_FILE.write_text(str(DEFAULT_BASELINE), encoding='utf-8')
    return DEFAULT_BASELINE


def run_reconcile() -> dict:
    cmd = ['.venv/bin/python', '-m', 'src.tools.hl_reconcile_watchdog', '--json']
    out = subprocess.check_output(cmd, cwd=PROJECT, text=True, timeout=120)
    return json.loads(out)


def runtime_running() -> str:
    if (PROJECT / 'runtime/KILL_SWITCH').exists():
        return 'pausiert (Kill-Switch aktiv)'
    pid_file = PROJECT / 'runtime/live/tiny_autonomous_live/runtime.pid'
    try:
        pid = pid_file.read_text(encoding='utf-8').strip()
        if pid and Path('/proc', pid).exists():
            return f'läuft (PID {pid})'
    except Exception:
        pass
    return 'unklar/nicht laufend'


def shadow_status() -> str:
    state_path = PROJECT / 'runtime/experiments/market_regime_shadow/state.json'
    pid_path = PROJECT / 'runtime/experiments/market_regime_shadow/bot.pid'
    try:
        state = json.loads(state_path.read_text(encoding='utf-8')) if state_path.exists() else {}
        pid = pid_path.read_text(encoding='utf-8').strip() if pid_path.exists() else ''
        running = pid and Path('/proc', pid).exists()
        closed = int(state.get('closed_trades') or 0)
        wins = int(state.get('wins') or 0)
        losses = int(state.get('losses') or 0)
        pnl = D(state.get('realized_pnl'))
        pos = state.get('positions') or {}
        return f"{'läuft' if running else 'nicht laufend'}; closed={closed}, W/L={wins}/{losses}, realized={fmt(pnl)} USDC, paper_pos={len(pos)}"
    except Exception:
        return 'unklar'


def pct(a: Decimal, b: Decimal) -> Decimal:
    return Decimal('0') if b == 0 else (a / b * Decimal('100'))


def fmt(x: Decimal, n=3) -> str:
    q = Decimal(10) ** -n
    return str(x.quantize(q))


def append_learning(now: str, net: Decimal, open_pnl: Decimal, positions: dict, stops_missing: int, block: bool) -> str:
    PROJECT.joinpath('runtime/reports').mkdir(parents=True, exist_ok=True)
    if stops_missing:
        learning = 'Sicherheitsproblem: Stop fehlt -> keine Optimierung, zuerst Schutz reparieren.'
    elif block:
        learning = 'Reconcile blockiert neue Entries -> Ursache prüfen, keine Risikoerhöhung.'
    elif net < 0 and open_pnl < 0:
        learning = 'Netto und offene Trades negativ -> Entry-Filter strenger, weniger schwache Breakouts akzeptieren.'
    elif net < 0 and open_pnl > 0:
        learning = 'Offene Trades positiv, netto noch negativ -> Gewinne schneller absichern/Trailing weiter priorisieren.'
    elif net > 0:
        learning = 'Netto positiv -> Gewinner laufen lassen, Stops weiter nachziehen, keine Positionsgrössen-Erhöhung ohne Stabilität.'
    else:
        learning = 'Neutral -> Setup-Qualität weiter beobachten.'
    coins = ','.join(sorted(positions)) or 'none'
    line = f'- {now}: net={fmt(net)} USDC, open_pnl={fmt(open_pnl)} USDC, coins={coins}. Learning: {learning}\n'
    with LEARNINGS_FILE.open('a', encoding='utf-8') as f:
        if LEARNINGS_FILE.stat().st_size == 0:
            f.write('# Tiny Live Learnings\n\n')
        f.write(line)
    return learning


def main() -> int:
    now = datetime.now(timezone.utc).astimezone().strftime('%Y-%m-%d %H:%M')
    try:
        data = run_reconcile()
    except Exception as exc:
        print(f'Crypto Live Kurzbericht: Reconcile konnte nicht gelesen werden ({type(exc).__name__}). Bitte prüfen.')
        return 1
    rec = data.get('reconcile', {})
    w = data.get('watchdog', {})
    positions = rec.get('positions', {}) or {}
    baseline = load_baseline()
    free = D(w.get('free_usdc') or rec.get('free_usdc'))
    equity = D(w.get('equity') or rec.get('equity'))
    total = free + equity
    net = total - baseline
    net_pct = pct(net, baseline)
    open_pnl = sum((D(p.get('unrealizedPnl')) for p in positions.values()), Decimal('0'))
    stops_missing = int(w.get('stops_missing_count') or rec.get('stops_missing_count') or 0)
    block = bool(w.get('block_new_entries'))
    learning = append_learning(now, net, open_pnl, positions, stops_missing, block)

    lines = []
    lines.append('Crypto Live Kurzbericht')
    lines.append(f'- Runtime: {runtime_running()}')
    lines.append(f'- Paper/Shadow: {shadow_status()}')
    lines.append(f'- Reconcile: {w.get("status", rec.get("status", "unknown"))}; Stops fehlen: {stops_missing}; neue Entries blockiert: {str(block).lower()}')
    lines.append(f'- Account seit Start: {fmt(net)} USDC ({fmt(net_pct, 2)}%)')
    lines.append(f'- Offenes PnL: {fmt(open_pnl)} USDC')
    if positions:
        pos_bits = []
        for coin, p in sorted(positions.items()):
            pos_bits.append(f'{coin} {fmt(D(p.get("unrealizedPnl")))} USDC')
        lines.append('- Trades: ' + '; '.join(pos_bits))
    else:
        lines.append('- Trades: keine offenen Positionen')
    lines.append(f'- Learning: {learning}')
    lines.append('- Optimierung: Learnings werden dokumentiert; Live-Risiko bleibt durch Stop/Reconcile/Loss-Gates begrenzt. Max Hebel-Cap: 5x.')
    print('\n'.join(lines))
    return 0


if __name__ == '__main__':
    raise SystemExit(main())
