//! Risk Data Models
//!
//! Database schema models and data structures for risk management in
//! high-frequency trading systems. Provides comprehensive data models
//! for `VaR` calculations, compliance logging, and position limits.

// Allow pedantic lints for data models
#![allow(clippy::missing_docs_in_private_items)]
#![allow(missing_docs)]

use chrono::{DateTime, Utc};
use rust_decimal::Decimal;
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use std::collections::HashMap;
use uuid::Uuid;

/// Database connection pool - proper newtype wrapper
#[derive(Debug, Clone)]
pub struct DbPool(sqlx::PgPool);

impl DbPool {
    /// Create a new database pool wrapper
    pub const fn new(pool: sqlx::PgPool) -> Self {
        Self(pool)
    }

    /// Get the underlying pool
    pub const fn inner(&self) -> &sqlx::PgPool {
        &self.0
    }

    /// Into the underlying pool
    pub fn into_inner(self) -> sqlx::PgPool {
        self.0
    }
}

impl std::ops::Deref for DbPool {
    type Target = sqlx::PgPool;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl From<sqlx::PgPool> for DbPool {
    fn from(pool: sqlx::PgPool) -> Self {
        Self::new(pool)
    }
}

impl From<DbPool> for sqlx::PgPool {
    fn from(pool: DbPool) -> Self {
        pool.into_inner()
    }
}

/// Redis connection - proper newtype wrapper
#[derive(Debug, Clone)]
pub struct RedisConnection(redis::aio::MultiplexedConnection);

impl RedisConnection {
    /// Create a new Redis connection wrapper
    pub const fn new(conn: redis::aio::MultiplexedConnection) -> Self {
        Self(conn)
    }

    /// Get the underlying connection
    pub const fn inner(&self) -> &redis::aio::MultiplexedConnection {
        &self.0
    }

    /// Into the underlying connection
    pub fn into_inner(self) -> redis::aio::MultiplexedConnection {
        self.0
    }
}

impl std::ops::Deref for RedisConnection {
    type Target = redis::aio::MultiplexedConnection;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl From<redis::aio::MultiplexedConnection> for RedisConnection {
    fn from(conn: redis::aio::MultiplexedConnection) -> Self {
        Self::new(conn)
    }
}

impl From<RedisConnection> for redis::aio::MultiplexedConnection {
    fn from(conn: RedisConnection) -> Self {
        conn.into_inner()
    }
}

/// Financial instrument types
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
#[sqlx(type_name = "instrument_type", rename_all = "snake_case")]
pub enum InstrumentType {
    /// Stock or equity security
    Equity,
    /// Fixed income bond
    Bond,
    /// Physical commodity or commodity future
    Commodity,
    /// Foreign exchange currency pair
    Currency,
    /// Generic derivative instrument
    Derivative,
    /// Futures contract
    Future,
    /// Options contract
    Option,
    /// Interest rate or currency swap
    Swap,
    /// Contract for difference
    Cfd,
    /// Cryptocurrency
    Crypto,
}

/// Asset classes for risk categorization
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
#[sqlx(type_name = "asset_class", rename_all = "snake_case")]
pub enum AssetClass {
    /// Equity securities and stocks
    Equities,
    /// Bonds and fixed income securities
    FixedIncome,
    /// Physical and financial commodities
    Commodities,
    /// Foreign exchange and currencies
    Currencies,
    /// Alternative investments
    Alternatives,
    /// Derivative instruments
    Derivatives,
    /// Cash and cash equivalents
    Cash,
}

/// Market sectors for concentration risk
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, sqlx::Type)]
#[sqlx(type_name = "market_sector", rename_all = "snake_case")]
pub enum MarketSector {
    /// Technology and software companies
    Technology,
    /// Healthcare and pharmaceutical companies
    Healthcare,
    /// Financial services and banking
    Financials,
    /// Energy and oil companies
    Energy,
    /// Consumer goods and services
    Consumer,
    /// Industrial and manufacturing companies
    Industrials,
    /// Basic materials and mining
    Materials,
    /// Utilities and infrastructure
    Utilities,
    /// Real estate and REITs
    RealEstate,
    /// Telecommunications and media
    Telecommunications,
    /// Government bonds and securities
    Government,
    /// Other or unclassified sectors
    Other,
}

