// compliance_service.rs
// SOX and MiFID II Compliance Service Integration
// Critical for production deployment - regulatory compliance

use anyhow::{Context, Result};
use serde_json::Value as JsonValue;
use sqlx::{PgPool, Row};
use tokio::time::Instant;
use tracing::{error, info, warn};
use uuid::Uuid;

use crate::error::TradingServiceError;
use rust_decimal::{prelude::ToPrimitive, Decimal};

/// SOX and MiFID II Compliance Service
///
/// Handles all regulatory audit trail requirements
#[derive(Clone)]
pub struct ComplianceService {
    db_pool: PgPool,
    config: ComplianceConfig,
}

#[derive(Clone, Debug)]
pub struct ComplianceConfig {
    pub enable_sox_audit: bool,
    pub enable_mifid_reporting: bool,
    pub enable_position_monitoring: bool,
    pub enable_best_execution_analysis: bool,
    pub kill_switch_enabled: bool,
    pub max_position_utilization: f64, // 0.0 to 1.0
    pub critical_risk_threshold: f64,
}

impl Default for ComplianceConfig {
    fn default() -> Self {
        Self {
            enable_sox_audit: true,
            enable_mifid_reporting: true,
            enable_position_monitoring: true,
            enable_best_execution_analysis: true,
            kill_switch_enabled: true,
            max_position_utilization: 0.95, // 95% of limit
            critical_risk_threshold: 0.8,   // 80% risk threshold
        }
    }
}

/// `Trade` execution data for compliance audit
#[derive(Debug, Clone)]
pub struct TradeExecutionData {
    pub trade_id: Uuid,
    pub user_id: Uuid,
    pub symbol: String,
    pub side: String, // BUY, SELL, SHORT, COVER
    pub quantity: Decimal,
    pub price: Option<Decimal>,
    pub order_type: String,
    pub trade_value: Decimal,
    pub commission: Option<Decimal>,
    pub order_timestamp: chrono::DateTime<chrono::Utc>,
    pub execution_timestamp: Option<chrono::DateTime<chrono::Utc>>,
    pub trade_status: String,
    pub risk_assessment: Option<JsonValue>,
    pub compliance_flags: Option<JsonValue>,
}

/// MiFID II transaction report data
#[derive(Debug, Clone)]
pub struct MiFidTransactionData {
    pub trade_id: Uuid,
    pub instrument_id: String,
    pub currency: String,
    pub price: Option<Decimal>,
    pub quantity: Decimal,
    pub trading_venue: String,
    pub transaction_timestamp: chrono::DateTime<chrono::Utc>,
    pub instrument_classification: Option<String>,
    pub isin_code: Option<String>,
    pub best_execution_venue: Option<String>,
}

/// `Position` limit check data
#[derive(Debug, Clone)]
pub struct PositionLimitData {
    pub user_id: Uuid,
    pub instrument_id: String,
    pub position_size: Decimal,
    pub position_limit: Decimal,
    pub instrument_type: String,
    pub position_direction: String, // LONG, SHORT, NET
}

/// Kill switch activation data
#[derive(Debug, Clone)]
pub struct KillSwitchData {
    pub switch_type: String, // MANUAL, AUTOMATIC, REGULATORY, RISK, TECHNICAL
    pub trigger_reason: String,
    pub severity_level: String, // LOW, MEDIUM, HIGH, CRITICAL
    pub triggered_by_user: Option<Uuid>,
    pub portfolio_value: Option<Decimal>,
    pub daily_pnl: Option<Decimal>,
    pub var_breach_amount: Option<Decimal>,
    pub active_positions: Option<i32>,
    pub pending_orders: Option<i32>,
}

/// Best execution analysis data
#[derive(Debug, Clone)]
pub struct BestExecutionData {
    pub trade_id: Uuid,
    pub primary_venue: String,
    pub reference_price: Decimal,
    pub execution_price: Decimal,
    pub explicit_costs: Decimal, // Commissions, fees
    pub implicit_costs: Decimal, // Spread, market impact
    pub alternative_venues: Option<JsonValue>,
    pub market_conditions: Option<JsonValue>,
}

impl ComplianceService {
    /// Create new compliance service instance
    pub fn new(db_pool: PgPool, config: ComplianceConfig) -> Self {
        Self { db_pool, config }
    }

