# source: https://raw.githubusercontent.com/ikonclast/bob_der_botmeister/da05d31149b2f7577580f600e6bb591310274fe0/scripts/halt_diagnosis.py
#!/usr/bin/env python3
"""
HALT DIAGNOSIS
1. Manifest integrity check
2. Repro anomaly check (1332 vs 387 trades)
3. Buy&Hold baseline benchmark
"""

import json
import subprocess
import zipfile
import hashlib
from pathlib import Path
from datetime import datetime

OUTPUT_DIR = Path("/home/node/.openclaw/workspace/bob_quant/halt_diagnosis")
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)

FREQTRADE_BASE = Path("/opt/docker/freqtrade/shared_data/user_data")
STRATEGIES_DIR = FREQTRADE_BASE / "strategies"
CONFIGS_DIR = FREQTRADE_BASE / "configs"
BACKTEST_BASE = FREQTRADE_BASE / "backtest_results"
DOCKER_ENV = {"DOCKER_HOST": "tcp://socket-proxy:2375"}


def get_file_hash(filepath: Path) -> str:
    """Generate SHA256 hash of file"""
    if not filepath.exists():
        return "FILE_NOT_FOUND"
    with open(filepath, 'rb') as f:
        return hashlib.sha256(f.read()).hexdigest()[:16]


def analyze_repro_run(run_id: str) -> dict:
    """Analyze a reproduction run for anomalies"""
    
    run_dir = Path(f"/home/node/.openclaw/workspace/bob_quant/exploration_repro_20260305/runs/Github_ikonclast_bob_der_botmeister__halt_diagnosis__20260716_095920")
    
    if not run_dir.exists():
        return {"error": "Run directory not found"}
    
    # Load params
    params_file = run_dir / "params.json"
    params = json.loads(params_file.read_text()) if params_file.exists() else {}
    
    # Load metrics
    metrics_file = run_dir / "metrics.json"
    metrics = json.loads(metrics_file.read_text()) if metrics_file.exists() else {}
    
    # Load stdout for CLI details
    stdout_file = run_dir / "stdout.txt"
    stdout = stdout_file.read_text() if stdout_file.exists() else ""
    
    # Extract from stdout
    timeframe = "1h"  # Expected
    timerange = "20230601-20231231"  # Expected
    pair_count = 5  # Expected
    
    # Check for anomalies in stdout
    if "timeframe : 1h" not in stdout.lower() and "timeframe: 1h" not in stdout.lower():
        timeframe = "ANOMALY_DETECTED"
    
    if "timerange: 20230601-20231231" not in stdout:
        # Try to find actual timerange
        for line in stdout.split("\n"):
            if "timerange" in line.lower():
                timerange = line.strip()
                break
    
    # Check pairs
    if "BTC/USDT" in stdout and "ETH/USDT" in stdout:
        # Count pairs in pairlist
        pair_count = stdout.count("/USDT")
    
    return {
        "run_id": run_id,
        "expected": {
            "timeframe": "1h",
            "timerange": "20230601-20231231",
            "pairs": 5,
            "ema_period": 20,
            "entry": 0.995,
            "exit": 0.985
        },
        "actual_from_config": params,
        "metrics": metrics,
        "anomalies": {
            "trade_ratio": metrics.get("trades", 0) / 387,  # vs expected 387
            "timeframe_mismatch": timeframe != "1h",
            "timerange_mismatch": "20230601-20231231" not in timerange,
            "too_many_trades": metrics.get("trades", 0) > 800
        },
        "diagnosis": "CANDY_CRUSH" if metrics.get("trades", 0) > 1000 else "OK"
    }