/// Trading venues
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)]
#[sqlx(type_name = "venue_type", rename_all = "snake_case")]
pub enum VenueType {
    /// Regulated exchange
    Exchange,
    /// Electronic Communication Network
    Ecn,
    /// Dark pool venue
    DarkPool,
    /// Over-the-counter market
    OverTheCounter,
    /// Internal crossing network
    InternalCross,
    /// Systematic Internalizer
    Systematic,
}

/// Risk metric types
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)]
#[sqlx(type_name = "risk_metric_type", rename_all = "snake_case")]
pub enum RiskMetricType {
    /// Value at Risk calculation
    Var,
    /// Expected Shortfall (Conditional `VaR`)
    ExpectedShortfall,
    /// Maximum drawdown metric
    MaxDrawdown,
    /// Sharpe ratio calculation
    SharpeRatio,
    /// Beta coefficient
    Beta,
    /// Volatility measurement
    Volatility,
    /// Correlation analysis
    Correlation,
    /// Portfolio concentration risk
    ConcentrationRisk,
    /// Liquidity risk assessment
    LiquidityRisk,
    /// Counterparty risk evaluation
    CounterpartyRisk,
}

/// Time periods for risk calculations
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, sqlx::Type)]
#[sqlx(type_name = "time_period", rename_all = "snake_case")]
pub enum TimePeriod {
    /// Intraday time period (within a day)
    Intraday,
    /// Daily time period
    Daily,
    /// Weekly time period
    Weekly,
    /// Monthly time period
    Monthly,
    /// Quarterly time period
    Quarterly,
    /// Yearly time period
    Yearly,
}

/// Financial instrument master data
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Instrument {
    /// Unique identifier for the instrument
    pub id: Uuid,
    /// Trading symbol (e.g., "AAPL", "MSFT")
    pub symbol: String,
    /// International Securities Identification Number
    pub isin: Option<String>,
    /// Committee on Uniform Securities Identification Procedures number
    pub cusip: Option<String>,
    /// Bloomberg identifier for the instrument
    pub bloomberg_id: Option<String>,
    /// Reuters identifier for the instrument
    pub reuters_id: Option<String>,
    /// Full name of the instrument (e.g., "Apple Inc.")
    pub name: String,
    /// Type of financial instrument
    pub instrument_type: InstrumentType,
    /// Asset class categorization
    pub asset_class: AssetClass,
    /// Market sector classification
    pub sector: Option<MarketSector>,
    /// Base currency of the instrument (ISO 3-letter code)
    pub currency: String,
    /// Primary exchange where the instrument is traded
    pub exchange: Option<String>,
    /// Minimum price movement (tick size)
    pub tick_size: Option<Decimal>,
    /// Standard trading lot size
    pub lot_size: Option<Decimal>,
    /// Contract multiplier for derivatives
    pub multiplier: Option<Decimal>,
    /// Maturity date for bonds, futures, and options
    pub maturity_date: Option<DateTime<Utc>>,
    /// Strike price for options and warrants
    pub strike_price: Option<Decimal>,
    /// Option type ("Call" or "Put" for options)
    pub option_type: Option<String>,
    /// Symbol of underlying asset for derivatives
    pub underlying_symbol: Option<String>,
    /// Whether the instrument is currently active for trading
    pub is_active: bool,
    /// Timestamp when the record was created
    pub created_at: DateTime<Utc>,
    /// Timestamp when the record was last updated
    pub updated_at: DateTime<Utc>,
    /// Additional instrument metadata as JSON
    pub metadata: serde_json::Value,
}

/// Portfolio definition
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Portfolio {
    /// Unique portfolio identifier
    pub id: String,
    /// Human-readable portfolio name
    pub name: String,
    /// Optional portfolio description
    pub description: Option<String>,
    /// Base currency for portfolio calculations (ISO 3-letter code)
    pub base_currency: String,
    /// Portfolio type (Strategy, Client, Prop, etc.)
    pub portfolio_type: String,
    /// Date when the portfolio was created/incepted
    pub inception_date: DateTime<Utc>,
    /// Identifier of the portfolio manager
    pub manager_id: String,
    /// Benchmark symbol for performance comparison
    pub benchmark: Option<String>,
    /// Allocated risk budget (typically as volatility target)
    pub risk_budget: Option<Decimal>,
    /// Value at Risk limit for the portfolio
    pub var_limit: Option<Decimal>,
    /// Maximum allowed drawdown percentage
    pub max_drawdown_limit: Option<Decimal>,
    /// Whether the portfolio is currently active
    pub is_active: bool,
    /// Timestamp when the record was created
    pub created_at: DateTime<Utc>,
    /// Timestamp when the record was last updated
    pub updated_at: DateTime<Utc>,
    /// Additional portfolio metadata as JSON
    pub metadata: serde_json::Value,
}