    /// Log SOX compliance audit trail for trade activity
    ///
    /// Required for Sarbanes-Oxley Section 404 internal controls
    pub async fn log_sox_trade_audit(&self, data: &TradeExecutionData) -> Result<Uuid> {
        if !self.config.enable_sox_audit {
            warn!(feature = "sox_audit", "Compliance feature disabled — returning nil audit ID. Configure enable_sox_audit=true for production.");
            return Ok(Uuid::nil());
        }

        let start_time = Instant::now();

        let audit_id: Uuid = sqlx::query_scalar(
            r#"
            SELECT log_sox_trade_activity(
                $1::UUID, $2::UUID, $3::VARCHAR, $4::VARCHAR, $5::DECIMAL,
                $6::DECIMAL, $7::VARCHAR, $8::DECIMAL, $9::TIMESTAMPTZ,
                $10::JSONB, $11::JSONB
            ) as audit_id
            "#,
        )
        .bind(data.trade_id)
        .bind(data.user_id)
        .bind(&data.symbol)
        .bind(&data.side)
        .bind(data.quantity)
        .bind(data.price)
        .bind(&data.order_type)
        .bind(data.trade_value)
        .bind(data.order_timestamp)
        .bind(&data.risk_assessment)
        .bind(&data.compliance_flags)
        .fetch_one(&self.db_pool)
        .await
        .context("Failed to log SOX trade audit")?;

        let duration = start_time.elapsed();
        info!(
            "SOX audit logged for trade {} in {:?}",
            data.trade_id, duration
        );

        Ok(audit_id)
    }

    /// Create MiFID II transaction report
    ///
    /// Required for European Markets in Financial Instruments Directive II
    pub async fn create_mifid_report(&self, data: &MiFidTransactionData) -> Result<Uuid> {
        if !self.config.enable_mifid_reporting {
            warn!(feature = "mifid_reporting", "Compliance feature disabled — returning nil report ID. Configure enable_mifid_reporting=true for production.");
            return Ok(Uuid::nil());
        }

        let start_time = Instant::now();

        let report_id: Uuid = sqlx::query_scalar(
            r#"
            SELECT create_mifid_transaction_report(
                $1::UUID, $2::VARCHAR, $3::VARCHAR, $4::DECIMAL,
                $5::DECIMAL, $6::VARCHAR, $7::TIMESTAMPTZ
            ) as report_id
            "#,
        )
        .bind(data.trade_id)
        .bind(&data.instrument_id)
        .bind(&data.currency)
        .bind(data.price)
        .bind(data.quantity)
        .bind(&data.trading_venue)
        .bind(data.transaction_timestamp)
        .fetch_one(&self.db_pool)
        .await
        .context("Failed to create MiFID II transaction report")?;

        let duration = start_time.elapsed();
        info!(
            "MiFID II report created for trade {} in {:?}",
            data.trade_id, duration
        );

        Ok(report_id)
    }

