"""
RL用State構築モジュール

OrderBookとTradeAnalyzerを統合し、
強化学習（Recurrent PPO等）モデルの入力として
利用可能な状態ベクトル（State）を構築する。
"""

import logging
from typing import Optional

from ..models import MBOEvent, TradeEvent
from .order_book import OrderBook
from .trade_analyzer import TradeAnalyzer

logger = logging.getLogger(__name__)

# numpyとtorchはオプション依存
try:
    import numpy as np
    HAS_NUMPY = True
except ImportError:
    HAS_NUMPY = False

try:
    import torch
    HAS_TORCH = True
except ImportError:
    HAS_TORCH = False


class StateBuilder:
    """
    RL用Stateビルダー。

    OrderBook（板情報）とTradeAnalyzer（約定分析）を統合し、
    強化学習モデルの入力に適したテンソル形式の状態ベクトルを生成する。

    状態ベクトルの構成:
        1. 板情報（上位N段のBid/Ask価格・サイズ・注文数）
        2. 板のメタ指標（Mid Price, Spread, Imbalance）
        3. 約定統計（Buy/Sell Volume, OFI, Cumulative Delta等）
    """

    def __init__(
        self,
        symbol: str = "",
        book_levels: int = 10,
        trade_window: int = 100,
    ):
        """
        Args:
            symbol: シンボル名
            book_levels: 含める板の価格レベル数
            trade_window: 約定分析のウィンドウサイズ
        """
        self.symbol = symbol
        self.book_levels = book_levels
        self.trade_window = trade_window

        # コンポーネント初期化
        self.order_book = OrderBook(symbol=symbol)
        self.trade_analyzer = TradeAnalyzer(window_size=trade_window)

        # 状態ベクトルの次元数を計算
        # 板: book_levels * 6 (bid3 + ask3 per level) + 3 (mid, spread, imb)
        # 約定: 10 (buy_vol, sell_vol, total_vol, ofi, agg_ratio, cum_delta, vwap, last_price, trade_rate, count)
        self._book_dim = book_levels * 6 + 3
        self._trade_dim = 10
        self.state_dim = self._book_dim + self._trade_dim

    def process_mbo(self, event: MBOEvent) -> None:
        """MBOイベントを処理する"""
        self.order_book.process_event(event)

    def process_trade(self, event: TradeEvent) -> None:
        """Tradeイベントを処理する"""
        self.trade_analyzer.process_event(event)

    def get_state_numpy(self) -> Optional["np.ndarray"]:
        """
        現在の状態をNumpy配列として取得する。

        Returns:
            np.ndarray: (state_dim,) 形状の状態ベクトル、
                        numpyが未インストールの場合はNone
        """
        if not HAS_NUMPY:
            logger.warning("numpyが利用できません")
            return None

        # 板情報のNumpy配列
        book_arr = self.order_book.to_numpy(n_levels=self.book_levels)
        if book_arr is None:
            book_arr = np.zeros(self._book_dim, dtype=np.float32)

        # 約定統計のNumpy配列
        trade_arr = self.trade_analyzer.to_numpy()
        if trade_arr is None:
            trade_arr = np.zeros(self._trade_dim, dtype=np.float32)

        # 結合
        return np.concatenate([book_arr, trade_arr])

    def get_state_tensor(self) -> Optional["torch.Tensor"]:
        """
        現在の状態をPyTorch Tensorとして取得する。

        Returns:
            torch.Tensor: (state_dim,) 形状のテンソル、
                          torchが未インストールの場合はNone
        """
        if not HAS_TORCH:
            logger.warning("torchが利用できません")
            return None

        state = self.get_state_numpy()
        if state is None:
            return None

        return torch.from_numpy(state).float()

    def get_state_normalized(
        self,
        price_mean: float = 2500.0,
        price_std: float = 50.0,
        volume_scale: float = 100.0,
    ) -> Optional["np.ndarray"]:
        """
        正規化された状態ベクトルを取得する。

        価格はz-score正規化、ボリュームはスケーリングを適用。

        Args:
            price_mean: 価格の平均値（正規化用）
            price_std: 価格の標準偏差（正規化用）
            volume_scale: ボリュームのスケール係数

        Returns:
            np.ndarray: 正規化された状態ベクトル
        """
        if not HAS_NUMPY:
            return None

        state = self.get_state_numpy()
        if state is None:
            return None

        normalized = state.copy()

        # 板の価格を正規化 (各レベルの最初の要素が価格)
        for i in range(self.book_levels):
            offset = i * 6
            # Bid価格の正規化
            normalized[offset] = (state[offset] - price_mean) / price_std
            # Ask価格の正規化
            normalized[offset + 3] = (state[offset + 3] - price_mean) / price_std
            # ボリュームのスケーリング
            normalized[offset + 1] /= volume_scale   # Bidサイズ
            normalized[offset + 2] /= volume_scale   # Bidカウント
            normalized[offset + 4] /= volume_scale   # Askサイズ
            normalized[offset + 5] /= volume_scale   # Askカウント

        # メタ指標のインデックス
        meta_start = self.book_levels * 6
        # Mid Price の正規化
        normalized[meta_start] = (state[meta_start] - price_mean) / price_std
        # Spread は price_std で正規化
        normalized[meta_start + 1] /= price_std
        # Imbalance はそのまま (-1 to 1)

        # 約定統計
        trade_start = self._book_dim
        # Buy/Sell/Total Volume のスケーリング
        normalized[trade_start] /= volume_scale
        normalized[trade_start + 1] /= volume_scale
        normalized[trade_start + 2] /= volume_scale
        # OFI, Agg Ratio はそのまま (-1 to 1, 0 to 1)
        # Cumulative Delta のスケーリング
        normalized[trade_start + 5] /= volume_scale
        # VWAP の正規化
        normalized[trade_start + 6] = (
            (state[trade_start + 6] - price_mean) / price_std
        )
        # Last Price の正規化
        normalized[trade_start + 7] = (
            (state[trade_start + 7] - price_mean) / price_std
        )
        # Trade Rate のスケーリング
        normalized[trade_start + 8] /= 100.0
        # Trade Count のスケーリング
        normalized[trade_start + 9] /= volume_scale

        return normalized

    def reset(self) -> None:
        """状態をリセットする"""
        self.order_book.clear()
        self.trade_analyzer.clear()

    def __repr__(self) -> str:
        return (
            f"StateBuilder(symbol={self.symbol!r}, "
            f"state_dim={self.state_dim}, "
            f"book_levels={self.book_levels}, "
            f"trade_window={self.trade_window}, "
            f"orders={self.order_book.order_count}, "
            f"trades={self.trade_analyzer.trade_count})"
        )
