"""Exchange configuration wrappers."""

from __future__ import annotations

import logging
import os
from dataclasses import dataclass, field

logger = logging.getLogger("horizon")


def _env(key: str) -> str | None:
    """Read from environment, stripping whitespace."""
    val = os.environ.get(key)
    return val.strip() if val else None


@dataclass
class Polymarket:
    """Polymarket exchange configuration.

    Credentials are resolved in this order:
    1. Explicit constructor arguments
    2. Environment variables (POLYMARKET_PRIVATE_KEY, POLYMARKET_API_KEY, etc.)

    Usage:
        Polymarket(private_key="0x...")  # explicit
        Polymarket()                     # from env vars
    """

    private_key: str | None = field(default=None)
    clob_url: str = "https://clob.polymarket.com"
    gamma_url: str = "https://gamma-api.polymarket.com"
    api_key: str | None = None
    api_secret: str | None = None
    api_passphrase: str | None = None

    def __post_init__(self):
        # Resolve from environment if not provided
        if self.private_key is None:
            self.private_key = _env("POLYMARKET_PRIVATE_KEY")
        if self.api_key is None:
            self.api_key = _env("POLYMARKET_API_KEY")
        if self.api_secret is None:
            self.api_secret = _env("POLYMARKET_API_SECRET")
        if self.api_passphrase is None:
            self.api_passphrase = _env("POLYMARKET_API_PASSPHRASE")

        if self.private_key and not self.api_key:
            logger.info(
                "Polymarket: private_key set but no api_key. "
                "Will attempt to derive API credentials from CLOB."
            )

    def derive_api_credentials(self) -> None:
        """Derive api_key/api_secret/api_passphrase from private_key via CLOB API.

        Calls POST /auth/derive-api-key with the wallet signature.
        This is a convenience method - call it before hz.run() if you only have a private key.
        """
        if not self.private_key:
            raise ValueError("private_key is required to derive API credentials")

        if self.api_key and self.api_secret and self.api_passphrase:
            return  # Already have credentials

        import time

        import requests

        url = f"{self.clob_url}/auth/derive-api-key"
        timestamp = int(time.time())
        nonce = timestamp

        # Sign CLOB auth message with private key using eth_account
        try:
            from eth_account import Account
            from eth_account.messages import encode_defunct

            account = Account.from_key(self.private_key)
            msg = encode_defunct(text=f"{timestamp}{nonce}")
            signed = account.sign_message(msg)
            signature = signed.signature.hex()
            address = account.address
        except ImportError:
            raise RuntimeError(
                "Cannot derive API credentials: install eth_account "
                "(`pip install eth-account`) or provide api_key/api_secret/api_passphrase directly"
            )

        body = {
            "timestamp": str(timestamp),
            "nonce": str(nonce),
            "signature": signature,
            "address": address,
        }

        resp = requests.post(url, json=body, timeout=30)
        if not resp.ok:
            raise RuntimeError(
                f"Failed to derive API credentials: {resp.status_code} {resp.text}"
            )

        data = resp.json()
        self.api_key = data.get("apiKey") or data.get("key")
        self.api_secret = data.get("secret")
        self.api_passphrase = data.get("passphrase")

        if not self.api_key:
            raise RuntimeError(f"Unexpected response from derive-api-key: {data}")

        logger.info("Polymarket: API credentials derived successfully")


@dataclass
class Kalshi:
    """Kalshi exchange configuration.

    Credentials are resolved in this order:
    1. Explicit constructor arguments
    2. Environment variables (KALSHI_EMAIL, KALSHI_PASSWORD, KALSHI_API_KEY)

    Usage:
        Kalshi(email="...", password="...")  # explicit
        Kalshi()                             # from env vars
    """

    email: str | None = None
    password: str | None = None
    api_key: str | None = None
    api_url: str = "https://trading-api.kalshi.com/trade-api/v2"

    def __post_init__(self):
        if self.email is None:
            self.email = _env("KALSHI_EMAIL")
        if self.password is None:
            self.password = _env("KALSHI_PASSWORD")
        if self.api_key is None:
            self.api_key = _env("KALSHI_API_KEY")
        if env_url := _env("KALSHI_API_URL"):
            self.api_url = env_url