    /// Check position limits and log compliance status
    ///
    /// Required for MiFID II Article 57 position limits
    pub async fn check_position_limits(&self, data: &PositionLimitData) -> Result<(Uuid, bool)> {
        if !self.config.enable_position_monitoring {
            warn!(feature = "position_monitoring", "Compliance feature disabled — returning nil audit ID. Configure enable_position_monitoring=true for production.");
            return Ok((Uuid::nil(), false));
        }

        let start_time = Instant::now();

        let audit_id: Uuid = sqlx::query_scalar(
            r#"
            SELECT check_position_limits(
                $1::UUID, $2::VARCHAR, $3::DECIMAL, $4::DECIMAL
            ) as audit_id
            "#,
        )
        .bind(data.user_id)
        .bind(&data.instrument_id)
        .bind(data.position_size)
        .bind(data.position_limit)
        .fetch_one(&self.db_pool)
        .await
        .context("Failed to check position limits")?;

        // Check if breach occurred
        let is_breach: bool = sqlx::query_scalar::<sqlx::Postgres, bool>(
            "SELECT is_breach FROM position_limits_audit WHERE id = $1",
        )
        .bind(audit_id)
        .fetch_one(&self.db_pool)
        .await
        .context("Failed to get breach status")?;

        let duration = start_time.elapsed();

        if is_breach {
            warn!(
                "Position limit breach detected for user {} instrument {} in {:?}",
                data.user_id, data.instrument_id, duration
            );

            // Check if kill switch should be activated
            let utilization = (data.position_size / data.position_limit)
                .to_f64()
                .unwrap_or(0.0);
            if utilization > self.config.max_position_utilization {
                let kill_switch_data = KillSwitchData {
                    switch_type: "AUTOMATIC".to_string(),
                    trigger_reason: format!(
                        "Position limit breach: {}% utilization on {}",
                        (utilization * 100.0) as i32,
                        data.instrument_id
                    ),
                    severity_level: if utilization > 1.2 {
                        "CRITICAL"
                    } else {
                        "HIGH"
                    }
                    .to_string(),
                    triggered_by_user: None,
                    portfolio_value: None,
                    daily_pnl: None,
                    var_breach_amount: Some(data.position_size - data.position_limit),
                    active_positions: None,
                    pending_orders: None,
                };

                self.activate_kill_switch(&kill_switch_data).await?;
            }
        } else {
            info!(
                "Position limit check passed for user {} instrument {} in {:?}",
                data.user_id, data.instrument_id, duration
            );
        }

        Ok((audit_id, is_breach))
    }

    /// Activate kill switch and create audit trail
    ///
    /// Critical risk management and regulatory compliance
    pub async fn activate_kill_switch(&self, data: &KillSwitchData) -> Result<Uuid> {
        if !self.config.kill_switch_enabled {
            warn!("Kill switch activation attempted but kill switch is disabled");
            return Err(anyhow::anyhow!("Kill switch is disabled"));
        }

        let start_time = Instant::now();

        let audit_id: Uuid = sqlx::query_scalar(
            r#"
            SELECT activate_kill_switch(
                $1::VARCHAR, $2::VARCHAR, $3::VARCHAR, $4::UUID,
                $5::DECIMAL, $6::DECIMAL
            ) as audit_id
            "#,
        )
        .bind(&data.switch_type)
        .bind(&data.trigger_reason)
        .bind(&data.severity_level)
        .bind(data.triggered_by_user)
        .bind(data.portfolio_value)
        .bind(data.daily_pnl)
        .fetch_one(&self.db_pool)
        .await
        .context("Failed to activate kill switch")?;

        let duration = start_time.elapsed();
        error!(
            "KILL SWITCH ACTIVATED: {} - {} (Audit ID: {}) in {:?}",
            data.switch_type, data.trigger_reason, audit_id, duration
        );

        // Kill switch actions: persist halt state for other services to detect
        error!(
            "KILL SWITCH ACTION: Halting all trading. Type={}, Reason={}, Severity={}",
            data.switch_type, data.trigger_reason, data.severity_level
        );

        // Record halt state in database (best-effort — audit record is already saved)
        if let Err(e) = sqlx::query(
            "INSERT INTO system_state (service_name, trading_halted, halt_reason, halted_at) \
             VALUES ('trading_service', true, $1, NOW()) \
             ON CONFLICT (service_name) DO UPDATE SET trading_halted = true, halt_reason = $1, halted_at = NOW()"
        )
        .bind(&data.trigger_reason)
        .execute(&self.db_pool)
        .await
        {
            error!("Failed to persist kill switch state: {} (audit record saved as {})", e, audit_id);
        }

        Ok(audit_id)
    }

