"""Normalized market-data domain (the silver layer of the SSOT warehouse) — MBP-10 order-book snapshots.

A faithful Python port of the Rust `data::providers::databento::mbp10` types: prices are kept as i64
fixed-point (DBN nanoprice, 1e-9 scaling) exactly as the Rust framework stores them, so feature
computations are bit-identical to the Rust oracle. The float conversion lives in one place (price_to_f64).
"""
from __future__ import annotations

from dataclasses import dataclass, field

_PRICE_SCALE = 1e9  # DBN nanoprice: i64 fixed-point / 1e9 (per Rust BidAskPair::price_to_f64)


def price_to_f64(fixed: int) -> float:
    """Convert a DBN fixed-point (i64, 1e-9) price to float — the single conversion site."""
    return fixed / _PRICE_SCALE


def price_from_f64(price: float) -> int:
    return int(price * _PRICE_SCALE)


@dataclass(frozen=True, slots=True)
class BidAskLevel:
    bid_px: int          # fixed-point i64 (1e-9)
    bid_sz: int
    ask_px: int          # fixed-point i64 (1e-9)
    ask_sz: int
    bid_ct: int = 0
    ask_ct: int = 0


@dataclass(frozen=True, slots=True)
class Mbp10Snapshot:
    symbol: str
    ts: int                                          # event timestamp (ns) — the point-in-time key
    levels: tuple[BidAskLevel, ...] = field(default_factory=tuple)

    def best_bid_ask(self) -> tuple[float, float]:
        if not self.levels:
            return (0.0, 0.0)
        l0 = self.levels[0]
        return (price_to_f64(l0.bid_px), price_to_f64(l0.ask_px))

    def mid_price(self) -> float:
        bid, ask = self.best_bid_ask()
        return (bid + ask) / 2.0

    def spread(self) -> float:
        bid, ask = self.best_bid_ask()
        return ask - bid
