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

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

RUNTIME = Path('/home/agent/.local/state/CryptoTradingBot/copy_research')
RUN_DIR = RUNTIME / 'reports' / 'observation_runs'


def load_latest_status() -> tuple[Path | None, dict]:
    files = sorted(RUN_DIR.glob('*_7d_status.json'), key=lambda p: p.stat().st_mtime, reverse=True)
    if not files:
        return None, {}
    path = files[0]
    try:
        return path, json.loads(path.read_text(encoding='utf-8'))
    except Exception as exc:
        return path, {'run_status': 'unreadable', 'error': str(exc)}


def process_running() -> bool:
    try:
        out = subprocess.check_output(['pgrep', '-af', 'ctb_copy_observation_7d.sh'], text=True)
    except subprocess.CalledProcessError:
        return False
    lines = [ln for ln in out.splitlines() if 'pgrep -af' not in ln and 'ctb_copy_observation_7d.sh' in ln]
    return bool(lines)


def main() -> int:
    path, status = load_latest_status()
    now = datetime.now(timezone.utc)
    if not path:
        print('⚠️ Copy Observation 7d: kein Statusfile gefunden. Bitte Runner prüfen.')
        return 0

    run_status = status.get('run_status', 'unknown')
    cycles = int(status.get('cycles_completed') or 0)
    expected = int(status.get('expected_cycles_total') or 84)
    last_cycle_raw = status.get('last_cycle_at')
    stale_hours = None
    if last_cycle_raw:
        try:
            last = datetime.fromisoformat(str(last_cycle_raw).replace('Z', '+00:00'))
            stale_hours = (now - last).total_seconds() / 3600
        except Exception:
            pass
    running = process_running()
    quality = status.get('data_quality_latest', ['unknown'])
    collector = status.get('collector_status_latest', 'unknown')
    errors = status.get('partial_errors_latest') or []

    off_plan = []
    if run_status not in {'running', 'completed'}:
        off_plan.append(f'Status {run_status}')
    if run_status == 'running' and not running:
        off_plan.append('Runner-Prozess nicht gefunden')
    if stale_hours is not None and run_status == 'running' and stale_hours > 4.5:
        off_plan.append(f'letzter Zyklus vor {stale_hours:.1f}h')
    if collector != 'ok':
        off_plan.append(f'Collector {collector}')
    if quality != ['ok']:
        off_plan.append(f'Datenqualität {quality}')
    if errors:
        off_plan.append(f'Partial errors: {errors[:2]}')

    prefix = '✅' if not off_plan else '⚠️'
    print(f'{prefix} Copy Observation 7d Status')
    print(f'- Lauf: {run_status}; Prozess: {"läuft" if running else "nicht gefunden"}')
    print(f'- Fortschritt: {cycles}/{expected} Zyklen ({status.get("progress_pct", 0)}%)')
    print(f'- Letzter Zyklus UTC: {last_cycle_raw or "unknown"}')
    print(f'- Wallets: {status.get("enabled_wallets_count", "?")}')
    print(f'- Snapshots seit Starttag: {status.get("wallet_snapshot_count_total_since_start_day", 0)}')
    print(f'- Shadow-Decisions: {status.get("shadow_decisions_count_since_start_day", 0)}; allowed {status.get("allowed_count_since_start_day", 0)}, blocked {status.get("blocked_count_since_start_day", 0)}, ignored {status.get("ignored_count_since_start_day", 0)}')
    print(f'- Safety: {status.get("safety_status", "unknown")}')
    if off_plan:
        print('- Off-plan: ' + '; '.join(off_plan))
    else:
        print('- Plancheck: läuft nach Plan')
    print(f'- Summary: {status.get("summary_file", "unknown")}')
    return 0

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