-- Foxhunt HFT Trading System Database Initialization
-- Creates database schema, users, and initial configuration

-- Create database (if not exists via environment)
-- This runs after the database specified in POSTGRES_DB is created

\c foxhunt;

-- Create extensions
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "pg_stat_statements";
CREATE EXTENSION IF NOT EXISTS "pgcrypto";
CREATE EXTENSION IF NOT EXISTS "hstore";
CREATE EXTENSION IF NOT EXISTS "ltree";

-- Create application roles
DO $$
BEGIN
    -- Trading service role
    IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'foxhunt_trading') THEN
        CREATE ROLE foxhunt_trading LOGIN PASSWORD 'trading_secure_password_123!';
    END IF;

    -- ML service role
    IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'foxhunt_ml') THEN
        CREATE ROLE foxhunt_ml LOGIN PASSWORD 'ml_secure_password_456!';
    END IF;

    -- Backtesting service role
    IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'foxhunt_backtesting') THEN
        CREATE ROLE foxhunt_backtesting LOGIN PASSWORD 'backtesting_secure_password_789!';
    END IF;

    -- Read-only role for reporting
    IF NOT EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'foxhunt_readonly') THEN
        CREATE ROLE foxhunt_readonly LOGIN PASSWORD 'readonly_secure_password_101!';
    END IF;
END $$;

-- Create schemas
CREATE SCHEMA IF NOT EXISTS trading;
CREATE SCHEMA IF NOT EXISTS ml;
CREATE SCHEMA IF NOT EXISTS backtesting;
CREATE SCHEMA IF NOT EXISTS config;
CREATE SCHEMA IF NOT EXISTS audit;
CREATE SCHEMA IF NOT EXISTS monitoring;

-- Grant schema permissions
GRANT USAGE ON SCHEMA trading TO foxhunt_trading;
GRANT ALL PRIVILEGES ON SCHEMA trading TO foxhunt_trading;

GRANT USAGE ON SCHEMA ml TO foxhunt_ml;
GRANT ALL PRIVILEGES ON SCHEMA ml TO foxhunt_ml;

GRANT USAGE ON SCHEMA backtesting TO foxhunt_backtesting;
GRANT ALL PRIVILEGES ON SCHEMA backtesting TO foxhunt_backtesting;

GRANT USAGE ON SCHEMA config TO foxhunt_trading, foxhunt_ml, foxhunt_backtesting;
GRANT SELECT ON ALL TABLES IN SCHEMA config TO foxhunt_trading, foxhunt_ml, foxhunt_backtesting;

-- Read-only access for monitoring
GRANT USAGE ON ALL SCHEMAS TO foxhunt_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA trading TO foxhunt_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA ml TO foxhunt_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA backtesting TO foxhunt_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA config TO foxhunt_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA audit TO foxhunt_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA monitoring TO foxhunt_readonly;

-- Create configuration tables
CREATE TABLE IF NOT EXISTS config.settings (
    id SERIAL PRIMARY KEY,
    namespace VARCHAR(100) NOT NULL,
    key VARCHAR(100) NOT NULL,
    value TEXT NOT NULL,
    value_type VARCHAR(20) NOT NULL DEFAULT 'string',
    description TEXT,
    is_encrypted BOOLEAN DEFAULT FALSE,
    is_sensitive BOOLEAN DEFAULT FALSE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    UNIQUE(namespace, key)
);

-- Create audit trail table
CREATE TABLE IF NOT EXISTS audit.events (
    id BIGSERIAL PRIMARY KEY,
    event_id UUID DEFAULT uuid_generate_v4() UNIQUE,
    service_name VARCHAR(50) NOT NULL,
    event_type VARCHAR(50) NOT NULL,
    entity_type VARCHAR(50),
    entity_id VARCHAR(100),
    user_id VARCHAR(100),
    session_id VARCHAR(100),
    event_data JSONB,
    ip_address INET,
    user_agent TEXT,
    timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    severity VARCHAR(20) DEFAULT 'INFO'
);

-- Create indexes for performance
CREATE INDEX IF NOT EXISTS idx_config_settings_namespace_key ON config.settings(namespace, key);
CREATE INDEX IF NOT EXISTS idx_config_settings_updated_at ON config.settings(updated_at);

CREATE INDEX IF NOT EXISTS idx_audit_events_timestamp ON audit.events(timestamp);
CREATE INDEX IF NOT EXISTS idx_audit_events_service ON audit.events(service_name);
CREATE INDEX IF NOT EXISTS idx_audit_events_type ON audit.events(event_type);
CREATE INDEX IF NOT EXISTS idx_audit_events_user ON audit.events(user_id);
CREATE INDEX IF NOT EXISTS idx_audit_events_session ON audit.events(session_id);

