"""Tests for live exchange code paths.

These tests exercise exchange construction, poll_fills, and
side mapping logic without making real API calls.
"""

import pytest

from horizon._horizon import (
    Engine,
    Fill,
    OrderSide,
    RiskConfig,
    Side,
)


class TestPolymarketConstruction:
    """Test Polymarket exchange backend creation."""

    def test_polymarket_engine_creation(self):
        """Engine with exchange_type='polymarket' should create a polymarket backend."""
        engine = Engine(
            exchange_type="polymarket",
            exchange_key="test_key",
            exchange_secret="dGVzdF9zZWNyZXQ=",  # base64("test_secret")
            exchange_passphrase="test_pass",
        )
        assert engine.exchange_name() == "polymarket"

    def test_polymarket_engine_with_custom_url(self):
        """Should accept a custom CLOB URL."""
        engine = Engine(
            exchange_type="polymarket",
            exchange_key="test_key",
            exchange_secret="dGVzdF9zZWNyZXQ=",
            exchange_passphrase="test_pass",
            clob_url="https://custom-clob.example.com",
        )
        assert engine.exchange_name() == "polymarket"

    def test_polymarket_engine_default_credentials(self):
        """Should handle empty/default credentials without panic."""
        engine = Engine(exchange_type="polymarket")
        assert engine.exchange_name() == "polymarket"


class TestKalshiConstruction:
    """Test Kalshi exchange backend creation."""

    def test_kalshi_engine_with_api_key(self):
        """Engine with exchange_type='kalshi' and api_key."""
        engine = Engine(
            exchange_type="kalshi",
            exchange_key="test_kalshi_key",
        )
        assert engine.exchange_name() == "kalshi"

    def test_kalshi_engine_with_email_password(self):
        """Engine with exchange_type='kalshi' and email/password."""
        engine = Engine(
            exchange_type="kalshi",
            email="test@example.com",
            password="test_password",
        )
        assert engine.exchange_name() == "kalshi"

    def test_kalshi_engine_with_custom_url(self):
        """Should accept a custom API URL."""
        engine = Engine(
            exchange_type="kalshi",
            exchange_key="test_key",
            api_url="https://demo-api.kalshi.co/trade-api/v2",
        )
        assert engine.exchange_name() == "kalshi"


class TestPollFills:
    """Test the poll_fills method on paper exchange."""

    def test_poll_fills_no_fills(self):
        """poll_fills returns 0 when there are no fills."""
        engine = Engine()
        count = engine.poll_fills()
        assert count == 0

    def test_poll_fills_after_tick(self):
        """poll_fills processes fills that were generated by tick."""
        config = RiskConfig(
            max_position_per_market=1000.0,
            max_portfolio_notional=10000.0,
        )
        engine = Engine(risk_config=config)

        from horizon._horizon import OrderRequest

        engine.submit_order(OrderRequest(
            market_id="mkt_1",
            side=Side.Yes,
            order_side=OrderSide.Buy,
            size=10.0,
            price=0.55,
        ))

        # Tick with price at order level to trigger fill
        fill_count = engine.tick("mkt_1", 0.55)
        assert fill_count == 1

        # Fills were already processed in tick, so poll_fills
        # shouldn't find additional ones for paper exchange
        additional = engine.poll_fills()
        assert additional == 0

        # Verify fill was recorded
        fills = engine.recent_fills()
        assert len(fills) == 1
        assert fills[0].market_id == "mkt_1"
        assert fills[0].price == pytest.approx(0.55)


class TestProcessFill:
    """Test manual fill processing."""

    def test_process_fill_updates_positions(self):
        """process_fill should update positions."""
        engine = Engine()

        fill = Fill(
            fill_id="test_fill_1",
            order_id="order_1",
            market_id="mkt_1",
            side=Side.Yes,
            order_side=OrderSide.Buy,
            price=0.50,
            size=10.0,
            fee=0.01,
            timestamp=1000.0,
        )
        engine.process_fill(fill)

        positions = engine.positions()
        assert len(positions) == 1
        assert positions[0].market_id == "mkt_1"
        assert positions[0].size == pytest.approx(10.0)

    def test_process_fill_buy_and_sell(self):
        """Buy then sell should update P&L."""
        engine = Engine()

        # Buy 10 @ 0.40
        buy_fill = Fill(
            fill_id="fill_buy",
            order_id="order_buy",
            market_id="mkt_1",
            side=Side.Yes,
            order_side=OrderSide.Buy,
            price=0.40,
            size=10.0,
        )
        engine.process_fill(buy_fill)

        # Sell 10 @ 0.60 - should realize profit
        sell_fill = Fill(
            fill_id="fill_sell",
            order_id="order_sell",
            market_id="mkt_1",
            side=Side.Yes,
            order_side=OrderSide.Sell,
            price=0.60,
            size=10.0,
        )
        engine.process_fill(sell_fill)

        status = engine.status()
        assert status.total_realized_pnl > 0


class TestFillWithTokenId:
    """Test fills with token_id (Polymarket-style)."""

    def test_fill_with_token_id(self):
        """Fill with a token_id should be stored correctly."""
        engine = Engine()

        fill = Fill(
            fill_id="poly_fill_1",
            order_id="poly_order_1",
            market_id="token_abc",
            side=Side.Yes,
            order_side=OrderSide.Buy,
            price=0.65,
            size=100.0,
            fee=0.10,
            token_id="0x1234567890abcdef",
        )
        engine.process_fill(fill)

        fills = engine.recent_fills()
        assert len(fills) == 1
        assert fills[0].token_id == "0x1234567890abcdef"
        assert fills[0].fill_id == "poly_fill_1"


