-- Migration 002: Create risk management and performance tracking tables
-- This migration adds comprehensive risk monitoring and performance metrics capabilities

-- Risk metrics table - comprehensive risk tracking
CREATE TABLE IF NOT EXISTS risk_metrics (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    account_id VARCHAR(64), -- Account identifier
    metric_type VARCHAR(50) NOT NULL CHECK (metric_type IN (
        'exposure', 'var', 'drawdown', 'violation', 'concentration',
        'leverage', 'margin', 'volatility', 'beta', 'correlation'
    )),
    symbol VARCHAR(32), -- NULL for portfolio-level metrics
    value DECIMAL(20, 8) NOT NULL, -- Metric value with high precision
    threshold DECIMAL(20, 8), -- Risk threshold
    severity VARCHAR(20) NOT NULL DEFAULT 'low' CHECK (severity IN ('low', 'medium', 'high', 'critical')),
    description TEXT NOT NULL, -- Human-readable description
    timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    metadata JSONB -- Additional risk data
);

-- Risk violations table - audit trail of risk breaches
CREATE TABLE IF NOT EXISTS risk_violations (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    account_id VARCHAR(64),
    violation_type VARCHAR(50) NOT NULL,
    symbol VARCHAR(32),
    threshold_value DECIMAL(20, 8) NOT NULL,
    actual_value DECIMAL(20, 8) NOT NULL,
    severity VARCHAR(20) NOT NULL CHECK (severity IN ('warning', 'error', 'critical')),
    description TEXT NOT NULL,
    action_taken VARCHAR(100), -- Action taken in response
    resolved_at TIMESTAMP WITH TIME ZONE, -- When violation was resolved
    timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    metadata JSONB
);

-- Daily statistics table - comprehensive daily trading metrics
CREATE TABLE IF NOT EXISTS daily_stats (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    account_id VARCHAR(64),
    date DATE NOT NULL, -- Trading date
    total_trades INTEGER NOT NULL DEFAULT 0,
    total_volume BIGINT NOT NULL DEFAULT 0,
    gross_pnl BIGINT NOT NULL DEFAULT 0, -- Gross P&L in fixed-point cents
    net_pnl BIGINT NOT NULL DEFAULT 0, -- Net P&L after fees in fixed-point cents
    fees_paid BIGINT NOT NULL DEFAULT 0, -- Total fees paid in fixed-point cents
    winning_trades INTEGER NOT NULL DEFAULT 0,
    losing_trades INTEGER NOT NULL DEFAULT 0,
    largest_win BIGINT NOT NULL DEFAULT 0, -- Largest winning trade
    largest_loss BIGINT NOT NULL DEFAULT 0, -- Largest losing trade (negative)
    max_drawdown DECIMAL(10, 4) NOT NULL DEFAULT 0, -- Maximum drawdown percentage
    max_position_size BIGINT NOT NULL DEFAULT 0, -- Maximum position size held
    avg_trade_size BIGINT NOT NULL DEFAULT 0, -- Average trade size
    sharpe_ratio DECIMAL(10, 4), -- Sharpe ratio
    win_rate DECIMAL(5, 4), -- Win rate percentage (0-1)
    profit_factor DECIMAL(10, 4), -- Profit factor
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    
    -- Ensure unique stats per account-date combination
    UNIQUE(account_id, date)
);

-- Performance metrics table - system and trading performance tracking
CREATE TABLE IF NOT EXISTS performance_metrics (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    metric_name VARCHAR(100) NOT NULL, -- e.g., "latency_p50", "latency_p95", "throughput"
    metric_value DECIMAL(20, 8) NOT NULL, -- Metric value
    unit VARCHAR(50) NOT NULL, -- "nanoseconds", "ops_per_second", "percent", etc.
    component VARCHAR(100) NOT NULL, -- "order_processing", "market_data", "risk_engine"
    timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
    tags JSONB, -- Additional tags for grouping/filtering
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
    
    -- Composite indexes will be created after table creation
);

-- Audit logs table - comprehensive audit trail for compliance
CREATE TABLE IF NOT EXISTS audit_logs (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    event_type VARCHAR(50) NOT NULL, -- "order_placed", "trade_executed", "position_updated"
    entity_type VARCHAR(50) NOT NULL, -- "order", "fill", "position", "risk_metric"
    entity_id UUID NOT NULL, -- ID of the affected entity
    account_id VARCHAR(64),
    user_id VARCHAR(64),
    action VARCHAR(50) NOT NULL, -- "create", "update", "delete", "execute"
    old_values JSONB, -- Previous state (for updates)
    new_values JSONB, -- New state
    timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
    source VARCHAR(100) NOT NULL, -- System component that generated the event
    correlation_id UUID, -- For tracking related events
    session_id VARCHAR(128), -- User session identifier
    ip_address INET, -- Client IP address
    user_agent TEXT, -- Client user agent
    metadata JSONB, -- Additional audit metadata
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);