-- Create NOTIFY trigger function for configuration changes
CREATE OR REPLACE FUNCTION config.notify_config_change()
RETURNS TRIGGER AS $$
BEGIN
    -- Notify all listening services of configuration changes
    IF TG_OP = 'INSERT' OR TG_OP = 'UPDATE' THEN
        PERFORM pg_notify('config_change',
            json_build_object(
                'operation', TG_OP,
                'namespace', NEW.namespace,
                'key', NEW.key,
                'timestamp', extract(epoch from NOW())
            )::text
        );
        RETURN NEW;
    ELSIF TG_OP = 'DELETE' THEN
        PERFORM pg_notify('config_change',
            json_build_object(
                'operation', TG_OP,
                'namespace', OLD.namespace,
                'key', OLD.key,
                'timestamp', extract(epoch from NOW())
            )::text
        );
        RETURN OLD;
    END IF;
    RETURN NULL;
END;
$$ LANGUAGE plpgsql;

-- Create trigger for configuration notifications
DROP TRIGGER IF EXISTS trigger_config_notify ON config.settings;
CREATE TRIGGER trigger_config_notify
    AFTER INSERT OR UPDATE OR DELETE ON config.settings
    FOR EACH ROW EXECUTE FUNCTION config.notify_config_change();

-- Insert initial configuration
INSERT INTO config.settings (namespace, key, value, value_type, description, is_sensitive) VALUES
    ('trading', 'max_position_size', '1000000', 'integer', 'Maximum position size in base currency', FALSE),
    ('trading', 'max_daily_loss', '50000', 'integer', 'Maximum daily loss threshold', FALSE),
    ('trading', 'circuit_breaker_enabled', 'true', 'boolean', 'Enable circuit breaker functionality', FALSE),
    ('trading', 'paper_trading_mode', 'false', 'boolean', 'Enable paper trading mode', FALSE),
    ('ml', 'model_training_enabled', 'true', 'boolean', 'Enable ML model training', FALSE),
    ('ml', 'inference_timeout_ms', '100', 'integer', 'ML inference timeout in milliseconds', FALSE),
    ('ml', 'model_update_frequency', '3600', 'integer', 'Model update frequency in seconds', FALSE),
    ('backtesting', 'max_concurrent_tests', '4', 'integer', 'Maximum concurrent backtests', FALSE),
    ('backtesting', 'default_commission', '0.001', 'float', 'Default commission rate', FALSE),
    ('compliance', 'best_execution_monitoring', 'true', 'boolean', 'Enable best execution monitoring', FALSE),
    ('compliance', 'transaction_reporting', 'true', 'boolean', 'Enable transaction reporting', FALSE),
    ('compliance', 'audit_trail_retention_days', '2555', 'integer', 'Audit trail retention period (7 years)', FALSE),
    ('monitoring', 'metrics_collection_interval', '10', 'integer', 'Metrics collection interval in seconds', FALSE),
    ('monitoring', 'health_check_interval', '30', 'integer', 'Health check interval in seconds', FALSE),
    ('security', 'jwt_expiry_hours', '24', 'integer', 'JWT token expiry in hours', FALSE),
    ('security', 'max_failed_logins', '5', 'integer', 'Maximum failed login attempts', FALSE)
ON CONFLICT (namespace, key) DO NOTHING;

-- Create function to update timestamps
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Create trigger to automatically update timestamps
DROP TRIGGER IF EXISTS trigger_update_config_timestamp ON config.settings;
CREATE TRIGGER trigger_update_config_timestamp
    BEFORE UPDATE ON config.settings
    FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();

-- Create monitoring tables
CREATE TABLE IF NOT EXISTS monitoring.service_health (
    id BIGSERIAL PRIMARY KEY,
    service_name VARCHAR(50) NOT NULL,
    status VARCHAR(20) NOT NULL,
    message TEXT,
    response_time_ms INTEGER,
    memory_usage_bytes BIGINT,
    cpu_usage_percent DECIMAL(5,2),
    timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_monitoring_service_health_service ON monitoring.service_health(service_name);
CREATE INDEX IF NOT EXISTS idx_monitoring_service_health_timestamp ON monitoring.service_health(timestamp);
CREATE INDEX IF NOT EXISTS idx_monitoring_service_health_status ON monitoring.service_health(status);

-- Vacuum and analyze for optimal performance
VACUUM ANALYZE;

-- Display initialization summary
SELECT 'Foxhunt HFT Database Initialized Successfully' AS status,
       (SELECT COUNT(*) FROM config.settings) AS config_entries,
       NOW() AS initialized_at;