    /// Analyze best execution for MiFID II Article 27 compliance
    pub async fn analyze_best_execution(&self, data: &BestExecutionData) -> Result<Uuid> {
        if !self.config.enable_best_execution_analysis {
            warn!(feature = "best_execution_analysis", "Compliance feature disabled — returning nil analysis ID. Configure enable_best_execution_analysis=true for production.");
            return Ok(Uuid::nil());
        }

        let start_time = Instant::now();

        let analysis_id: Uuid = sqlx::query_scalar(
            r#"
            SELECT analyze_best_execution(
                $1::UUID, $2::VARCHAR, $3::DECIMAL, $4::DECIMAL,
                $5::DECIMAL, $6::DECIMAL
            ) as analysis_id
            "#,
        )
        .bind(data.trade_id)
        .bind(&data.primary_venue)
        .bind(data.reference_price)
        .bind(data.execution_price)
        .bind(data.explicit_costs)
        .bind(data.implicit_costs)
        .fetch_one(&self.db_pool)
        .await
        .context("Failed to analyze best execution")?;

        // Get analysis results
        let analysis_result = sqlx::query(
            r#"
            SELECT execution_quality_grade, meets_best_execution,
                   price_improvement_percentage, overall_score
            FROM best_execution_analysis WHERE id = $1
            "#,
        )
        .bind(analysis_id)
        .fetch_one(&self.db_pool)
        .await
        .context("Failed to get best execution analysis result")?;

        let duration = start_time.elapsed();

        if let Some(meets_best_execution) =
            analysis_result.get::<Option<bool>, _>("meets_best_execution")
        {
            if meets_best_execution {
                info!(
                    "Best execution analysis PASSED for trade {} (Grade: {}) in {:?}",
                    data.trade_id,
                    analysis_result
                        .get::<Option<String>, _>("execution_quality_grade")
                        .unwrap_or("Unknown".to_string()),
                    duration
                );
            } else {
                warn!(
                    "Best execution analysis FAILED for trade {} (Grade: {}) in {:?}",
                    data.trade_id,
                    analysis_result
                        .get::<Option<String>, _>("execution_quality_grade")
                        .unwrap_or("Unknown".to_string()),
                    duration
                );
            }
        }

        Ok(analysis_id)
    }

    /// Get compliance dashboard data
    pub async fn get_compliance_dashboard(&self) -> Result<ComplianceDashboard> {
        let mut dashboard = ComplianceDashboard::default();

        // SOX audit statistics (last 24 hours)
        if self.config.enable_sox_audit {
            let sox_stats = sqlx::query(
                r#"
                SELECT
                    COUNT(*) as total_trades,
                    COUNT(CASE WHEN trade_status = 'FILLED' THEN 1 END) as filled_trades,
                    COUNT(CASE WHEN compliance_flags IS NOT NULL THEN 1 END) as flagged_trades,
                    SUM(trade_value) as total_value
                FROM sox_trade_audit
                WHERE created_at >= NOW() - INTERVAL '24 hours'
                "#,
            )
            .fetch_one(&self.db_pool)
            .await?;

            dashboard.sox_trades_24h =
                sox_stats.get::<Option<i64>, _>("total_trades").unwrap_or(0) as u32;
            dashboard.sox_filled_trades_24h = sox_stats
                .get::<Option<i64>, _>("filled_trades")
                .unwrap_or(0) as u32;
            dashboard.sox_flagged_trades_24h = sox_stats
                .get::<Option<i64>, _>("flagged_trades")
                .unwrap_or(0) as u32;
            dashboard.sox_total_value_24h = sox_stats
                .get::<Option<Decimal>, _>("total_value")
                .unwrap_or(Decimal::ZERO);
        }

        // Position limit breaches (last 24 hours)
        if self.config.enable_position_monitoring {
            let position_stats = sqlx::query(
                r#"
                SELECT
                    COUNT(*) as total_checks,
                    COUNT(CASE WHEN is_breach THEN 1 END) as breaches,
                    AVG(limit_utilization) as avg_utilization,
                    MAX(limit_utilization) as max_utilization
                FROM position_limits_audit
                WHERE assessment_timestamp >= NOW() - INTERVAL '24 hours'
                "#,
            )
            .fetch_one(&self.db_pool)
            .await?;

            dashboard.position_checks_24h = position_stats
                .get::<Option<i64>, _>("total_checks")
                .unwrap_or(0) as u32;
            dashboard.position_breaches_24h = position_stats
                .get::<Option<i64>, _>("breaches")
                .unwrap_or(0) as u32;
            dashboard.avg_position_utilization = position_stats
                .get::<Option<Decimal>, _>("avg_utilization")
                .and_then(|d| d.to_f64())
                .unwrap_or(0.0);
            dashboard.max_position_utilization = position_stats
                .get::<Option<Decimal>, _>("max_utilization")
                .and_then(|d| d.to_f64())
                .unwrap_or(0.0);
        }

        // Kill switch activations (last 7 days)
        let kill_switch_stats = sqlx::query(
            r#"
            SELECT
                COUNT(*) as total_activations,
                COUNT(CASE WHEN severity_level = 'CRITICAL' THEN 1 END) as critical_activations,
                MAX(trigger_timestamp) as last_activation
            FROM kill_switch_audit
            WHERE trigger_timestamp >= NOW() - INTERVAL '7 days'
            "#,
        )
        .fetch_one(&self.db_pool)
        .await?;

        dashboard.kill_switch_activations_7d = kill_switch_stats
            .get::<Option<i64>, _>("total_activations")
            .unwrap_or(0) as u32;
        dashboard.critical_activations_7d = kill_switch_stats
            .get::<Option<i64>, _>("critical_activations")
            .unwrap_or(0) as u32;
        dashboard.last_kill_switch_activation =
            kill_switch_stats.get::<Option<chrono::DateTime<chrono::Utc>>, _>("last_activation");

        // Best execution analysis (last 24 hours)
        if self.config.enable_best_execution_analysis {
            let execution_stats = sqlx::query(
                r#"
                SELECT
                    COUNT(*) as total_analyses,
                    COUNT(CASE WHEN meets_best_execution THEN 1 END) as passed_analyses,
                    AVG(overall_score) as avg_score,
                    AVG(price_improvement_percentage) as avg_price_improvement
                FROM best_execution_analysis
                WHERE analysis_timestamp >= NOW() - INTERVAL '24 hours'
                "#,
            )
            .fetch_one(&self.db_pool)
            .await?;

            dashboard.execution_analyses_24h = execution_stats
                .get::<Option<i64>, _>("total_analyses")
                .unwrap_or(0) as u32;
            dashboard.passed_execution_analyses_24h = execution_stats
                .get::<Option<i64>, _>("passed_analyses")
                .unwrap_or(0) as u32;
            dashboard.avg_execution_score = execution_stats
                .get::<Option<Decimal>, _>("avg_score")
                .and_then(|d| d.to_f64())
                .unwrap_or(0.0);
            dashboard.avg_price_improvement = execution_stats
                .get::<Option<Decimal>, _>("avg_price_improvement")
                .and_then(|d| d.to_f64())
                .unwrap_or(0.0);
        }

        Ok(dashboard)
    }