-- Strategy performance table - track individual strategy performance
CREATE TABLE IF NOT EXISTS strategy_performance (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    strategy_name VARCHAR(100) NOT NULL,
    account_id VARCHAR(64),
    date DATE NOT NULL,
    trades_count INTEGER NOT NULL DEFAULT 0,
    total_pnl BIGINT NOT NULL DEFAULT 0, -- P&L in fixed-point cents
    win_rate DECIMAL(5, 4), -- Win rate (0-1)
    sharpe_ratio DECIMAL(10, 4),
    max_drawdown DECIMAL(10, 4),
    avg_trade_duration INTERVAL, -- Average time positions are held
    total_volume BIGINT NOT NULL DEFAULT 0,
    risk_adjusted_return DECIMAL(10, 4),
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    
    UNIQUE(strategy_name, account_id, date)
);

-- Symbol statistics table - per-symbol performance and characteristics
CREATE TABLE IF NOT EXISTS symbol_stats (
    id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
    symbol VARCHAR(32) NOT NULL,
    date DATE NOT NULL,
    open_price BIGINT, -- Opening price in fixed-point cents
    high_price BIGINT, -- High price
    low_price BIGINT, -- Low price
    close_price BIGINT, -- Closing price
    volume BIGINT NOT NULL DEFAULT 0,
    trade_count INTEGER NOT NULL DEFAULT 0,
    vwap BIGINT, -- Volume-weighted average price
    volatility DECIMAL(10, 6), -- Daily volatility
    beta DECIMAL(10, 4), -- Beta relative to market
    correlation_spy DECIMAL(10, 4), -- Correlation to SPY
    avg_spread BIGINT, -- Average bid-ask spread
    liquidity_score DECIMAL(5, 2), -- Liquidity scoring
    created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
    
    UNIQUE(symbol, date)
);

-- Create composite indexes for risk_metrics table
CREATE INDEX IF NOT EXISTS idx_risk_metrics_composite ON risk_metrics(metric_type, severity, timestamp);
CREATE INDEX IF NOT EXISTS idx_risk_metrics_symbol_type ON risk_metrics(symbol, metric_type);
CREATE INDEX IF NOT EXISTS idx_risk_metrics_account_timestamp ON risk_metrics(account_id, timestamp);

-- Create composite indexes for performance_metrics table
CREATE INDEX IF NOT EXISTS idx_performance_metrics_component_name_timestamp ON performance_metrics(component, metric_name, timestamp);
CREATE INDEX IF NOT EXISTS idx_performance_metrics_timestamp ON performance_metrics(timestamp);

-- Create optimized indexes for performance queries

-- Risk metrics indexes
CREATE INDEX IF NOT EXISTS idx_risk_metrics_timestamp ON risk_metrics(timestamp);
CREATE INDEX IF NOT EXISTS idx_risk_metrics_account_id ON risk_metrics(account_id) WHERE account_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_risk_violations_timestamp ON risk_violations(timestamp);
CREATE INDEX IF NOT EXISTS idx_risk_violations_severity ON risk_violations(severity);

-- Daily stats indexes
CREATE INDEX IF NOT EXISTS idx_daily_stats_date ON daily_stats(date);
CREATE INDEX IF NOT EXISTS idx_daily_stats_account_date ON daily_stats(account_id, date);

-- Performance metrics indexes (optimized for time-series analysis)
CREATE INDEX IF NOT EXISTS idx_performance_metrics_name_timestamp ON performance_metrics(metric_name, timestamp);

-- Audit logs indexes (optimized for compliance queries)
CREATE INDEX IF NOT EXISTS idx_audit_logs_timestamp ON audit_logs(timestamp);
CREATE INDEX IF NOT EXISTS idx_audit_logs_entity ON audit_logs(entity_type, entity_id);
CREATE INDEX IF NOT EXISTS idx_audit_logs_account_id ON audit_logs(account_id) WHERE account_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_audit_logs_user_id ON audit_logs(user_id) WHERE user_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_audit_logs_correlation_id ON audit_logs(correlation_id) WHERE correlation_id IS NOT NULL;

