#!/usr/bin/env python3
"""
Ciccio Trading Bot - Automated Backtesting & Configuration Optimizer
Runs comprehensive backtests and generates optimal per-crypto configurations
"""

import json
import time
import sys
import os
from datetime import datetime, timedelta
from urllib.request import urlopen, Request
from urllib.parse import urlencode
from urllib.error import URLError, HTTPError
import statistics

API_BASE = "http://localhost:3031"

# Crypto symbols to test (Binance format)
SYMBOLS = [
    "BTCUSDT", "ETHUSDT", "SOLUSDT", "XRPUSDT", "ADAUSDT",
    "DOGEUSDT", "DOTUSDT", "AVAXUSDT", "LINKUSDT", "LTCUSDT",
    "BNBUSDT", "MATICUSDT", "ATOMUSDT"
]

# Test configurations
TEST_PERIODS = [
    {"days": 7, "label": "1 week"},
    {"days": 30, "label": "1 month"},
    {"days": 90, "label": "3 months"},
]

TIMEFRAMES = ["1m", "5m", "15m"]

# Parameter ranges to test
PARAM_RANGES = {
    "minConfidence": [50, 60, 70, 80],
    "roeTakeProfit": [20, 30, 40, 50],
    "roeStopLoss": [-15, -20, -25, -30],
    "roeTrailingPercent": [5, 8, 10, 15],
    "roeMinLockIn": [10, 15, 20, 25],
    "positionSizePercent": [5, 10, 15, 20],
}