    /// Health check for compliance service
    pub async fn health_check(&self) -> Result<bool> {
        let result = sqlx::query_scalar::<sqlx::Postgres, i32>("SELECT 1 as health_check")
            .fetch_one(&self.db_pool)
            .await;

        match result {
            Ok(_) => Ok(true),
            Err(e) => {
                error!("Compliance service health check failed: {}", e);
                Ok(false)
            },
        }
    }
}

/// Compliance dashboard data structure
#[derive(Debug, Default)]
pub struct ComplianceDashboard {
    // SOX Statistics
    pub sox_trades_24h: u32,
    pub sox_filled_trades_24h: u32,
    pub sox_flagged_trades_24h: u32,
    pub sox_total_value_24h: Decimal,

    // Position Limit Statistics
    pub position_checks_24h: u32,
    pub position_breaches_24h: u32,
    pub avg_position_utilization: f64,
    pub max_position_utilization: f64,

    // Kill Switch Statistics
    pub kill_switch_activations_7d: u32,
    pub critical_activations_7d: u32,
    pub last_kill_switch_activation: Option<chrono::DateTime<chrono::Utc>>,

    // Best Execution Statistics
    pub execution_analyses_24h: u32,
    pub passed_execution_analyses_24h: u32,
    pub avg_execution_score: f64,
    pub avg_price_improvement: f64,
}

/// Compliance service errors
#[derive(Debug, thiserror::Error)]
pub enum ComplianceError {
    #[error("Database error: {0}")]
    Database(#[from] sqlx::Error),

    #[error("Configuration error: {0}")]
    Config(String),

    #[error("Kill switch error: {0}")]
    KillSwitch(String),

    #[error("Audit trail error: {0}")]
    AuditTrail(String),

    #[error("Position limit error: {0}")]
    PositionLimit(String),
}

impl From<ComplianceError> for TradingServiceError {
    fn from(err: ComplianceError) -> Self {
        TradingServiceError::Internal {
            message: err.to_string(),
        }
    }
}