/// Position snapshot for risk calculations
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Position {
    /// Unique position identifier
    pub id: Uuid,
    /// Portfolio containing this position
    pub portfolio_id: String,
    /// Instrument symbol being held
    pub symbol: String,
    /// Number of shares/contracts held (positive for long, negative for short)
    pub quantity: Decimal,
    /// Average price at which the position was established
    pub average_price: Decimal,
    /// Current market price of the instrument
    pub market_price: Decimal,
    /// Total market value of the position (quantity × market price)
    pub market_value: Decimal,
    /// Unrealized profit and loss on the position
    pub unrealized_pnl: Decimal,
    /// Currency of the position (ISO 3-letter code)
    pub currency: String,
    /// Date when the position was first established
    pub entry_date: DateTime<Utc>,
    /// Timestamp of last update to position data
    pub last_updated: DateTime<Utc>,
    /// Position weight as percentage of total portfolio value
    pub weight: Option<Decimal>,
    /// Beta coefficient relative to market benchmark
    pub beta: Option<Decimal>,
    /// Duration for fixed income securities
    pub duration: Option<Decimal>,
    /// Delta sensitivity for derivatives (price sensitivity)
    pub delta: Option<Decimal>,
    /// Gamma sensitivity for derivatives (delta sensitivity)
    pub gamma: Option<Decimal>,
    /// Vega sensitivity for derivatives (volatility sensitivity)
    pub vega: Option<Decimal>,
    /// Theta sensitivity for derivatives (time decay)
    pub theta: Option<Decimal>,
}

/// Daily portfolio performance metrics
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct PortfolioPerformance {
    /// Unique performance record identifier
    pub id: Uuid,
    /// Portfolio this performance data belongs to
    pub portfolio_id: String,
    /// Date of the performance calculation
    pub date: DateTime<Utc>,
    /// Net Asset Value of the portfolio
    pub nav: Decimal,
    /// Daily return percentage
    pub daily_return: Decimal,
    /// Cumulative return since inception
    pub cumulative_return: Decimal,
    /// Annualized volatility
    pub volatility: Decimal,
    /// Sharpe ratio (risk-adjusted return)
    pub sharpe_ratio: Option<Decimal>,
    /// Maximum drawdown percentage
    pub max_drawdown: Decimal,
    /// Value at Risk at 95% confidence level
    pub var_95: Option<Decimal>,
    /// Value at Risk at 99% confidence level
    pub var_99: Option<Decimal>,
    /// Expected Shortfall at 95% confidence level
    pub expected_shortfall_95: Option<Decimal>,
    /// Beta coefficient relative to benchmark
    pub beta: Option<Decimal>,
    /// Alpha (excess return over benchmark)
    pub alpha: Option<Decimal>,
    /// Information ratio (alpha divided by tracking error)
    pub information_ratio: Option<Decimal>,
    /// Portfolio turnover rate
    pub turnover: Option<Decimal>,
    /// Weight of the largest position in the portfolio
    pub largest_position: Option<Decimal>,
    /// Total number of positions held
    pub number_of_positions: i32,
    /// Sector exposure breakdown as JSON
    pub sector_concentration: serde_json::Value,
    /// Currency exposure breakdown as JSON
    pub currency_exposure: serde_json::Value,
}

/// Risk factor exposures
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct RiskFactorExposure {
    /// Unique risk factor exposure identifier
    pub id: Uuid,
    /// Portfolio this exposure belongs to
    pub portfolio_id: String,
    /// Factor name (e.g., "Equity Market", "Interest Rates")
    pub risk_factor: String,
    /// Factor type ("Market", "Style", "Currency", "Country", etc.)
    pub factor_type: String,
    /// Factor loading/exposure amount
    pub exposure: Decimal,
    /// Contribution to portfolio variance
    pub contribution_to_risk: Decimal,
    /// Date of the exposure calculation
    pub date: DateTime<Utc>,
    /// Confidence interval for the exposure estimate
    pub confidence_interval: Option<Decimal>,
    /// R-squared goodness of fit measure
    pub r_squared: Option<Decimal>,
}