class BacktestRunner:
    def __init__(self):
        self.results = {}
        self.optimal_configs = {}

    def fetch_json(self, url, timeout=120):
        """Fetch JSON from URL with timeout"""
        try:
            req = Request(url)
            with urlopen(req, timeout=timeout) as response:
                return json.loads(response.read().decode())
        except Exception as e:
            print(f"  [ERROR] {e}")
            return None

    def run_backtest_sync(self, symbol, start_date, end_date, timeframe="5m",
                          initial_capital=1000, advanced_features=None):
        """Run backtest via SSE stream and collect results"""
        params = {
            "symbol": symbol,
            "startDate": start_date,
            "endDate": end_date,
            "candleTimeframe": timeframe,
            "initialCapital": str(initial_capital),
        }

        # Add advanced features
        if advanced_features:
            for key, val in advanced_features.items():
                params[key] = "true" if val else "false"
        else:
            # Default: all enabled
            params["pyramidingEnabled"] = "true"
            params["crashProtectionEnabled"] = "true"
            params["adaptiveExecutionEnabled"] = "true"
            params["dynamicTrailingEnabled"] = "true"
            params["mtfConfirmationEnabled"] = "true"

        url = f"{API_BASE}/api/backtest/stream?{urlencode(params)}"

        result = {
            "symbol": symbol,
            "start_date": start_date,
            "end_date": end_date,
            "timeframe": timeframe,
            "trades": 0,
            "win_rate": 0,
            "total_pnl": 0,
            "total_pnl_percent": 0,
            "max_drawdown": 0,
            "sharpe": 0,
            "profit_factor": 0,
            "final_equity": initial_capital,
            "error": None
        }

        try:
            req = Request(url)
            req.add_header("Accept", "text/event-stream")

            with urlopen(req, timeout=300) as response:
                buffer = ""
                event_type = None

                for line in response:
                    line = line.decode('utf-8').strip()

                    if line.startswith("event:"):
                        event_type = line[6:].strip()
                    elif line.startswith("data:"):
                        data_str = line[5:].strip()
                        try:
                            data = json.loads(data_str)

                            if event_type == "complete":
                                stats = data.get("stats", {})
                                result["trades"] = stats.get("totalTrades", 0)
                                result["win_rate"] = stats.get("winRate", 0)
                                result["total_pnl"] = stats.get("totalPnL", 0)
                                result["total_pnl_percent"] = stats.get("returnPercent", 0)
                                result["max_drawdown"] = stats.get("maxDrawdownPercent", 0)
                                result["sharpe"] = stats.get("sharpeRatio", 0)
                                result["profit_factor"] = stats.get("profitFactor", 0)
                                result["final_equity"] = stats.get("finalEquity", initial_capital)
                                break

                        except json.JSONDecodeError:
                            pass
                    elif line == "":
                        event_type = None

        except Exception as e:
            result["error"] = str(e)

        return result

    def test_symbol(self, symbol, period_days=30, timeframe="5m"):
        """Run backtest for a single symbol"""
        end_date = datetime.now()
        start_date = end_date - timedelta(days=period_days)

        start_str = start_date.strftime("%Y-%m-%d")
        end_str = end_date.strftime("%Y-%m-%d")

        print(f"\n  Testing {symbol} ({period_days}d, {timeframe})...")

        result = self.run_backtest_sync(symbol, start_str, end_str, timeframe)

        if result["error"]:
            print(f"    ERROR: {result['error']}")
        else:
            print(f"    Trades: {result['trades']}, Win Rate: {result['win_rate']:.1f}%")
            print(f"    P&L: ${result['total_pnl']:.2f} ({result['total_pnl_percent']:.2f}%)")
            print(f"    Max DD: {result['max_drawdown']:.2f}%, Sharpe: {result['sharpe']:.2f}")

        return result

    def run_comprehensive_tests(self):
        """Run tests on all symbols with multiple configurations"""
        print("\n" + "="*60)
        print("CICCIO TRADING BOT - COMPREHENSIVE BACKTEST")
        print("="*60)
        print(f"Started: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        print(f"Symbols: {len(SYMBOLS)}")
        print(f"Periods: {[p['label'] for p in TEST_PERIODS]}")
        print(f"Timeframes: {TIMEFRAMES}")

        all_results = []

        # Test each symbol
        for i, symbol in enumerate(SYMBOLS, 1):
            print(f"\n{'='*40}")
            print(f"[{i}/{len(SYMBOLS)}] {symbol}")
            print("="*40)

            symbol_results = []

            # Test with different periods
            for period in TEST_PERIODS:
                # Use 5m timeframe for main tests (good balance)
                result = self.test_symbol(symbol, period["days"], "5m")
                result["period_label"] = period["label"]
                symbol_results.append(result)

                # Small delay to avoid overwhelming the API
                time.sleep(1)

            self.results[symbol] = symbol_results
            all_results.extend(symbol_results)

        return all_results

    def analyze_results(self):
        """Analyze results and determine optimal settings per crypto"""
        print("\n" + "="*60)
        print("ANALYSIS & OPTIMIZATION")
        print("="*60)

        for symbol, results in self.results.items():
            kraken_symbol = symbol.replace("USDT", "/USD")

            # Find best performing configuration
            valid_results = [r for r in results if not r.get("error") and r["trades"] > 0]

            if not valid_results:
                print(f"\n{symbol}: No valid results")
                continue

            # Score each result (higher is better)
            for r in valid_results:
                # Composite score: return, sharpe, win rate, low drawdown
                r["score"] = (
                    r["total_pnl_percent"] * 0.3 +
                    r["sharpe"] * 20 +
                    r["win_rate"] * 0.2 -
                    r["max_drawdown"] * 0.5 +
                    (r["profit_factor"] - 1) * 10
                )

            best = max(valid_results, key=lambda x: x["score"])

            # Calculate recommended settings based on performance
            avg_win_rate = statistics.mean([r["win_rate"] for r in valid_results])
            avg_drawdown = statistics.mean([r["max_drawdown"] for r in valid_results])
            avg_pnl = statistics.mean([r["total_pnl_percent"] for r in valid_results])

            # Determine risk profile
            if avg_drawdown < 10 and avg_win_rate > 55:
                risk_profile = "conservative"
                min_conf = 70
                roe_tp = 25
                roe_sl = -20
                pos_size = 10
            elif avg_drawdown < 20 and avg_win_rate > 50:
                risk_profile = "moderate"
                min_conf = 60
                roe_tp = 30
                roe_sl = -25
                pos_size = 12
            else:
                risk_profile = "aggressive"
                min_conf = 50
                roe_tp = 40
                roe_sl = -30
                pos_size = 15

            config = {
                "symbol": kraken_symbol,
                "enabled": True,
                "positionSizePercent": pos_size,
                "minConfidence": min_conf,
                "roeTakeProfit": roe_tp,
                "roeStopLoss": roe_sl,
                "roeTrailingStop": True,
                "roeTrailingPercent": 8,
                "roeMinLockIn": 15,
                "maxLeverageOverride": 10 if risk_profile == "conservative" else 15,
                "slippagePercent": 0.05,
                "commissionPercent": 0.05,
                # Backtest stats
                "_backtest": {
                    "avgWinRate": round(avg_win_rate, 2),
                    "avgDrawdown": round(avg_drawdown, 2),
                    "avgPnLPercent": round(avg_pnl, 2),
                    "riskProfile": risk_profile,
                    "bestPeriod": best["period_label"],
                    "testedAt": datetime.now().isoformat()
                }
            }

            self.optimal_configs[kraken_symbol] = config

            print(f"\n{kraken_symbol}:")
            print(f"  Risk Profile: {risk_profile}")
            print(f"  Avg Win Rate: {avg_win_rate:.1f}%")
            print(f"  Avg Drawdown: {avg_drawdown:.1f}%")
            print(f"  Avg P&L: {avg_pnl:.2f}%")
            print(f"  Recommended: TP={roe_tp}%, SL={roe_sl}%, MinConf={min_conf}%")

    def save_configs(self):
        """Save optimal configs to file"""
        config_file = "/var/www/html/ciccio.cuttalo.com/crypto-configs.json"

        # Load existing configs
        try:
            with open(config_file, 'r') as f:
                existing = json.load(f)
        except:
            existing = {}

        # Merge with new optimized configs
        for symbol, config in self.optimal_configs.items():
            if symbol in existing:
                # Preserve existing fields, update with optimized
                existing[symbol].update({k: v for k, v in config.items() if not k.startswith("_")})
                existing[symbol]["_backtest"] = config.get("_backtest", {})
            else:
                existing[symbol] = config

        # Add metadata
        existing["_metadata"] = {
            "lastOptimized": datetime.now().isoformat(),
            "symbolsOptimized": len(self.optimal_configs),
            "version": "auto-optimized-v1"
        }

        # Save
        with open(config_file, 'w') as f:
            json.dump(existing, f, indent=2)

        print(f"\nConfigs saved to {config_file}")

        # Set permissions
        os.system(f"chown www-data:www-data {config_file}")

    def generate_report(self):
        """Generate summary report"""
        print("\n" + "="*60)
        print("FINAL REPORT")
        print("="*60)

        total_symbols = len(self.results)
        successful = sum(1 for s in self.results.values() if any(r["trades"] > 0 for r in s))

        print(f"Symbols Tested: {total_symbols}")
        print(f"Successful Tests: {successful}")
        print(f"Configs Generated: {len(self.optimal_configs)}")

        # Best performers
        best_performers = []
        for symbol, config in self.optimal_configs.items():
            bt = config.get("_backtest", {})
            best_performers.append({
                "symbol": symbol,
                "pnl": bt.get("avgPnLPercent", 0),
                "winRate": bt.get("avgWinRate", 0),
                "risk": bt.get("riskProfile", "unknown")
            })

        best_performers.sort(key=lambda x: x["pnl"], reverse=True)

        print("\nTop Performers:")
        for p in best_performers[:5]:
            print(f"  {p['symbol']}: {p['pnl']:.2f}% P&L, {p['winRate']:.1f}% WR ({p['risk']})")

        print("\n" + "="*60)
        print(f"Completed: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
        print("="*60)


def main():
    print("Starting Ciccio Auto-Backtest System...")

    runner = BacktestRunner()

    # Run comprehensive tests
    runner.run_comprehensive_tests()

    # Analyze and optimize
    runner.analyze_results()

    # Save configs
    runner.save_configs()

    # Generate report
    runner.generate_report()

    print("\nDone!")


if __name__ == "__main__":
    main()