@dataclass
class Alpaca:
    """Alpaca Markets exchange configuration.

    Credentials are resolved in this order:
    1. Explicit constructor arguments
    2. Environment variables (ALPACA_API_KEY / APCA_API_KEY_ID,
       ALPACA_API_SECRET / APCA_API_SECRET_KEY)

    Usage:
        Alpaca(api_key="...", api_secret="...")  # explicit
        Alpaca()                                  # from env vars
        Alpaca(paper=False)                       # live trading
    """

    api_key: str | None = None
    api_secret: str | None = None
    paper: bool = True
    api_url: str | None = None
    data_source: str = "iex"

    def __post_init__(self):
        if self.api_key is None:
            self.api_key = _env("ALPACA_API_KEY") or _env("APCA_API_KEY_ID")
        if self.api_secret is None:
            self.api_secret = _env("ALPACA_API_SECRET") or _env("APCA_API_SECRET_KEY")
        if self.api_url is None:
            if self.paper:
                self.api_url = "https://paper-api.alpaca.markets"
            else:
                self.api_url = "https://api.alpaca.markets"


@dataclass
class Coinbase:
    """Coinbase Advanced Trade exchange configuration.

    Credentials are resolved in this order:
    1. Explicit constructor arguments
    2. Environment variables (COINBASE_API_KEY, COINBASE_API_SECRET)

    Usage:
        Coinbase(api_key="...", api_secret="...")  # explicit
        Coinbase()                                  # from env vars
    """

    api_key: str | None = None
    api_secret: str | None = None
    api_url: str = "https://api.coinbase.com/api/v3/brokerage"

    def __post_init__(self):
        if self.api_key is None:
            self.api_key = _env("COINBASE_API_KEY")
        if self.api_secret is None:
            self.api_secret = _env("COINBASE_API_SECRET")


@dataclass
class Robinhood:
    """Robinhood Crypto exchange configuration.

    Credentials are resolved in this order:
    1. Explicit constructor arguments
    2. Environment variables (ROBINHOOD_API_KEY)

    Usage:
        Robinhood(api_key="...")  # explicit
        Robinhood()               # from env vars
    """

    api_key: str | None = None
    api_url: str = "https://trading.robinhood.com/api/v1"

    def __post_init__(self):
        if self.api_key is None:
            self.api_key = _env("ROBINHOOD_API_KEY")


@dataclass
class Limitless:
    """Limitless exchange configuration (Base chain prediction market).

    Limitless is a prediction market on Base (chain ID 8453), a fork of Polymarket.

    Credentials are resolved in this order:
    1. Explicit constructor arguments
    2. Environment variables (LIMITLESS_API_KEY, LIMITLESS_PRIVATE_KEY, LIMITLESS_OWNER_ID)

    Usage:
        Limitless(api_key="...", private_key="0x...", owner_id="...")  # explicit
        Limitless()                                                      # from env vars
    """

    api_key: str | None = None
    private_key: str | None = None
    owner_id: str | None = None
    api_url: str = "https://api.limitless.exchange"

    def __post_init__(self):
        if self.api_key is None:
            self.api_key = _env("LIMITLESS_API_KEY")
        if self.private_key is None:
            self.private_key = _env("LIMITLESS_PRIVATE_KEY")
        if self.owner_id is None:
            self.owner_id = _env("LIMITLESS_OWNER_ID")


@dataclass
class InteractiveBrokers:
    """Interactive Brokers exchange configuration.

    Supports stocks, options, futures, and ForecastEx event contracts.

    Credentials are resolved in this order:
    1. Explicit constructor arguments
    2. Environment variables (IBKR_ACCESS_TOKEN, IBKR_ACCOUNT_ID)

    Usage:
        InteractiveBrokers(access_token="...", account_id="...")  # explicit
        InteractiveBrokers()                                       # from env vars
        InteractiveBrokers(paper=False)                            # live trading
    """

    access_token: str | None = None
    account_id: str | None = None
    paper: bool = True
    api_url: str | None = None

    def __post_init__(self):
        if self.access_token is None:
            self.access_token = _env("IBKR_ACCESS_TOKEN")
        if self.account_id is None:
            self.account_id = _env("IBKR_ACCOUNT_ID")
        if self.api_url is None:
            if self.paper:
                self.api_url = "https://paper-api.ibkr.com/v1/api"
            else:
                self.api_url = "https://api.ibkr.com/v1/api"
