//! Database schema definitions for Foxhunt HFT Trading System

use serde::{Deserialize, Serialize};
use sqlx::types::Uuid;
use chrono::{DateTime, Utc};
use rust_decimal::Decimal;
use common::types::Order as DomainOrder;

/// Configuration table schema
/// 
/// Represents a configuration key-value pair stored in the database.
///
/// Used for storing application settings that can be modified at runtime.
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct ConfigEntry {
    /// Unique identifier for the configuration entry
    pub id: Uuid,
    /// Configuration key (unique identifier for the setting)
    pub key: String,
    /// Configuration value stored as a string
    pub value: String,
    /// Optional human-readable description of the configuration
    pub description: Option<String>,
    /// Timestamp when the configuration was created
    pub created_at: DateTime<Utc>,
    /// Timestamp when the configuration was last updated
    pub updated_at: DateTime<Utc>,
}

/// Trading positions schema
/// 
/// Represents an open trading position in the database.
///
/// Tracks position size, entry price, and current profit/loss.
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct Position {
    /// Unique identifier for the position
    pub id: Uuid,
    /// Trading symbol (e.g., "AAPL", "BTC/USD")
    pub symbol: String,
    /// Position size (positive for long, negative for short)
    pub quantity: Decimal,
    /// Price at which the position was entered
    pub entry_price: Decimal,
    /// Current market price of the instrument
    pub current_price: Option<Decimal>,
    /// Current profit/loss of the position
    pub pnl: Option<Decimal>,
    /// Timestamp when the position was opened
    pub created_at: DateTime<Utc>,
    /// Timestamp when the position was last updated
    pub updated_at: DateTime<Utc>,
}

/// Database Order schema - simplified representation for persistence
/// 
/// Simplified order representation for database storage.
///
/// Use DomainOrder from common crate for business logic operations.
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct DbOrder {
    /// Unique identifier for the order
    pub id: Uuid,
    /// Trading symbol for the order
    pub symbol: String,
    /// Order type as string (MARKET, LIMIT, STOP, etc.)
    pub order_type: String,
    /// Order side as string (BUY, SELL)
    pub side: String,
    /// Order quantity
    pub quantity: Decimal,
    /// Order price (may be zero for market orders)
    pub price: Decimal,
    /// Order status as string (PENDING, FILLED, CANCELLED, etc.)
    pub status: String,
    /// Timestamp when the order was created
    pub created_at: DateTime<Utc>,
    /// Timestamp when the order was last updated
    pub updated_at: DateTime<Utc>,
}

/// Type alias for the canonical Order from common crate
/// 
/// This provides a convenient alias to the domain Order type
/// for use within the database module while maintaining
/// separation between database and business logic representations.
pub type Order = DomainOrder;

/// Conversion from canonical domain Order to database Order
impl From<DomainOrder> for DbOrder {
    fn from(order: DomainOrder) -> Self {
        Self {
            id: order.id,
            symbol: order.symbol,
            order_type: order.order_type.to_string(),
            side: order.side.to_string(),
            quantity: order.quantity,
            price: order.price.unwrap_or(Decimal::ZERO),
            status: order.status.to_string(),
            created_at: order.created_at,
            updated_at: order.updated_at,
        }
    }
}

/// Conversion from database Order to canonical domain Order
impl From<DbOrder> for DomainOrder {
    fn from(db_order: DbOrder) -> Self {
        use common::types::OrderSide;
use common::types::OrderType;
use common::types::OrderStatus;

        // Parse enums with defaults for safety
        let side = match db_order.side.to_uppercase().as_str() {
            "BUY" => OrderSide::Buy,
            "SELL" => OrderSide::Sell,
            _ => {
                tracing::error!("Invalid order side '{}' in database - this indicates data corruption", db_order.side);
                return Err(anyhow::anyhow!("Invalid order side: {}", db_order.side));
            }
        };

        let order_type = match db_order.order_type.to_uppercase().as_str() {
            "MARKET" => OrderType::Market,
            "LIMIT" => OrderType::Limit,
            "STOP" => OrderType::Stop,
            "STOP_LIMIT" => OrderType::StopLimit,
            _ => {
                tracing::error!("Invalid order type '{}' in database - this indicates data corruption", db_order.order_type);
                return Err(anyhow::anyhow!("Invalid order type: {}", db_order.order_type));
            }
        };

        let status = match db_order.status.to_uppercase().as_str() {
            "PENDING" => OrderStatus::Pending,
            "SUBMITTED" => OrderStatus::Submitted,
            "FILLED" => OrderStatus::Filled,
            "CANCELLED" => OrderStatus::Cancelled,
            "REJECTED" => OrderStatus::Rejected,
            _ => {
                tracing::error!("Invalid order status '{}' in database - this indicates data corruption", db_order.status);
                return Err(anyhow::anyhow!("Invalid order status: {}", db_order.status));
            }
        };

        Self {
            id: db_order.id,
            symbol: db_order.symbol,
            side,
            quantity: db_order.quantity,
            price: if db_order.price.is_zero() { None } else { Some(db_order.price) },
            order_type,
            status,
            filled_quantity: Decimal::ZERO, // Not stored in simple schema
            remaining_quantity: db_order.quantity, // Assume unfilled for simple schema
            avg_fill_price: None, // Not stored in simple schema
            created_at: db_order.created_at,
            updated_at: db_order.updated_at,
            expires_at: None,
            client_order_id: None,
            broker_order_id: None,
            stop_loss: None,
            take_profit: None,
        }
    }
}

/// Model configuration schema
/// 
/// Configuration for ML models including storage locations,
/// metadata, and activation status.
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct ModelConfig {
    /// Unique identifier for the model configuration
    pub id: Uuid,
    /// Human-readable model name
    pub name: String,
    /// Model version string
    pub version: String,
    /// S3 path where the model is stored
    pub s3_path: String,
    /// Optional local cache path for the model
    pub cache_path: Option<String>,
    /// Additional metadata stored as JSON
    pub metadata: serde_json::Value,
    /// Whether this model configuration is currently active
    pub is_active: bool,
    /// Timestamp when the configuration was created
    pub created_at: DateTime<Utc>,
    /// Timestamp when the configuration was last updated
    pub updated_at: DateTime<Utc>,
}

/// Model version tracking schema
/// 
/// Tracks different versions of ML models with performance metrics,
/// training metadata, and integrity information.
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
pub struct ModelVersion {
    /// Unique identifier for the model version
    pub id: Uuid,
    /// Reference to the parent model configuration
    pub model_config_id: Uuid,
    /// Version string for this specific model version
    pub version: String,
    /// S3 path where this model version is stored
    pub s3_path: String,
    /// Optional local cache path for this model version
    pub cache_path: Option<String>,
    /// Optional checksum for integrity verification
    pub checksum: Option<String>,
    /// Size of the model file in bytes
    pub size_bytes: Option<i64>,
    /// Performance metrics stored as JSON
    pub performance_metrics: serde_json::Value,
    /// Training metadata and parameters stored as JSON
    pub training_metadata: serde_json::Value,
    /// Whether this is the current active version
    pub is_current: bool,
    /// Timestamp when the version was created
    pub created_at: DateTime<Utc>,
    /// Timestamp when the version was last updated
    pub updated_at: DateTime<Utc>,
}