"""
Shared test fixtures for the LuckySt Trading Terminal test suite.
"""

import os
import sys
import json
import asyncio
from datetime import datetime, timedelta
from unittest.mock import AsyncMock, MagicMock, patch

import pytest
import fakeredis

# Ensure backend is importable
sys.path.insert(0, os.path.join(os.path.dirname(__file__), ".."))

# Set test environment variables BEFORE any app imports
os.environ.setdefault("SECRET_KEY", "test-secret-key-for-testing-1234")
os.environ.setdefault("JWT_SECRET_KEY", "test-jwt-secret-key-12345678")
os.environ.setdefault("POSTGRES_USER", "test")
os.environ.setdefault("POSTGRES_PASSWORD", "test")
os.environ.setdefault("POSTGRES_HOST", "localhost")
os.environ.setdefault("POSTGRES_DB", "test_luckyst")
os.environ.setdefault("LUCKYST_NETWORK", "testnet")
os.environ.setdefault("ENVIRONMENT", "development")


# ---------------------------------------------------------------------------
# RSA key fixture (valid 2048-bit test key)
# ---------------------------------------------------------------------------

def _generate_test_rsa_key():
    """Generate a valid 2048-bit RSA key for testing."""
    from cryptography.hazmat.primitives.asymmetric import rsa
    from cryptography.hazmat.primitives import serialization
    key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
    pem = key.private_bytes(
        serialization.Encoding.PEM,
        serialization.PrivateFormat.TraditionalOpenSSL,
        serialization.NoEncryption(),
    )
    return pem.decode()


TEST_RSA_KEY = _generate_test_rsa_key()


# ---------------------------------------------------------------------------
# Fake Redis
# ---------------------------------------------------------------------------

@pytest.fixture
def fake_redis():
    """Synchronous fake Redis client."""
    return fakeredis.FakeRedis(decode_responses=True)


@pytest.fixture
def fake_redis_async():
    """Async-compatible fake Redis (mocked)."""
    r = AsyncMock()
    _store = {}
    _sets = {}
    _sorted_sets = {}

    async def _get(key):
        return _store.get(key)

    async def _set(key, value, ex=None):
        _store[key] = value

    async def _setex(key, ttl, value):
        _store[key] = value

    async def _delete(*keys):
        for k in keys:
            _store.pop(k, None)

    async def _rpush(key, value):
        if key not in _store:
            _store[key] = []
        _store[key].append(value)

    async def _lpop(key):
        lst = _store.get(key, [])
        if lst:
            return lst.pop(0)
        return None

    async def _sadd(key, *values):
        if key not in _sets:
            _sets[key] = set()
        _sets[key].update(values)

    async def _smembers(key):
        return _sets.get(key, set())

    async def _srem(key, *values):
        if key in _sets:
            _sets[key] -= set(values)

    async def _zadd(key, mapping):
        if key not in _sorted_sets:
            _sorted_sets[key] = {}
        _sorted_sets[key].update(mapping)

    async def _zrange(key, start, stop, **kwargs):
        ss = _sorted_sets.get(key, {})
        items = sorted(ss.items(), key=lambda x: x[1])
        # For -1 (all)
        if stop == -1:
            stop = len(items)
        else:
            stop = stop + 1
        return [k for k, v in items[start:stop]]

    r.get = AsyncMock(side_effect=_get)
    r.set = AsyncMock(side_effect=_set)
    r.setex = AsyncMock(side_effect=_setex)
    r.delete = AsyncMock(side_effect=_delete)
    r.rpush = AsyncMock(side_effect=_rpush)
    r.lpop = AsyncMock(side_effect=_lpop)
    r.sadd = AsyncMock(side_effect=_sadd)
    r.smembers = AsyncMock(side_effect=_smembers)
    r.srem = AsyncMock(side_effect=_srem)
    r.zadd = AsyncMock(side_effect=_zadd)
    r.zrange = AsyncMock(side_effect=_zrange)
    r._store = _store
    r._sets = _sets
    r._sorted_sets = _sorted_sets

    return r


# ---------------------------------------------------------------------------
# Mock database session
# ---------------------------------------------------------------------------

@pytest.fixture
def mock_db():
    """Mock async database session."""
    db = AsyncMock()
    db.add = MagicMock()
    db.commit = AsyncMock()
    db.refresh = AsyncMock()
    db.delete = AsyncMock()
    db.execute = AsyncMock()
    return db


# ---------------------------------------------------------------------------
# Test user
# ---------------------------------------------------------------------------

@pytest.fixture
def test_user():
    """Create a test User object."""
    from app.modules.user.models import User
    return User(
        id=1,
        username="testtrader",
        email="trader@luckyst.com",
        hashed_password="$2b$12$hashed",
        is_active=True,
        is_superuser=False,
        failed_login_attempts=0,
        recent_events=[],
    )


# ---------------------------------------------------------------------------
# Valid deploy configs
# ---------------------------------------------------------------------------

VALID_UUID4 = "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"


@pytest.fixture
def kalshi_deploy_config():
    """Valid Kalshi single-market deploy config dict."""
    return {
        "platform": "kalshi",
        "num_markets": 1,
        "mode": "automated",
        "markets": ["KXBTC-25MAR05-BTC1-YES"],
        "kalshi_api_key": VALID_UUID4,
        "rsa_key_path": TEST_RSA_KEY,
        "min_spread": 2,
        "max_spread": 7,
        "m1_bounds": [1, 7, 93, 99],
        "position_increment": 10,
        "max_position": 1000,
        "trade_strategy": "join_jump",
    }


@pytest.fixture
def kalshi_2market_config():
    """Valid Kalshi two-market deploy config dict."""
    return {
        "platform": "kalshi",
        "num_markets": 2,
        "mode": "automated",
        "markets": ["KXNBA-25MAR05-LALMIA-LAL", "KXNBA-25MAR05-LALMIA-MIA"],
        "kalshi_api_key": VALID_UUID4,
        "rsa_key_path": TEST_RSA_KEY,
        "both_side": "no",
        "market_priority": "expensive",
        "min_spread": 2,
        "max_spread": 7,
        "m1_bounds": [1, 7, 93, 99],
        "m2_bounds": [1, 7, 93, 99],
        "position_increment": 10,
        "max_position": 1000,
        "trade_strategy": "join_jump",
    }


@pytest.fixture
def turbine_deploy_config():
    """Valid Turbine deploy config dict."""
    return {
        "platform": "turbine",
        "num_markets": 1,
        "mode": "automated",
        "markets": ["BTC"],
        "turbine_private_key": "0x" + "ab" * 32,
        "turbine_asset": "BTC",
        "min_spread": 2,
        "max_spread": 7,
        "m1_bounds": [1, 7, 93, 99],
        "position_increment": 10,
        "max_position": 1000,
    }


@pytest.fixture
def polymarket_deploy_config():
    """Valid Polymarket deploy config dict."""
    return {
        "platform": "polymarket",
        "num_markets": 1,
        "mode": "automated",
        "markets": ["some-condition-id"],
        "poly_private_key": "0x" + "cd" * 32,
        "poly_market_type": "interval",
        "poly_asset": "BTC",
        "poly_interval": "5m",
        "min_spread": 2,
        "max_spread": 7,
        "m1_bounds": [1, 7, 93, 99],
        "position_increment": 10,
        "max_position": 1000,
    }