/// Stress test scenarios
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct StressScenario {
    /// Unique stress scenario identifier
    pub id: Uuid,
    /// Human-readable scenario name
    pub name: String,
    /// Detailed description of the stress scenario
    pub description: String,
    /// Scenario type (Historical, Hypothetical, Monte Carlo)
    pub scenario_type: String,
    /// Whether this scenario is currently active for testing
    pub active: bool,
    /// Factor shocks definition as JSON
    pub shock_factors: serde_json::Value,
    /// User who created this scenario
    pub created_by: String,
    /// Timestamp when the scenario was created
    pub created_at: DateTime<Utc>,
    /// Timestamp when the scenario was last updated
    pub updated_at: DateTime<Utc>,
}

/// Stress test results
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct StressTestResult {
    /// Unique stress test result identifier
    pub id: Uuid,
    /// Portfolio that was stress tested
    pub portfolio_id: String,
    /// Stress scenario that was applied
    pub scenario_id: Uuid,
    /// Date when the stress test was performed
    pub test_date: DateTime<Utc>,
    /// Portfolio value before applying stress
    pub base_portfolio_value: Decimal,
    /// Portfolio value after applying stress scenario
    pub stressed_portfolio_value: Decimal,
    /// Absolute loss amount from the stress test
    pub absolute_loss: Decimal,
    /// Percentage loss from the stress test
    pub percentage_loss: Decimal,
    /// Symbol of the worst performing position
    pub worst_performing_position: Option<String>,
    /// Loss amount of the worst performing position
    pub worst_position_loss: Option<Decimal>,
    /// Impact breakdown by sector as JSON
    pub sector_impacts: serde_json::Value,
    /// Detailed stress test results as JSON
    pub detailed_results: serde_json::Value,
}

/// Counterparty information for counterparty risk
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct Counterparty {
    /// Unique counterparty identifier
    pub id: String,
    /// Legal name of the counterparty
    pub name: String,
    /// Counterparty type (Bank, Broker, Exchange, etc.)
    pub counterparty_type: String,
    /// Country where the counterparty is domiciled
    pub country: String,
    /// Credit rating from rating agencies
    pub credit_rating: Option<String>,
    /// Legal Entity Identifier code
    pub lei_code: Option<String>,
    /// Parent company if applicable
    pub parent_company: Option<String>,
    /// Whether the counterparty is currently active
    pub is_active: bool,
    /// Maximum allowed exposure to this counterparty
    pub exposure_limit: Option<Decimal>,
    /// Margin requirement for this counterparty
    pub margin_requirement: Option<Decimal>,
    /// Whether a netting agreement is in place
    pub netting_agreement: bool,
    /// Timestamp when the record was created
    pub created_at: DateTime<Utc>,
    /// Timestamp when the record was last updated
    pub updated_at: DateTime<Utc>,
    /// Additional counterparty metadata as JSON
    pub metadata: serde_json::Value,
}

/// Counterparty exposure tracking
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct CounterpartyExposure {
    /// Unique counterparty exposure identifier
    pub id: Uuid,
    /// Counterparty this exposure relates to
    pub counterparty_id: String,
    /// Portfolio generating this exposure (if applicable)
    pub portfolio_id: Option<String>,
    /// Exposure type (Current, Potential, Settlement)
    pub exposure_type: String,
    /// Gross exposure amount before netting
    pub gross_exposure: Decimal,
    /// Net exposure amount after netting agreements
    pub net_exposure: Decimal,
    /// Collateral held from the counterparty
    pub collateral_held: Decimal,
    /// Collateral posted to the counterparty
    pub collateral_posted: Decimal,
    /// Current mark-to-market value
    pub mark_to_market: Decimal,
    /// Currency of the exposure (ISO 3-letter code)
    pub currency: String,
    /// Maturity bucket classification (0-1Y, 1-5Y, etc.)
    pub maturity_bucket: Option<String>,
    /// Risk weight for regulatory capital calculations
    pub risk_weight: Option<Decimal>,
    /// Date of the exposure calculation
    pub date: DateTime<Utc>,
}