def run_buy_and_hold(pair: str, timerange: str) -> dict:
    """Benchmark Buy&Hold for a pair"""
    
    run_id = f"BNH_{pair.replace('/', '')}_{timerange}"
    
    # Simple B&H strategy
    strategy_code = f"""from freqtrade.strategy import IStrategy
from pandas import DataFrame

class Github_ikonclast_bob_der_botmeister__halt_diagnosis__20260716_095920(IStrategy):
    timeframe = '1h'
    stoploss = -0.99
    minimal_roi = {{"0": 999}}
    max_open_trades = 1
    
    def populate_buy_trend(self, dataframe: DataFrame, metadata: dict):
        dataframe.loc[:, 'buy'] = 0
        if len(dataframe) > 0:
            # Buy on first candle
            dataframe.iloc[0, dataframe.columns.get_loc('buy')] = 1
        return dataframe
    
    def populate_sell_trend(self, dataframe: DataFrame, metadata: dict):
        dataframe.loc[:, 'sell'] = 0
        if len(dataframe) > 0:
            # Sell on last candle
            dataframe.iloc[-1, dataframe.columns.get_loc('sell')] = 1
        return dataframe
"""
    
    strat_file = STRATEGIES_DIR / f"Github_ikonclast_bob_der_botmeister__halt_diagnosis__20260716_095920.py"
    strat_file.write_text(strategy_code)
    
    # Config with single pair
    config = {
        "max_open_trades": 1,
        "stake_currency": "USDT",
        "stake_amount": 1000,
        "dry_run": True,
        "dry_run_wallet": 1000,
        "timeframe": "1h",
        "trading_mode": "spot",
        "exchange": {"name": "binance", "pair_whitelist": [pair]},
        "pairlists": [{"method": "StaticPairList"}],
        "telegram": {"enabled": False, "token": "x", "chat_id": "1"},
        "api_server": {"enabled": False}
    }
    
    cfg_file = CONFIGS_DIR / f"cfgGithub_ikonclast_bob_der_botmeister__halt_diagnosis__20260716_095920.json"
    cfg_file.write_text(json.dumps(config, indent=2))
    
    cmd = [
        "docker", "run", "--rm", "--network", "host",
        "-v", f"{FREQTRADE_BASE}:/freqtrade/user_data:rw",
        "freqtradeorg/freqtrade:stable",
        "backtesting",
        "--strategy", run_id,
        "--config", f"/freqtrade/user_data/configs/cfgGithub_ikonclast_bob_der_botmeister__halt_diagnosis__20260716_095920.json",
        "--timeframe", "1h",
        "--timerange", timerange,
        "--fee", "0.0",  # No fees for B&H
        "--no-color"
    ]
    
    try:
        proc = subprocess.run(cmd, capture_output=True, text=True, timeout=120, env=DOCKER_ENV)
        
        zip_files = list(BACKTEST_BASE.glob(f"*Github_ikonclast_bob_der_botmeister__halt_diagnosis__20260716_095920*.zip"))
        if zip_files:
            with zipfile.ZipFile(max(zip_files, key=lambda p: p.stat().st_mtime), 'r') as zf:
                jsons = [f for f in zf.namelist() if f.endswith('.json') and '_config' not in f]
                if jsons:
                    with zf.open(jsons[0]) as f:
                        data = json.load(f)
                    
                    strat = list(data["strategy"].keys())[0]
                    s = data["strategy"][strat]
                    
                    return {
                        "pair": pair,
                        "timerange": timerange,
                        "profit_pct": round((s.get("profit_total") or 0) * 100, 2),
                        "trades": s.get("total_trades", 0),
                        "status": "SUCCESS"
                    }
    except Exception as e:
        return {"pair": pair, "timerange": timerange, "status": "FAILED", "error": str(e)}
    
    return {"pair": pair, "timerange": timerange, "status": "NO_DATA"}


