#pragma once

// Top-of-book L2 cache.
//
// Maintains the best N bid/ask levels from an exchange depth feed
// (snapshot + per-level overlays) and a short last-sale tape.
// This is a market-data view, not a matching engine: incoming
// quantities replace the level at that price; they do not trade.

namespace md {

enum class Side { Bid = 0, Ask = 1 };

struct Level {
  double price = 0.0;
  double qty = 0.0;
};

struct BookMetrics {
  double best_bid = 0.0;
  double best_ask = 0.0;
  double spread = 0.0;
  double mid = 0.0;
  double microprice = 0.0;
  double imbalance = 0.0;
  double vwap = 0.0;
  double ema = 0.0;
  double bid_qty = 0.0;
  double ask_qty = 0.0;
  double n_bids = 0.0;
  double n_asks = 0.0;
};

class OrderBook {
 public:
  static constexpr int kMaxDepth = 20;
  static constexpr int kTapeLen = 256;
  static constexpr int kEmaSpan = 20;

  void clear();

  // Replace both sides. Levels are re-sorted: bids high→low, asks low→high.
  void apply_snapshot(const Level* bids, int n_bids, const Level* asks,
                      int n_asks);

  // Insert, update, or delete a single price. qty <= 0 removes the level.
  // If the book is already at kMaxDepth, a new level is kept only when it
  // improves the inside (tighter than the current worst level).
  void set_level(Side side, double price, double qty);

  void on_print(double price, double qty);

  BookMetrics metrics() const;

  const Level* bids() const { return bids_; }
  const Level* asks() const { return asks_; }
  int bid_count() const { return n_bids_; }
  int ask_count() const { return n_asks_; }

 private:
  void set_on_side(Level* levels, int& count, double price, double qty,
                   bool descending);
  void sort_side(Level* levels, int count, bool descending);
  double vwap() const;

  Level bids_[kMaxDepth]{};
  Level asks_[kMaxDepth]{};
  int n_bids_ = 0;
  int n_asks_ = 0;

  double tape_px_[kTapeLen]{};
  double tape_qty_[kTapeLen]{};
  int tape_head_ = 0;
  int tape_count_ = 0;

  double ema_ = 0.0;
  bool ema_seeded_ = false;
};

}  // namespace md