/// Liquidity metrics for positions
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct LiquidityMetrics {
    /// Unique liquidity metrics identifier
    pub id: Uuid,
    /// Instrument symbol being analyzed
    pub symbol: String,
    /// Date of the liquidity analysis
    pub date: DateTime<Utc>,
    /// Average daily trading volume
    pub average_daily_volume: Decimal,
    /// Bid-ask spread in basis points
    pub bid_ask_spread_bps: Decimal,
    /// Market impact coefficient for large trades
    pub market_impact_coefficient: Option<Decimal>,
    /// Days to liquidate 10% of average daily volume
    pub days_to_liquidate_10pct: Option<Decimal>,
    /// Days to liquidate 50% of average daily volume
    pub days_to_liquidate_50pct: Option<Decimal>,
    /// Overall liquidity score (1-10 scale)
    pub liquidity_score: Option<Decimal>,
    /// High frequency trading volume ratio
    pub high_frequency_ratio: Option<Decimal>,
    /// Dark pool volume ratio
    pub dark_pool_ratio: Option<Decimal>,
    /// Price volatility measure
    pub volatility: Decimal,
    /// Amihud illiquidity measure
    pub amihud_illiquidity: Option<Decimal>,
}

/// Economic scenarios for scenario analysis
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct EconomicScenario {
    /// Unique economic scenario identifier
    pub id: Uuid,
    /// Human-readable scenario name
    pub name: String,
    /// Detailed description of the economic scenario
    pub description: String,
    /// Probability assignment for this scenario
    pub probability: Option<Decimal>,
    /// Time horizon for the scenario
    pub time_horizon: TimePeriod,
    /// GDP growth rate change in the scenario
    pub gdp_growth_rate: Option<Decimal>,
    /// Inflation rate change in the scenario
    pub inflation_rate: Option<Decimal>,
    /// Interest rate change in the scenario
    pub interest_rate_change: Option<Decimal>,
    /// Unemployment rate in the scenario
    pub unemployment_rate: Option<Decimal>,
    /// Currency pair shocks as JSON
    pub currency_shock: serde_json::Value,
    /// Commodity price shocks as JSON
    pub commodity_shock: serde_json::Value,
    /// Market index shocks as JSON
    pub equity_market_shock: serde_json::Value,
    /// Volatility regime changes as JSON
    pub volatility_shock: serde_json::Value,
    /// User who created this scenario
    pub created_by: String,
    /// Timestamp when the scenario was created
    pub created_at: DateTime<Utc>,
    /// Whether this scenario is currently active
    pub is_active: bool,
}

/// Risk report templates
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct RiskReportTemplate {
    /// Unique report template identifier
    pub id: Uuid,
    /// Human-readable template name
    pub name: String,
    /// Description of the report template
    pub description: String,
    /// Report type (Daily, Weekly, Monthly, Regulatory)
    pub report_type: String,
    /// Report structure and parameters as JSON
    pub template_config: serde_json::Value,
    /// Email distribution list as JSON
    pub recipients: serde_json::Value,
    /// Cron schedule for automated reports
    pub schedule_cron: Option<String>,
    /// Whether this template is currently active
    pub is_active: bool,
    /// User who created this template
    pub created_by: String,
    /// Timestamp when the template was created
    pub created_at: DateTime<Utc>,
    /// Timestamp when the template was last updated
    pub updated_at: DateTime<Utc>,
}

/// Generated risk reports
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct RiskReport {
    /// Unique risk report identifier
    pub id: Uuid,
    /// Template used to generate this report
    pub template_id: Uuid,
    /// Portfolio this report covers (if applicable)
    pub portfolio_id: Option<String>,
    /// Date the report covers
    pub report_date: DateTime<Utc>,
    /// Timestamp when the report was generated
    pub generated_at: DateTime<Utc>,
    /// User who generated the report
    pub generated_by: String,
    /// Full report content as JSON
    pub report_data: serde_json::Value,
    /// Path to generated PDF/Excel file
    pub file_path: Option<String>,
    /// Report status (Generated, Sent, Failed)
    pub status: String,
    /// Error message if report generation failed
    pub error_message: Option<String>,
    /// List of recipients who received the report as JSON
    pub recipients_sent: serde_json::Value,
}

