//! Trading state and agent metrics types for DQN.

use serde::{Deserialize, Serialize};

// Use canonical common crate types
use common::types::Price as IntegerPrice;
use rust_decimal::Decimal;

pub use crate::trading_action::TradingAction;

/// Trading state representation for `DQN`
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TradingState {
    /// Price features using canonical type system
    pub price_features: Vec<f32>,
    /// Technical indicators (normalized values)
    pub technical_indicators: Vec<f32>,
    /// Market micro-structure features
    pub market_features: Vec<f32>,
    /// Portfolio state using canonical types
    pub portfolio_features: Vec<f32>,
    /// Regime detection features (market state classification)
    pub regime_features: Vec<f32>,
}

impl TradingState {
    /// Create a new trading state
    pub fn new(
        price_features: Vec<IntegerPrice>,
        technical_indicators: Vec<f32>,
        market_features: Vec<f32>,
        portfolio_features: Vec<Decimal>,
    ) -> Self {
        Self {
            price_features: price_features.iter().map(|p| p.to_f64() as f32).collect(),
            technical_indicators,
            market_features,
            portfolio_features: portfolio_features
                .iter()
                .map(|d| TryInto::<f64>::try_into(*d).unwrap_or(0.0) as f32)
                .collect(),
            regime_features: Vec::new(),
        }
    }

    /// Create a new trading state from normalized features (preserves sign information)
    /// Used when features are already normalized (e.g., log returns) and don't need Price validation
    pub const fn from_normalized(
        price_features: Vec<f32>,
        technical_indicators: Vec<f32>,
        market_features: Vec<f32>,
        portfolio_features: Vec<f32>,
        regime_features: Vec<f32>,
    ) -> Self {
        Self {
            price_features,
            technical_indicators,
            market_features,
            portfolio_features,
            regime_features,
        }
    }

    /// Convert state to flat vector for neural network input
    pub fn to_vector(&self) -> Vec<f32> {
        let mut vec = Vec::new();
        vec.extend(self.price_features.iter().copied());
        vec.extend_from_slice(&self.technical_indicators);
        vec.extend_from_slice(&self.market_features);
        vec.extend(self.portfolio_features.iter().copied());
        vec.extend_from_slice(&self.regime_features);
        vec
    }

    /// Get state dimension
    pub fn dimension(&self) -> usize {
        self.price_features.len()
            + self.technical_indicators.len()
            + self.market_features.len()
            + self.portfolio_features.len()
            + self.regime_features.len()
    }

    /// Validate state consistency
    pub fn is_valid(&self) -> bool {
        !self.price_features.is_empty()
            && !self.technical_indicators.is_empty()
            && !self.market_features.is_empty()
            && !self.portfolio_features.is_empty()
    }
}

impl Default for TradingState {
    fn default() -> Self {
        Self {
            price_features: vec![0.0; 16],
            technical_indicators: vec![0.0; 16],
            market_features: vec![0.0; 16],
            portfolio_features: vec![0.0; 16],
            regime_features: Vec::new(),
        }
    }
}

/// `DQN` Agent metrics
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AgentMetrics {
    /// Total training episodes
    pub total_episodes: u64,
    /// Total steps taken
    pub total_steps: u64,
    /// Current epsilon value
    pub epsilon: f64,
    /// Average reward over last 100 episodes (using canonical decimal)
    pub avg_reward: Decimal,
    /// Win rate over last 100 episodes
    pub win_rate: f64,
    /// Current loss value (using canonical decimal)
    pub current_loss: Decimal,
}

impl Default for AgentMetrics {
    fn default() -> Self {
        Self {
            total_episodes: 0,
            total_steps: 0,
            epsilon: 1.0,
            avg_reward: Decimal::ZERO,
            win_rate: 0.0,
            current_loss: Decimal::ZERO,
        }
    }
}