-- Strategy performance indexes
CREATE INDEX IF NOT EXISTS idx_strategy_performance_name_date ON strategy_performance(strategy_name, date);
CREATE INDEX IF NOT EXISTS idx_strategy_performance_account_date ON strategy_performance(account_id, date);

-- Symbol stats indexes
CREATE INDEX IF NOT EXISTS idx_symbol_stats_symbol_date ON symbol_stats(symbol, date);
CREATE INDEX IF NOT EXISTS idx_symbol_stats_date ON symbol_stats(date);

-- Create triggers for automatic timestamp updates
CREATE TRIGGER trigger_daily_stats_updated_at
    BEFORE UPDATE ON daily_stats
    FOR EACH ROW
    EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER trigger_strategy_performance_updated_at
    BEFORE UPDATE ON strategy_performance
    FOR EACH ROW
    EXECUTE FUNCTION update_updated_at_column();

-- Create materialized view for real-time risk dashboard
CREATE MATERIALIZED VIEW IF NOT EXISTS risk_dashboard AS
SELECT 
    account_id,
    metric_type,
    symbol,
    AVG(value) as avg_value,
    MAX(value) as max_value,
    MIN(value) as min_value,
    COUNT(*) as measurement_count,
    COUNT(*) FILTER (WHERE severity IN ('high', 'critical')) as high_risk_count,
    MAX(timestamp) as last_updated
FROM risk_metrics 
WHERE timestamp >= NOW() - INTERVAL '1 day'
GROUP BY account_id, metric_type, symbol;

-- Create unique index on risk dashboard materialized view
CREATE UNIQUE INDEX IF NOT EXISTS idx_risk_dashboard_unique 
ON risk_dashboard(account_id, metric_type, COALESCE(symbol, ''));

-- Create materialized view for performance summary
CREATE MATERIALIZED VIEW IF NOT EXISTS performance_summary AS
SELECT 
    component,
    metric_name,
    unit,
    AVG(metric_value) as avg_value,
    PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY metric_value) as p50_value,
    PERCENTILE_CONT(0.95) WITHIN GROUP (ORDER BY metric_value) as p95_value,
    PERCENTILE_CONT(0.99) WITHIN GROUP (ORDER BY metric_value) as p99_value,
    MAX(metric_value) as max_value,
    MIN(metric_value) as min_value,
    COUNT(*) as sample_count,
    MAX(timestamp) as last_updated
FROM performance_metrics 
WHERE timestamp >= NOW() - INTERVAL '1 hour'
GROUP BY component, metric_name, unit;

-- Create unique index on performance summary
CREATE UNIQUE INDEX IF NOT EXISTS idx_performance_summary_unique 
ON performance_summary(component, metric_name, unit);

-- Create functions for risk management

-- Function to calculate portfolio exposure
CREATE OR REPLACE FUNCTION calculate_portfolio_exposure(p_account_id VARCHAR(64) DEFAULT NULL)
RETURNS DECIMAL(20, 2) AS $$
DECLARE
    total_exposure DECIMAL(20, 2) := 0;
BEGIN
    SELECT COALESCE(SUM(ABS(quantity * last_price) / 100.0), 0)
    INTO total_exposure
    FROM positions 
    WHERE (p_account_id IS NULL OR account_id = p_account_id)
      AND quantity != 0;
    
    RETURN total_exposure;
END;
$$ LANGUAGE plpgsql;

-- Function to calculate daily P&L
CREATE OR REPLACE FUNCTION calculate_daily_pnl(p_date DATE, p_account_id VARCHAR(64) DEFAULT NULL)
RETURNS DECIMAL(20, 2) AS $$
DECLARE
    total_pnl DECIMAL(20, 2) := 0;
BEGIN
    SELECT COALESCE(SUM(
        (CASE WHEN f.side = 'buy' THEN -1 ELSE 1 END) * f.quantity * f.price / 100.0
    ), 0)
    INTO total_pnl
    FROM fills f
    WHERE DATE(f.execution_time) = p_date
      AND (p_account_id IS NULL OR EXISTS (
          SELECT 1 FROM orders o 
          WHERE o.id = f.order_id 
            AND o.account_id = p_account_id
      ));
    
    RETURN total_pnl;
END;
$$ LANGUAGE plpgsql;

-- Function to update daily statistics
CREATE OR REPLACE FUNCTION update_daily_stats(p_date DATE, p_account_id VARCHAR(64) DEFAULT NULL)
RETURNS VOID AS $$
DECLARE
    v_total_trades INTEGER;
    v_total_volume BIGINT;
    v_gross_pnl BIGINT;
    v_winning_trades INTEGER;
    v_losing_trades INTEGER;
    v_largest_win BIGINT;
    v_largest_loss BIGINT;
    v_avg_trade_size BIGINT;
    v_win_rate DECIMAL(5, 4);
    v_profit_factor DECIMAL(10, 4);
    v_gross_wins DECIMAL(20, 2);
    v_gross_losses DECIMAL(20, 2);