/// Market data feeds configuration
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct MarketDataFeed {
    /// Unique market data feed identifier
    pub id: Uuid,
    /// Name of the data provider
    pub provider_name: String,
    /// Feed type (Real-time, End-of-day, Historical)
    pub feed_type: String,
    /// List of symbols covered by this feed as JSON
    pub symbols_covered: serde_json::Value,
    /// Connection parameters as JSON
    pub connection_config: serde_json::Value,
    /// Whether this is the primary feed (vs backup)
    pub is_primary: bool,
    /// Whether this feed is currently active
    pub is_active: bool,
    /// Latency SLA in milliseconds
    pub latency_sla_ms: Option<i32>,
    /// Uptime SLA as percentage
    pub uptime_sla_pct: Option<Decimal>,
    /// Timestamp of last heartbeat from the feed
    pub last_heartbeat: Option<DateTime<Utc>>,
    /// Timestamp when the record was created
    pub created_at: DateTime<Utc>,
    /// Timestamp when the record was last updated
    pub updated_at: DateTime<Utc>,
}

/// Risk calculation jobs queue
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct RiskCalculationJob {
    /// Unique job identifier
    pub id: Uuid,
    /// Job type (`VaR`, `StressTest`, Scenario, etc.)
    pub job_type: String,
    /// Portfolio to calculate risk for (if applicable)
    pub portfolio_id: Option<String>,
    /// Job-specific parameters as JSON
    pub parameters: serde_json::Value,
    /// Job priority (1-10, higher is more urgent)
    pub priority: i32,
    /// Job status (Queued, Running, Completed, Failed)
    pub status: String,
    /// Timestamp when job execution started
    pub started_at: Option<DateTime<Utc>>,
    /// Timestamp when job execution completed
    pub completed_at: Option<DateTime<Utc>>,
    /// Job completion percentage (0-100)
    pub progress_pct: Option<Decimal>,
    /// Job result data as JSON
    pub result_data: Option<serde_json::Value>,
    /// Error message if job failed
    pub error_message: Option<String>,
    /// Number of times this job has been retried
    pub retry_count: i32,
    /// Maximum number of retry attempts allowed
    pub max_retries: i32,
    /// User who created this job
    pub created_by: String,
    /// Timestamp when the job was created
    pub created_at: DateTime<Utc>,
}

/// Custom risk metrics configuration
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct CustomRiskMetric {
    /// Unique custom risk metric identifier
    pub id: Uuid,
    /// Human-readable metric name
    pub name: String,
    /// Description of what this metric measures
    pub description: String,
    /// Mathematical formula or SQL query for calculation
    pub formula: String,
    /// Configurable parameters as JSON
    pub parameters: serde_json::Value,
    /// Output type (Number, Percentage, Currency)
    pub output_type: String,
    /// Calculation frequency
    pub frequency: TimePeriod,
    /// Metric scope (Portfolio, Position, Global)
    pub scope: String,
    /// Whether this metric is currently active
    pub is_active: bool,
    /// User who created this metric
    pub created_by: String,
    /// Timestamp when the metric was created
    pub created_at: DateTime<Utc>,
    /// Timestamp when the metric was last updated
    pub updated_at: DateTime<Utc>,
}

/// Calculated custom risk metrics
#[derive(Debug, Clone, Serialize, Deserialize, FromRow)]
pub struct CustomRiskMetricResult {
    /// Unique metric result identifier
    pub id: Uuid,
    /// Custom risk metric that was calculated
    pub metric_id: Uuid,
    /// Portfolio this result applies to (if applicable)
    pub portfolio_id: Option<String>,
    /// Symbol this result applies to (if applicable)
    pub symbol: Option<String>,
    /// Date when the calculation was performed
    pub calculation_date: DateTime<Utc>,
    /// Calculated metric value
    pub value: Decimal,
    /// Additional calculation details as JSON
    pub metadata: serde_json::Value,
}

/// Common financial calculations and utilities
#[derive(Debug)]
pub struct FinancialCalculations;

