from __future__ import annotations

from dataclasses import dataclass

from .errors import EasyBinanceError

REST_API = "rest"
WEBSOCKET_API = "websocket-api"
WEBSOCKET_STREAMS = "websocket-streams"
APIS = (REST_API, WEBSOCKET_API, WEBSOCKET_STREAMS)


@dataclass(frozen=True, slots=True)
class ProductSpec:
    name: str
    aliases: tuple[str, ...]
    import_path: str
    class_name: str
    docs_url: str
    supported_apis: tuple[str, ...]
    rest_testnet_url: str | None = None
    ws_api_testnet_url: str | None = None
    ws_streams_testnet_url: str | None = None


PRODUCTS: tuple[ProductSpec, ...] = (
    ProductSpec(
        name="usds-futures",
        aliases=("usdm", "usdsm", "usds", "usds-futures", "usd-m", "usd-m-futures"),
        import_path=(
            "binance_sdk_derivatives_trading_usds_futures.derivatives_trading_usds_futures"
        ),
        class_name="DerivativesTradingUsdsFutures",
        docs_url=(
            "https://developers.binance.com/docs/derivatives/usds-margined-futures/general-info"
        ),
        supported_apis=(REST_API, WEBSOCKET_API, WEBSOCKET_STREAMS),
        rest_testnet_url="https://testnet.binancefuture.com",
        ws_api_testnet_url="wss://testnet.binancefuture.com/ws-fapi/v1",
        ws_streams_testnet_url="wss://stream.binancefuture.com",
    ),
    ProductSpec(
        name="coin-futures",
        aliases=("coinm", "coin", "coin-futures", "coin-m", "coin-m-futures"),
        import_path=(
            "binance_sdk_derivatives_trading_coin_futures.derivatives_trading_coin_futures"
        ),
        class_name="DerivativesTradingCoinFutures",
        docs_url=(
            "https://developers.binance.com/docs/derivatives/coin-margined-futures/general-info"
        ),
        supported_apis=(REST_API, WEBSOCKET_API, WEBSOCKET_STREAMS),
        rest_testnet_url="https://testnet.binancefuture.com",
        ws_api_testnet_url="wss://testnet.binancefuture.com/ws-dapi/v1",
        ws_streams_testnet_url="wss://dstream.binancefuture.com",
    ),
    ProductSpec(
        name="options",
        aliases=("option", "options"),
        import_path=(
            "binance_sdk_derivatives_trading_options.derivatives_trading_options"
        ),
        class_name="DerivativesTradingOptions",
        docs_url="https://developers.binance.com/docs/derivatives/option/general-info",
        supported_apis=(REST_API, WEBSOCKET_STREAMS),
    ),
    ProductSpec(
        name="portfolio-margin",
        aliases=("pm", "portfolio-margin", "portfolio_margin"),
        import_path=(
            "binance_sdk_derivatives_trading_portfolio_margin.derivatives_trading_portfolio_margin"
        ),
        class_name="DerivativesTradingPortfolioMargin",
        docs_url=(
            "https://developers.binance.com/docs/derivatives/portfolio-margin/general-info"
        ),
        supported_apis=(REST_API, WEBSOCKET_STREAMS),
        rest_testnet_url="https://testnet.binancefuture.com",
        ws_streams_testnet_url="wss://fstream.binancefuture.com/pm",
    ),
    ProductSpec(
        name="portfolio-margin-pro",
        aliases=(
            "pm-pro",
            "pmp",
            "portfolio-margin-pro",
            "portfolio_margin_pro",
        ),
        import_path=(
            "binance_sdk_derivatives_trading_portfolio_margin_pro.derivatives_trading_portfolio_margin_pro"
        ),
        class_name="DerivativesTradingPortfolioMarginPro",
        docs_url=(
            "https://developers.binance.com/docs/derivatives/portfolio-margin-pro/general-info"
        ),
        supported_apis=(REST_API, WEBSOCKET_STREAMS),
    ),
)


def normalize_name(value: str) -> str:
    return value.strip().lower().replace("_", "-").replace(" ", "-")


def normalize_api_kind(value: str) -> str:
    normalized = normalize_name(value)
    aliases = {
        "rest": REST_API,
        "ws-api": WEBSOCKET_API,
        "websocket-api": WEBSOCKET_API,
        "websocketapi": WEBSOCKET_API,
        "ws-streams": WEBSOCKET_STREAMS,
        "websocket-streams": WEBSOCKET_STREAMS,
        "websocketstreams": WEBSOCKET_STREAMS,
    }
    try:
        return aliases[normalized]
    except KeyError as exc:
        raise EasyBinanceError(
            f"Unsupported api kind: {value}. Expected one of: {', '.join(APIS)}."
        ) from exc


def get_product_spec(name: str) -> ProductSpec:
    normalized = normalize_name(name)
    for product in PRODUCTS:
        if normalized == product.name or normalized in product.aliases:
            return product
    raise EasyBinanceError(
        f"Unsupported product: {name}. Available products: "
        + ", ".join(product.name for product in PRODUCTS)
    )