def main():
    print("=" * 70)
    print("🛑 HALT DIAGNOSIS")
    print("=" * 70)
    
    diagnosis = {
        "timestamp": datetime.now().isoformat(),
        "phase": "HALT_DIAGNOSIS",
        "checks": {}
    }
    
    # Check 1: Repro Anomaly
    print("\n--- 1. REPRO ANOMALY CHECK (1332 vs 387 trades) ---")
    
    repro_analysis = analyze_repro_run("REPRO_001")
    diagnosis["checks"]["repro_anomaly"] = repro_analysis
    
    print(f"Expected trades: 387")
    print(f"Actual trades: {repro_analysis.get('metrics', {}).get('trades', 'N/A')}")
    print(f"Trade ratio: {repro_analysis.get('anomalies', {}).get('trade_ratio', 'N/A'):.1f}x")
    
    if repro_analysis.get("anomalies", {}).get("too_many_trades"):
        print("\n🔴 ANOMALY DETECTED: 3.4x more trades than expected!")
        print("\nPossible causes:")
        print("  1. Different data downloaded (price spikes = more EMA crossings)")
        print("  2. EMA calculation differs (TA-Lib vs pandas)")
        print("  3. Entry logic too loose (close > EMA*0.995 vs close > EMA*0.999)")
        print("  4. Config override (max_open_trades, fees, etc.)")
    
    # Check 2: Buy & Hold Benchmark
    print("\n--- 2. BUY & HOLD BENCHMARK ---")
    
    pairs = ["BTC/USDT", "ETH/USDT", "SOL/USDT", "ADA/USDT", "AVAX/USDT"]
    timeranges = ["20230601-20231231", "20240101-20241231"]
    
    benchmark_results = []
    
    for tr in timeranges:
        print(f"\nTimerange: {tr}")
        range_results = []
        for pair in pairs:
            result = run_buy_and_hold(pair, tr)
            range_results.append(result)
            if result.get("status") == "SUCCESS":
                print(f"  {pair}: {result.get('profit_pct')}%")
        benchmark_results.append({"timerange": tr, "results": range_results})
        
        # Calculate average
        profits = [r.get("profit_pct", 0) for r in range_results if r.get("status") == "SUCCESS"]
        if profits:
            avg = sum(profits) / len(profits)
            print(f"  Average: {avg:.2f}%")
    
    diagnosis["checks"]["buy_and_hold"] = benchmark_results
    
    # Check 3: Manifest Integrity
    print("\n--- 3. MANIFEST INTEGRITY CHECK ---")
    
    # Check current manifest
    manifest_file = Path("/home/node/.openclaw/workspace/bob_quant/manifest.json")
    if manifest_file.exists():
        manifest = json.loads(manifest_file.read_text())
        
        integrity = {
            "manifest_exists": True,
            "total_runs_documented": manifest.get("summary", {}).get("total_runs_attempted", 0),
            "positive_weak": manifest.get("summary", {}).get("positive_weak", 0),
            "files_exist": {}
        }
        
        for report in manifest.get("reports", []):
            filepath = Path("/home/node/.openclaw/workspace/bob_quant") / Path(report).name
            integrity["files_exist"][report] = filepath.exists()
        
        diagnosis["checks"]["manifest_integrity"] = integrity
        
        print(f"Total runs documented: {integrity['total_runs_documented']}")
        print(f"POSITIVE_WEAK: {integrity['positive_weak']}")
        print(f"Reports: {sum(integrity['files_exist'].values())}/{len(integrity['files_exist'])} exist")
    
    # Save diagnosis
    diagnosis_file = OUTPUT_DIR / "halt_diagnosis_report.json"
    with open(diagnosis_file, 'w') as f:
        json.dump(diagnosis, f, indent=2, default=str)
    
    # Summary report
    print("\n" + "=" * 70)
    print("📊 DIAGNOSIS SUMMARY")
    print("=" * 70)
    
    # B&H comparison
    print("\nBuy&Hold vs Strategy Performance:")
    for tr_data in benchmark_results:
        tr = tr_data["timerange"]
        profits = [r.get("profit_pct", 0) for r in tr_data["results"] if r.get("status") == "SUCCESS"]
        avg_bnh = sum(profits) / len(profits) if profits else 0
        
        print(f"  {tr}:")
        print(f"    Buy&Hold: {avg_bnh:.2f}%")
        if tr == "20230601-20231231":
            print(f"    EXPLORATION_TEST_MULTI (claim): +10.24%")
            print(f"    Our repro: -89.9%")
    
    print(f"\nDiagnosis file: {diagnosis_file}")
    
    return diagnosis


if __name__ == "__main__":
    main()