impl FinancialCalculations {
    /// Calculate annualized volatility from daily returns
    #[allow(clippy::arithmetic_side_effects)]
    pub fn annualized_volatility(daily_vol: Decimal) -> Decimal {
        daily_vol * Decimal::from(16_i32) // sqrt(252) ≈ 15.87, using 16 as approximation
    }

    /// Calculate Sharpe ratio
    #[allow(clippy::arithmetic_side_effects)]
    pub fn sharpe_ratio(
        returns: Decimal,
        risk_free_rate: Decimal,
        volatility: Decimal,
    ) -> Option<Decimal> {
        if volatility == Decimal::ZERO {
            None
        } else {
            Some((returns - risk_free_rate) / volatility)
        }
    }

    /// Calculate maximum drawdown
    ///
    /// # Returns
    /// - `Ok(Decimal)` - Maximum drawdown as a percentage
    ///
    /// - `Err(String)` - Error if peak is zero or calculation fails
    ///
    ///
    /// # Errors
    /// Returns error if the operation fails
    /// # Errors
    /// Returns error if peak is zero or negative, preventing valid drawdown calculation
    #[allow(clippy::arithmetic_side_effects)]
    pub fn max_drawdown(peak: Decimal, trough: Decimal) -> Result<Decimal, String> {
        if peak == Decimal::ZERO {
            // CRITICAL: Zero peak value prevents meaningful drawdown calculation
            // This could indicate:
            // 1. No historical high water mark (data corruption)
            // 2. Portfolio started with zero value (configuration error)
            // 3. Missing performance data
            return Err(
                "Cannot calculate drawdown with zero peak value - this may indicate \
                 missing performance data or data corruption"
                    .to_owned(),
            );
        }

        if peak < Decimal::ZERO {
            return Err(format!("Invalid negative peak value: {}", peak));
        }

        Ok(((trough - peak) / peak) * Decimal::from(100_i32))
    }
}

/// Portfolio aggregation utilities
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PortfolioSummary {
    /// Total market value of all positions
    pub total_market_value: Decimal,
    /// Breakdown of exposure by currency
    pub currency_breakdown: HashMap<String, Decimal>,
    /// Breakdown of exposure by market sector
    pub sector_breakdown: HashMap<MarketSector, Decimal>,
    /// Breakdown of exposure by asset class
    pub asset_class_breakdown: HashMap<AssetClass, Decimal>,
    /// Top positions by weight (Symbol, Weight)
    pub top_positions: Vec<(String, Decimal)>,
    /// Total number of positions in the portfolio
    pub number_of_positions: usize,
    /// Weight of the largest single position
    pub largest_position_weight: Decimal,
    /// Effective number of positions (diversification measure)
    pub effective_number_of_positions: Decimal,
    /// Gross exposure (sum of absolute position values)
    pub gross_exposure: Decimal,
    /// Net exposure (sum of signed position values)
    pub net_exposure: Decimal,
    /// Portfolio beta relative to benchmark
    pub beta: Option<Decimal>,
    /// Tracking error relative to benchmark
    pub tracking_error: Option<Decimal>,
}

/// Risk factor model utilities
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FactorModel {
    /// Name of the risk factor model
    pub model_name: String,
    /// List of risk factors in the model
    pub factors: Vec<String>,
    /// Factor loadings for each symbol (Symbol -> Factor -> Loading)
    pub factor_loadings: HashMap<String, HashMap<String, Decimal>>,
    /// Covariance matrix between factors
    pub factor_covariance_matrix: HashMap<String, HashMap<String, Decimal>>,
    /// Specific (idiosyncratic) risks for each symbol
    pub specific_risks: HashMap<String, Decimal>,
    /// R-squared values for each symbol's factor model fit
    pub r_squared: HashMap<String, Decimal>,
    /// Timestamp when the model was last updated
    pub last_updated: DateTime<Utc>,
}

/// Validation utilities
impl Instrument {
    /// Validates the instrument configuration
    ///
    /// # Errors
    /// Returns error if symbol or name is empty, or currency is not a 3-character ISO code
    pub fn validate(&self) -> Result<(), String> {
        if self.symbol.is_empty() {
            return Err("Symbol cannot be empty".to_owned());
        }

        if self.name.is_empty() {
            return Err("Instrument name cannot be empty".to_owned());
        }

        if self.currency.len() != 3 {
            return Err("Currency must be 3-character ISO code".to_owned());
        }

        Ok(())
    }
}

