"""
Shared fixtures for NexQuant integration tests.
Provides common test data, mock objects, and utilities.
"""
import pytest
import tempfile
import os
import sys
import numpy as np
import pandas as pd
from pathlib import Path
from datetime import datetime, timedelta
from unittest.mock import MagicMock, patch

# Project root
PROJECT_ROOT = Path(__file__).parent.parent.parent
sys.path.insert(0, str(PROJECT_ROOT))


# =============================================================================
# MOCK DATA FIXTURES
# =============================================================================


@pytest.fixture
def mock_factor_data():
    """Generate mock factor time series data."""
    np.random.seed(42)
    dates = pd.date_range("2024-01-01", periods=252, freq="B")
    factor_values = pd.Series(np.random.randn(252), index=dates, name="test_factor")
    forward_returns = pd.Series(np.random.randn(252) * 0.01, index=dates, name="returns")
    return factor_values, forward_returns


@pytest.fixture
def mock_portfolio_returns():
    """Generate mock portfolio return data."""
    np.random.seed(42)
    n_assets = 5
    n_days = 252
    dates = pd.date_range("2024-01-01", periods=n_days, freq="B")
    returns = pd.DataFrame(
        np.random.randn(n_days, n_assets) * 0.01,
        index=dates,
        columns=[f"asset_{i}" for i in range(n_assets)]
    )
    return returns


@pytest.fixture
def mock_expected_returns():
    """Generate mock expected returns."""
    return pd.Series({
        "asset_0": 0.10, "asset_1": 0.08, "asset_2": 0.06,
        "asset_3": 0.07, "asset_4": 0.12
    })


@pytest.fixture
def mock_covariance_matrix(mock_portfolio_returns):
    """Generate mock covariance matrix."""
    return mock_portfolio_returns.cov() * 252


@pytest.fixture
def mock_backtest_metrics():
    """Generate mock backtest metrics dictionary."""
    return {
        "ic": 0.08,
        "sharpe_ratio": 1.5,
        "annualized_return": 0.12,
        "max_drawdown": -0.08,
        "win_rate": 0.55,
        "total_trades": 252,
        "total_return": 0.15,
        "factor_name": "TestFactor",
        "timestamp": datetime.now().isoformat(),
    }


# =============================================================================
# TEMPORARY RESOURCE FIXTURES
# =============================================================================


@pytest.fixture
def temp_database_path():
    """Create a temporary database path for testing."""
    with tempfile.TemporaryDirectory() as tmpdir:
        db_path = os.path.join(tmpdir, "test.db")
        yield db_path


@pytest.fixture
def temp_output_dir():
    """Create a temporary output directory for testing."""
    with tempfile.TemporaryDirectory() as tmpdir:
        yield Path(tmpdir)


@pytest.fixture
def temp_env_file():
    """Create a temporary .env file for testing."""
    with tempfile.TemporaryDirectory() as tmpdir:
        env_path = os.path.join(tmpdir, ".env")
        with open(env_path, "w") as f:
            f.write("OPENAI_API_KEY=local\n")
            f.write("OPENAI_API_BASE=http://localhost:8081/v1\n")
            f.write("CHAT_MODEL=qwen3.5-35b\n")
            f.write("EMBEDD_MODEL=nomic-embed-text\n")
            f.write("LITELLM_PROXY_API_BASE=http://localhost:11434/v1\n")
        yield env_path


# =============================================================================
# COMPONENT FIXTURES
# =============================================================================


@pytest.fixture
def backtest_metrics_instance():
    """BacktestMetrics instance for testing."""
    from rdagent.components.backtesting.backtest_engine import BacktestMetrics
    return BacktestMetrics(risk_free_rate=0.02)


@pytest.fixture
def factor_backtester_instance(temp_output_dir):
    """FactorBacktester instance with temporary output directory."""
    from rdagent.components.backtesting.backtest_engine import FactorBacktester
    backtester = FactorBacktester()
    backtester.results_path = temp_output_dir
    return backtester


@pytest.fixture
def results_database_instance(temp_database_path):
    """ResultsDatabase instance with temporary database."""
    from rdagent.components.backtesting.results_db import ResultsDatabase
    db = ResultsDatabase(db_path=temp_database_path)
    yield db
    db.close()


@pytest.fixture
def populated_database_instance(results_database_instance):
    """ResultsDatabase pre-populated with test data."""
    db = results_database_instance

    # Add factors
    db.add_factor("Momentum", "price_based")
    db.add_factor("MeanReversion", "price_based")
    db.add_factor("Volatility", "risk_based")
    db.add_factor("ML_Factor", "ml_based")

    # Add backtest results
    db.add_backtest("Momentum", {
        "ic": 0.08, "sharpe_ratio": 1.5, "annualized_return": 0.12,
        "max_drawdown": -0.08, "win_rate": 0.55
    })
    db.add_backtest("MeanReversion", {
        "ic": 0.05, "sharpe_ratio": 1.2, "annualized_return": 0.08,
        "max_drawdown": -0.05, "win_rate": 0.52
    })
    db.add_backtest("Volatility", {
        "ic": -0.03, "sharpe_ratio": 0.8, "annualized_return": 0.04,
        "max_drawdown": -0.03, "win_rate": 0.48
    })
    db.add_backtest("ML_Factor", {
        "ic": 0.12, "sharpe_ratio": 2.1, "annualized_return": 0.18,
        "max_drawdown": -0.10, "win_rate": 0.60
    })

    # Add loop results
    db.add_loop(1, 4, 6, 0.08, "completed")
    db.add_loop(2, 5, 5, 0.10, "completed")

    return db


@pytest.fixture
def correlation_analyzer_instance():
    """CorrelationAnalyzer instance for testing."""
    from rdagent.components.backtesting.risk_management import CorrelationAnalyzer
    return CorrelationAnalyzer(lookback=60)


@pytest.fixture
def portfolio_optimizer_instance():
    """PortfolioOptimizer instance for testing."""
    from rdagent.components.backtesting.risk_management import PortfolioOptimizer
    return PortfolioOptimizer()


@pytest.fixture
def risk_manager_instance():
    """AdvancedRiskManager instance for testing."""
    from rdagent.components.backtesting.risk_management import AdvancedRiskManager
    return AdvancedRiskManager(max_pos=0.2, max_lev=5.0, max_dd=0.20)