BEGIN
    -- Calculate basic stats
    SELECT 
        COUNT(*),
        SUM(quantity),
        SUM((CASE WHEN side = 'buy' THEN -1 ELSE 1 END) * quantity * price),
        AVG(quantity * price)
    INTO v_total_trades, v_total_volume, v_gross_pnl, v_avg_trade_size
    FROM fills f
    JOIN orders o ON f.order_id = o.id
    WHERE DATE(f.execution_time) = p_date
      AND (p_account_id IS NULL OR o.account_id = p_account_id);
    
    -- Calculate win/loss statistics
    SELECT 
        COUNT(*) FILTER (WHERE pnl > 0),
        COUNT(*) FILTER (WHERE pnl < 0),
        MAX(pnl),
        MIN(pnl),
        SUM(pnl) FILTER (WHERE pnl > 0),
        ABS(SUM(pnl) FILTER (WHERE pnl < 0))
    INTO v_winning_trades, v_losing_trades, v_largest_win, v_largest_loss, v_gross_wins, v_gross_losses
    FROM (
        SELECT (CASE WHEN f.side = 'buy' THEN -1 ELSE 1 END) * f.quantity * f.price as pnl
        FROM fills f
        JOIN orders o ON f.order_id = o.id
        WHERE DATE(f.execution_time) = p_date
          AND (p_account_id IS NULL OR o.account_id = p_account_id)
    ) trade_pnl;
    
    -- Calculate derived metrics
    v_win_rate := CASE 
        WHEN v_total_trades > 0 THEN v_winning_trades::DECIMAL / v_total_trades 
        ELSE 0 
    END;
    
    v_profit_factor := CASE 
        WHEN v_gross_losses > 0 THEN v_gross_wins / v_gross_losses 
        ELSE NULL 
    END;
    
    -- Insert or update daily stats
    INSERT INTO daily_stats (
        account_id, date, total_trades, total_volume, gross_pnl,
        winning_trades, losing_trades, largest_win, largest_loss,
        avg_trade_size, win_rate, profit_factor
    ) VALUES (
        p_account_id, p_date, COALESCE(v_total_trades, 0), COALESCE(v_total_volume, 0), COALESCE(v_gross_pnl, 0),
        COALESCE(v_winning_trades, 0), COALESCE(v_losing_trades, 0), COALESCE(v_largest_win, 0), COALESCE(v_largest_loss, 0),
        COALESCE(v_avg_trade_size, 0), v_win_rate, v_profit_factor
    )
    ON CONFLICT (account_id, date) 
    DO UPDATE SET
        total_trades = EXCLUDED.total_trades,
        total_volume = EXCLUDED.total_volume,
        gross_pnl = EXCLUDED.gross_pnl,
        winning_trades = EXCLUDED.winning_trades,
        losing_trades = EXCLUDED.losing_trades,
        largest_win = EXCLUDED.largest_win,
        largest_loss = EXCLUDED.largest_loss,
        avg_trade_size = EXCLUDED.avg_trade_size,
        win_rate = EXCLUDED.win_rate,
        profit_factor = EXCLUDED.profit_factor,
        updated_at = NOW();
END;
$$ LANGUAGE plpgsql;

-- Add comments for documentation
COMMENT ON TABLE risk_metrics IS 'Comprehensive risk metrics tracking with real-time monitoring';
COMMENT ON TABLE risk_violations IS 'Audit trail of risk limit breaches for compliance';
COMMENT ON TABLE daily_stats IS 'Daily trading statistics and performance metrics';
COMMENT ON TABLE performance_metrics IS 'System performance metrics for latency and throughput monitoring';
COMMENT ON TABLE audit_logs IS 'Complete audit trail for regulatory compliance';
COMMENT ON TABLE strategy_performance IS 'Individual strategy performance tracking';
COMMENT ON TABLE symbol_stats IS 'Per-symbol market characteristics and performance';

COMMENT ON FUNCTION calculate_portfolio_exposure IS 'Calculate total portfolio exposure in dollars';
COMMENT ON FUNCTION calculate_daily_pnl IS 'Calculate daily P&L for specified date and account';
COMMENT ON FUNCTION update_daily_stats IS 'Update daily statistics from fill data';