impl Portfolio {
    /// Validates the portfolio configuration
    ///
    /// # Errors
    ///
    /// Returns error if:
    /// - Portfolio name is empty
    /// - Currency is not a 3-character ISO code
    /// - No instruments are defined
    ///
    /// # Errors
    /// Returns error if portfolio ID or name is empty, currency is invalid, or VAR limit is not positive
    pub fn validate(&self) -> Result<(), String> {
        if self.id.is_empty() {
            return Err("Portfolio ID cannot be empty".to_owned());
        }

        if self.name.is_empty() {
            return Err("Portfolio name cannot be empty".to_owned());
        }

        if self.base_currency.len() != 3 {
            return Err("Base currency must be 3-character ISO code".to_owned());
        }

        if let Some(var_limit) = self.var_limit {
            if var_limit <= Decimal::ZERO {
                return Err("VaR limit must be positive".to_owned());
            }
        }

        Ok(())
    }
}

#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
    use super::*;

    #[test]
    fn test_decimal_calculations() {
        let daily_vol = Decimal::from_str_exact("0.02").unwrap();
        let annual_vol = FinancialCalculations::annualized_volatility(daily_vol);
        assert!(annual_vol > daily_vol);

        let returns = Decimal::from_str_exact("0.12").unwrap();
        let risk_free = Decimal::from_str_exact("0.03").unwrap();
        let volatility = Decimal::from_str_exact("0.15").unwrap();

        let sharpe = FinancialCalculations::sharpe_ratio(returns, risk_free, volatility).unwrap();
        assert!(sharpe > Decimal::ZERO);

        let peak = Decimal::from(100_i32);
        let trough = Decimal::from(85_i32);
        let drawdown = FinancialCalculations::max_drawdown(peak, trough);
        assert_eq!(drawdown, Ok(Decimal::from(-15_i32)));
    }

    #[test]
    fn test_instrument_validation() {
        let valid_instrument = Instrument {
            id: Uuid::new_v4(),
            symbol: "AAPL".to_string(),
            isin: Some("US0378331005".to_string()),
            cusip: None,
            bloomberg_id: Some("AAPL UW Equity".to_string()),
            reuters_id: None,
            name: "Apple Inc.".to_string(),
            instrument_type: InstrumentType::Equity,
            asset_class: AssetClass::Equities,
            sector: Some(MarketSector::Technology),
            currency: "USD".to_string(),
            exchange: Some("NASDAQ".to_string()),
            tick_size: Some(Decimal::from_str_exact("0.01").unwrap()),
            lot_size: Some(Decimal::from(1_i32)),
            multiplier: Some(Decimal::from(1_i32)),
            maturity_date: None,
            strike_price: None,
            option_type: None,
            underlying_symbol: None,
            is_active: true,
            created_at: Utc::now(),
            updated_at: Utc::now(),
            metadata: serde_json::json!({}),
        };

        valid_instrument.validate().unwrap();

        // Test invalid currency
        let invalid_instrument = Instrument {
            currency: "INVALID".to_string(),
            ..valid_instrument
        };

        assert!(invalid_instrument.validate().is_err());
    }

    #[test]
    fn test_portfolio_validation() {
        let valid_portfolio = Portfolio {
            id: "TEST_PORTFOLIO".to_string(),
            name: "Test Portfolio".to_string(),
            description: Some("Test portfolio for validation".to_string()),
            base_currency: "USD".to_string(),
            portfolio_type: "Strategy".to_string(),
            inception_date: Utc::now(),
            manager_id: "test_manager".to_string(),
            benchmark: Some("SPY".to_string()),
            risk_budget: Some(Decimal::from_str_exact("0.15").unwrap()),
            var_limit: Some(Decimal::from(100_000_i32)),
            max_drawdown_limit: Some(Decimal::from_str_exact("0.20").unwrap()),
            is_active: true,
            created_at: Utc::now(),
            updated_at: Utc::now(),
            metadata: serde_json::json!({}),
        };

        valid_portfolio.validate().unwrap();

        // Test invalid VaR limit
        let invalid_portfolio = Portfolio {
            var_limit: Some(Decimal::from(-1_000_i32)),
            ..valid_portfolio
        };

        assert!(invalid_portfolio.validate().is_err());
    }
}
