"""
Mock Jesse Integration Layer for Development and Testing

Provides realistic mock implementations of Jesse functionality without requiring
the full Jesse framework to be installed. This enables development and testing
of Phase 3 optimization tools without complex dependencies.
"""

import random
import time
import numpy as np
import pandas as pd
from typing import Dict, List, Any, Optional, Tuple
from datetime import datetime, timedelta
import logging

logger = logging.getLogger("jesse-mcp.mock")


class MockJesseWrapper:
    """Mock implementation of JesseWrapper for development testing"""

    def __init__(self):
        self.mock_strategies = [
            "SimpleMovingAverage",
            "RSIMeanReversion",
            "MACrossStrategy",
            "BollingerBands",
            "GridTrading",
        ]

        self.mock_exchanges = [
            "Binance",
            "Coinbase",
            "Bybit",
            "Gate",
            "Hyperliquid",
            "Bitfinex",
            "Apex",
        ]

        self.mock_symbols = ["BTC-USDT", "ETH-USDT", "SOL-USDT", "ADA-USDT", "DOT-USDT"]

        self.mock_timeframes = ["1m", "5m", "15m", "1h", "4h", "1D"]

        # Seed for reproducible results
        np.random.seed(42)
        random.seed(42)

    def backtest(
        self,
        strategy_name: str,
        symbol: str,
        timeframe: str,
        start_date: str,
        end_date: str,
        **kwargs,
    ) -> Dict[str, Any]:
        """
        Mock backtest implementation that returns realistic results
        """
        logger.info(f"Mock backtest: {strategy_name} on {symbol} {timeframe}")

        # Simulate processing time
        time.sleep(0.1)

        # Generate realistic mock results
        days = (
            datetime.strptime(end_date, "%Y-%m-%d") - datetime.strptime(start_date, "%Y-%m-%d")
        ).days

        # Base performance varies by strategy
        strategy_performance = {
            "SimpleMovingAverage": 0.15,
            "RSIMeanReversion": 0.12,
            "MACrossStrategy": 0.18,
            "BollingerBands": 0.14,
            "GridTrading": 0.08,
        }

        base_return = strategy_performance.get(strategy_name, 0.10)

        # Add some randomness
        total_return = base_return + np.random.normal(0, 0.05)
        sharpe_ratio = np.random.normal(1.2, 0.3)
        max_drawdown = abs(np.random.normal(0.15, 0.05))
        win_rate = np.random.normal(0.55, 0.1)

        # Generate mock trades
        num_trades = int(days * np.random.uniform(0.5, 2.0))
        trades = self._generate_mock_trades(num_trades, win_rate)

        # Generate mock equity curve
        equity_curve = self._generate_mock_equity_curve(days, total_return, max_drawdown)

        return {
            "strategy": strategy_name,
            "symbol": symbol,
            "timeframe": timeframe,
            "start_date": start_date,
            "end_date": end_date,
            "total_return": round(total_return, 4),
            "sharpe_ratio": round(sharpe_ratio, 4),
            "max_drawdown": round(max_drawdown, 4),
            "win_rate": round(win_rate, 4),
            "total_trades": num_trades,
            "trades": trades,
            "equity_curve": equity_curve,
            "starting_balance": kwargs.get("starting_balance", 10000),
            "ending_balance": round(10000 * (1 + total_return), 2),
            "execution_time": round(np.random.uniform(0.5, 2.0), 2),
        }

    def list_strategies(self) -> List[str]:
        """Return list of available mock strategies"""
        return self.mock_strategies.copy()

    def read_strategy(self, strategy_name: str) -> str:
        """Return mock strategy code"""
        mock_code = f"""
# Mock Strategy: {strategy_name}
# This is a simulated strategy implementation for testing

class {strategy_name}:
    def __init__(self):
        self.name = "{strategy_name}"
        self.timeframe = "1h"
        
    def should_long(self):
        # Mock logic for long signals
        return True
        
    def should_short(self):
        # Mock logic for short signals  
        return False
        
    def update_position(self):
        # Mock position management
        pass
        
    def should_cancel(self):
        # Mock exit logic
        return False
"""
        return mock_code.strip()

    def validate_strategy(self, strategy_code: str) -> Dict[str, Any]:
        """Mock strategy validation"""
        # Simulate validation time
        time.sleep(0.05)

        # Basic syntax check
        try:
            compile(strategy_code, "<string>", "exec")
            syntax_valid = True
        except SyntaxError as e:
            return {
                "valid": False,
                "error": f"Syntax Error: {str(e)}",
                "line": e.lineno if hasattr(e, "lineno") else None,
            }

        # Mock additional validation
        has_class = "class " in strategy_code
        has_methods = any(method in strategy_code for method in ["should_long", "should_short"])

        if not has_class:
            return {
                "valid": False,
                "error": "Strategy must contain a class definition",
                "line": None,
            }

        if not has_methods:
            return {
                "valid": False,
                "error": "Strategy must implement required methods (should_long, should_short)",
                "line": None,
            }

        return {
            "valid": True,
            "error": None,
            "line": None,
            "warnings": ["Using mock validation - real Jesse validation may be stricter"],
        }

    def import_candles(
        self, exchange: str, symbol: str, timeframe: str, start_date: str, end_date: str
    ) -> Dict[str, Any]:
        """Mock candle import"""
        logger.info(f"Mock import: {exchange} {symbol} {timeframe}")

        # Simulate download time
        time.sleep(0.2)

        # Calculate number of candles
        days = (
            datetime.strptime(end_date, "%Y-%m-%d") - datetime.strptime(start_date, "%Y-%m-%d")
        ).days

        timeframe_minutes = {
            "1m": 1,
            "5m": 5,
            "15m": 15,
            "1h": 60,
            "4h": 240,
            "1D": 1440,
        }
        minutes = timeframe_minutes.get(timeframe, 60)
        num_candles = int((days * 24 * 60) / minutes)

        # Generate mock candle data
        base_price = 50000 if "BTC" in symbol else 3000 if "ETH" in symbol else 100
        candles = self._generate_mock_candles(num_candles, base_price)

        return {
            "exchange": exchange,
            "symbol": symbol,
            "timeframe": timeframe,
            "start_date": start_date,
            "end_date": end_date,
            "candles_imported": len(candles),
            "file_path": f"/mock/data/{exchange}/{symbol}-{timeframe}.csv",
            "file_size_mb": round(len(candles) * 0.0001, 2),
            "download_time": round(np.random.uniform(1.0, 5.0), 2),
        }

    def _generate_mock_trades(self, num_trades: int, win_rate: float) -> List[Dict[str, Any]]:
        """Generate realistic mock trades"""
        trades = []
        for i in range(num_trades):
            is_win = random.random() < win_rate

            if is_win:
                pnl = round(np.random.uniform(0.5, 3.0), 2)
                entry_price = round(np.random.uniform(100, 1000), 2)
                exit_price = round(entry_price * (1 + pnl / 100), 2)
            else:
                pnl = round(np.random.uniform(-2.0, -0.5), 2)
                entry_price = round(np.random.uniform(100, 1000), 2)
                exit_price = round(entry_price * (1 + pnl / 100), 2)

            trade_date = datetime.now() - timedelta(days=random.randint(1, 365))

            trades.append(
                {
                    "id": i + 1,
                    "entry_date": trade_date.strftime("%Y-%m-%d %H:%M:%S"),
                    "exit_date": (trade_date + timedelta(hours=random.randint(1, 48))).strftime(
                        "%Y-%m-%d %H:%M:%S"
                    ),
                    "entry_price": entry_price,
                    "exit_price": exit_price,
                    "quantity": round(np.random.uniform(0.1, 2.0), 4),
                    "pnl": pnl,
                    "pnl_percent": pnl,
                    "type": random.choice(["long", "short"]),
                    "fee": round(abs(entry_price * 0.001), 4),
                }
            )

        return trades

    def _generate_mock_equity_curve(
        self, days: int, total_return: float, max_drawdown: float
    ) -> List[Dict[str, Any]]:
        """Generate realistic mock equity curve"""
        equity_points = []
        current_equity = 10000
        peak_equity = 10000

        # Generate daily equity points
        for day in range(days + 1):
            # Add some daily volatility
            daily_return = np.random.normal(total_return / days, 0.02)
            current_equity *= 1 + daily_return

            # Track peak for drawdown calculation
            if current_equity > peak_equity:
                peak_equity = current_equity

            # Calculate current drawdown
            current_drawdown = (peak_equity - current_equity) / peak_equity

            equity_points.append(
                {
                    "date": (datetime.now() - timedelta(days=days - day)).strftime("%Y-%m-%d"),
                    "equity": round(current_equity, 2),
                    "return": round(daily_return * 100, 4),
                    "drawdown": round(current_drawdown * 100, 4),
                }
            )

        return equity_points

    def _generate_mock_candles(self, num_candles: int, base_price: float) -> List[Dict[str, Any]]:
        """Generate realistic mock candle data"""
        candles = []
        current_price = base_price

        for i in range(num_candles):
            # Generate OHLC with some randomness
            open_price = current_price
            high_price = open_price * (1 + np.random.uniform(0, 0.02))
            low_price = open_price * (1 - np.random.uniform(0, 0.02))
            close_price = open_price + np.random.normal(0, base_price * 0.001)
            volume = np.random.uniform(100, 10000)

            # Ensure OHLC relationships are valid
            high_price = max(high_price, open_price, close_price)
            low_price = min(low_price, open_price, close_price)

            candles.append(
                {
                    "timestamp": int(
                        (datetime.now() - timedelta(minutes=num_candles - i)).timestamp()
                    ),
                    "open": round(open_price, 2),
                    "high": round(high_price, 2),
                    "low": round(low_price, 2),
                    "close": round(close_price, 2),
                    "volume": round(volume, 2),
                }
            )

            current_price = close_price

        return candles


# Mock wrapper instance for testing
mock_wrapper = MockJesseWrapper()


def get_mock_jesse_wrapper():
    """Get mock Jesse wrapper instance"""
    return mock_wrapper
