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

import json
import fcntl
import os
import subprocess
import sys
from pathlib import Path

sys.path.insert(0, '/home/agent/projects/CryptoTradingBot/Crypto_Agent')
from src.tools.paper_runtime_supervisor import CANDIDATES, inspect_candidate

REPO = Path('/home/agent/projects/CryptoTradingBot/Crypto_Agent')
PYTHON = REPO / '.venv/bin/python'
RUNTIME_ROOT = REPO / 'runtime/experiments'
LOG_ROOT = Path('/home/agent/.local/state/CryptoTradingBot/v77_logs')
MARKER_PATH = Path('/home/agent/.local/state/CryptoTradingBot/v77_supervisor_markers.json')
LOCK_PATH = Path('/home/agent/.local/state/CryptoTradingBot/v77_supervisor.lock')
STRATEGIES = (
    'candidate_v77_trend_retest_anti_chase_long',
    'research_v77_bear_trend_retest_short',
    'research_v77_2_relative_strength_momentum_continuation',
)


def process_is_safe(strategy_id: str) -> bool:
    pid_path = RUNTIME_ROOT / strategy_id / 'bot.pid'
    try:
        pid = int(pid_path.read_text(encoding='utf-8').strip())
        proc = Path('/proc') / str(pid)
        if not proc.exists():
            return False
        cmd = (proc / 'cmdline').read_bytes().decode('utf-8', 'ignore')
        env = (proc / 'environ').read_bytes().decode('utf-8', 'ignore')
    except Exception:
        return False
    return (
        'src.tools.v77_trend_retest_runtime' in cmd
        and f'--strategy-id\x00{strategy_id}' in cmd
        and f'CTB_STRATEGY_ID={strategy_id}' in env
        and 'CTB_PAPER_TRADING=true' in env
        and 'CTB_LIVE_TRADING_ALLOWED=true' not in env
        and 'CTB_LIVE_ORDER_ALLOWED=true' not in env
        and 'HL_MAINNET_SIGNED_ACTION=true' not in env
    )


def start_strategy(strategy_id: str) -> int:
    LOG_ROOT.mkdir(parents=True, exist_ok=True)
    log_path = LOG_ROOT / f'{strategy_id}.log'
    env = os.environ.copy()
    env.update({
        'PYTHONPATH': '.',
        'CTB_STRATEGY_ID': strategy_id,
        'CTB_PAPER_TRADING': 'true',
        'CTB_DRY_RUN': 'false',
        'CTB_LIVE_TRADING_ALLOWED': 'false',
        'CTB_LIVE_ORDER_ALLOWED': 'false',
        'HL_MAINNET_SIGNED_ACTION': 'false',
    })
    with log_path.open('ab') as log:
        proc = subprocess.Popen(
            [
                str(PYTHON), '-m', 'src.tools.v77_trend_retest_runtime',
                '--strategy-id', strategy_id,
                '--coins', ','.join(CANDIDATES[strategy_id].coins),
                '--iterations', '720',
                '--interval-seconds', '60',
                '--json',
            ],
            cwd=REPO,
            env=env,
            stdout=log,
            stderr=subprocess.STDOUT,
            start_new_session=True,
        )
    return proc.pid


def refresh_promotion_report(strategy_id: str) -> dict:
    runtime = RUNTIME_ROOT / strategy_id
    proc = subprocess.run(
        [str(PYTHON), '-m', 'src.tools.v77_promotion_report', '--runtime-dir', str(runtime), '--strategy-version', CANDIDATES[strategy_id].strategy_version, '--json'],
        cwd=REPO,
        env={**os.environ, 'PYTHONPATH': '.'},
        text=True,
        capture_output=True,
        timeout=120,
    )
    if proc.returncode != 0:
        raise RuntimeError((proc.stderr or proc.stdout)[-500:])
    return json.loads(proc.stdout)


def refresh_signal_funnel(strategy_id: str) -> dict:
    runtime = RUNTIME_ROOT / strategy_id
    proc = subprocess.run(
        [str(PYTHON), '-m', 'src.tools.v77_signal_funnel', '--runtime-dir', str(runtime), '--strategy-version', CANDIDATES[strategy_id].strategy_version, '--json'],
        cwd=REPO,
        env={**os.environ, 'PYTHONPATH': '.'},
        text=True,
        capture_output=True,
        timeout=120,
    )
    if proc.returncode != 0:
        raise RuntimeError((proc.stderr or proc.stdout)[-500:])
    return json.loads(proc.stdout)


def load_markers() -> dict[str, bool]:
    try:
        raw = json.loads(MARKER_PATH.read_text(encoding='utf-8'))
        return {str(key): bool(value) for key, value in raw.items()} if isinstance(raw, dict) else {}
    except Exception:
        return {}


def save_markers(markers: dict[str, bool]) -> None:
    MARKER_PATH.parent.mkdir(parents=True, exist_ok=True)
    MARKER_PATH.write_text(json.dumps(markers, indent=2, sort_keys=True), encoding='utf-8')


def main() -> int:
    LOCK_PATH.parent.mkdir(parents=True, exist_ok=True)
    lock = LOCK_PATH.open('w')
    try:
        fcntl.flock(lock, fcntl.LOCK_EX | fcntl.LOCK_NB)
    except BlockingIOError:
        return 0
    messages: list[str] = []
    markers = load_markers()
    for strategy_id in STRATEGIES:
        assessment = inspect_candidate(CANDIDATES[strategy_id], runtime_root=RUNTIME_ROOT)
        if assessment.restart_allowed:
            pid = start_strategy(strategy_id)
            messages.append(f'⚙️ v77 Paper-Supervisor startete {strategy_id} neu (PID {pid}); paper=true, live=false, signed=false.')
        elif assessment.state != 'healthy':
            messages.append(f'⚠️ v77 Prozesslage {strategy_id}: {assessment.state}; kein blinder Neustart, blocker={",".join(assessment.blockers[:4])}.')
        try:
            refresh_signal_funnel(strategy_id)
        except Exception as exc:
            messages.append(f'⚠️ v77 Signal-Funnel fehlgeschlagen: {strategy_id}: {type(exc).__name__}')
        try:
            report = refresh_promotion_report(strategy_id)
        except Exception as exc:
            messages.append(f'⚠️ v77 Promotion-Report fehlgeschlagen: {strategy_id}: {type(exc).__name__}')
            continue
        eligible = report.get('promotion_eligible') is True
        if eligible and not markers.get(strategy_id):
            # This is deliberately only a one-shot manual proposal alert; never an execution grant.
            messages.append(
                f'🟢 v77 erreicht quantitative Paper-Gates: {strategy_id}. '
                f"closed={report.get('closed_lifecycles')}, PF={report.get('profit_factor')}, "
                'weiterhin live=false; manueller Preflight und explizites Sir-GO erforderlich.'
            )
        markers[strategy_id] = eligible
    save_markers(markers)
    if messages:
        print('\n'.join(messages))
    return 0


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