class TestEIP712Signing:
    """Test EIP-712 signing integration via Engine constructor."""

    def test_polymarket_with_private_key(self):
        """Engine should accept a private_key for Polymarket signing."""
        # Hardhat account #0 private key (well-known test key)
        engine = Engine(
            exchange_type="polymarket",
            exchange_key="test_key",
            exchange_secret="dGVzdF9zZWNyZXQ=",
            exchange_passphrase="test_pass",
            private_key="0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
        )
        assert engine.exchange_name() == "polymarket"

    def test_polymarket_with_private_key_no_prefix(self):
        """Private key without 0x prefix should also work."""
        engine = Engine(
            exchange_type="polymarket",
            exchange_key="test_key",
            exchange_secret="dGVzdF9zZWNyZXQ=",
            exchange_passphrase="test_pass",
            private_key="ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80",
        )
        assert engine.exchange_name() == "polymarket"

    def test_polymarket_with_invalid_private_key(self):
        """Invalid private key should not crash - just disables signing."""
        engine = Engine(
            exchange_type="polymarket",
            exchange_key="test_key",
            exchange_secret="dGVzdF9zZWNyZXQ=",
            exchange_passphrase="test_pass",
            private_key="not_a_valid_hex_key",
        )
        assert engine.exchange_name() == "polymarket"

    def test_polymarket_without_private_key(self):
        """No private_key should still create the exchange (signing disabled)."""
        engine = Engine(
            exchange_type="polymarket",
            exchange_key="test_key",
            exchange_secret="dGVzdF9zZWNyZXQ=",
            exchange_passphrase="test_pass",
        )
        assert engine.exchange_name() == "polymarket"


class TestTokenIdRouting:
    """Test token_id and neg_risk on OrderRequest."""

    def test_order_request_with_token_id(self):
        """OrderRequest should accept token_id."""
        from horizon._horizon import OrderRequest

        req = OrderRequest(
            market_id="mkt_slug",
            side=Side.Yes,
            order_side=OrderSide.Buy,
            size=10.0,
            price=0.55,
            token_id="1234567890",
            neg_risk=True,
        )
        assert req.token_id == "1234567890"
        assert req.neg_risk is True
        assert req.market_id == "mkt_slug"

    def test_order_request_defaults(self):
        """token_id defaults to None, neg_risk to False."""
        from horizon._horizon import OrderRequest

        req = OrderRequest(
            market_id="mkt_1",
            side=Side.Yes,
            order_side=OrderSide.Buy,
            size=10.0,
            price=0.55,
        )
        assert req.token_id is None
        assert req.neg_risk is False

    def test_submit_quotes_with_token_id(self):
        """submit_quotes should accept token_id and neg_risk."""
        config = RiskConfig(
            max_position_per_market=1000.0,
            max_portfolio_notional=10000.0,
        )
        engine = Engine(risk_config=config)
        from horizon._horizon import Quote

        quote = Quote(bid=0.40, ask=0.60, size=5.0)
        ids = engine.submit_quotes(
            "mkt_1", [quote], Side.Yes,
            token_id="9999", neg_risk=True,
        )
        assert len(ids) == 2  # bid + ask


class TestSyncPositions:
    """Test sync_positions on paper exchange."""

    def test_sync_positions_paper(self):
        """sync_positions on paper returns 0 (no remote positions)."""
        engine = Engine()
        count = engine.sync_positions()
        assert count == 0


class TestEnvConfig:
    """Test environment variable config loading."""

    def test_polymarket_from_env(self):
        """Polymarket should read from env vars."""
        import os

        os.environ["POLYMARKET_PRIVATE_KEY"] = "0xdeadbeef"
        os.environ["POLYMARKET_API_KEY"] = "test_key_from_env"
        try:
            from horizon.exchanges import Polymarket

            pm = Polymarket()
            assert pm.private_key == "0xdeadbeef"
            assert pm.api_key == "test_key_from_env"
        finally:
            del os.environ["POLYMARKET_PRIVATE_KEY"]
            del os.environ["POLYMARKET_API_KEY"]

    def test_kalshi_from_env(self):
        """Kalshi should read from env vars."""
        import os

        os.environ["KALSHI_EMAIL"] = "test@env.com"
        os.environ["KALSHI_PASSWORD"] = "env_pass"
        try:
            from horizon.exchanges import Kalshi

            k = Kalshi()
            assert k.email == "test@env.com"
            assert k.password == "env_pass"
        finally:
            del os.environ["KALSHI_EMAIL"]
            del os.environ["KALSHI_PASSWORD"]

    def test_explicit_overrides_env(self):
        """Explicit args should take priority over env vars."""
        import os

        os.environ["POLYMARKET_PRIVATE_KEY"] = "0xfromenv"
        try:
            from horizon.exchanges import Polymarket

            pm = Polymarket(private_key="0xexplicit")
            assert pm.private_key == "0xexplicit"
        finally:
            del os.environ["POLYMARKET_PRIVATE_KEY"]


class TestExchangeBackendFallback:
    """Test that unknown exchange types fall back to paper."""

    def test_unknown_exchange_type_is_paper(self):
        """An unrecognized exchange_type should default to paper."""
        engine = Engine(exchange_type="unknown_exchange")
        assert engine.exchange_name() == "paper"

    def test_empty_exchange_type_is_paper(self):
        """Empty exchange_type should default to paper."""
        engine = Engine(exchange_type="")
        assert engine.exchange_name() == "paper"
