-- Migration 018: RBAC Permissions System
--
-- Implements role-based access control for API Gateway
-- Supports hot-reload via PostgreSQL NOTIFY/LISTEN
--
-- Created: Wave 70 Agent 7
-- Performance: Sub-100ns cached permission checks

-- ============================================================================
-- RBAC Schema
-- ============================================================================

-- Roles table: Define system roles
CREATE TABLE IF NOT EXISTS roles (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(100) UNIQUE NOT NULL,
    description TEXT,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);

-- Permissions table: Define endpoint permissions
CREATE TABLE IF NOT EXISTS permissions (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    endpoint VARCHAR(255) UNIQUE NOT NULL,  -- e.g., "trading.submit_order"
    description TEXT,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMP NOT NULL DEFAULT NOW()
);

-- Role-Permission mapping: Many-to-many relationship
CREATE TABLE IF NOT EXISTS role_permissions (
    role_id UUID REFERENCES roles(id) ON DELETE CASCADE,
    permission_id UUID REFERENCES permissions(id) ON DELETE CASCADE,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    PRIMARY KEY (role_id, permission_id)
);

-- User-Role mapping: Many-to-many relationship
-- Note: Assumes 'users' table exists from previous migrations
CREATE TABLE IF NOT EXISTS user_roles (
    user_id UUID REFERENCES users(id) ON DELETE CASCADE,
    role_id UUID REFERENCES roles(id) ON DELETE CASCADE,
    created_at TIMESTAMP NOT NULL DEFAULT NOW(),
    PRIMARY KEY (user_id, role_id)
);

-- ============================================================================
-- Performance Indexes
-- ============================================================================

-- Critical path: Fast user permission lookups
CREATE INDEX IF NOT EXISTS idx_user_roles_user_id ON user_roles(user_id);
CREATE INDEX IF NOT EXISTS idx_user_roles_role_id ON user_roles(role_id);

-- Fast role permission lookups
CREATE INDEX IF NOT EXISTS idx_role_permissions_role_id ON role_permissions(role_id);
CREATE INDEX IF NOT EXISTS idx_role_permissions_permission_id ON role_permissions(permission_id);

-- Fast permission endpoint lookups
CREATE INDEX IF NOT EXISTS idx_permissions_endpoint ON permissions(endpoint);

-- Fast role name lookups
CREATE INDEX IF NOT EXISTS idx_roles_name ON roles(name);

-- ============================================================================
-- Default Roles
-- ============================================================================

INSERT INTO roles (name, description) VALUES
    ('admin', 'Full system access - all permissions granted')
ON CONFLICT (name) DO NOTHING;

INSERT INTO roles (name, description) VALUES
    ('trader', 'Trading operations - submit/cancel orders, view positions')
ON CONFLICT (name) DO NOTHING;

INSERT INTO roles (name, description) VALUES
    ('analyst', 'Read-only access - view data and reports')
ON CONFLICT (name) DO NOTHING;

INSERT INTO roles (name, description) VALUES
    ('risk_manager', 'Risk management - view/update risk limits, circuit breakers')
ON CONFLICT (name) DO NOTHING;

INSERT INTO roles (name, description) VALUES
    ('developer', 'Development access - backtesting, ML training, configuration')
ON CONFLICT (name) DO NOTHING;

-- ============================================================================
-- Default Permissions
-- ============================================================================

-- Trading Service Permissions
INSERT INTO permissions (endpoint, description) VALUES
    ('trading.submit_order', 'Submit trading orders to market')
ON CONFLICT (endpoint) DO NOTHING;

INSERT INTO permissions (endpoint, description) VALUES
    ('trading.cancel_order', 'Cancel pending orders')
ON CONFLICT (endpoint) DO NOTHING;

INSERT INTO permissions (endpoint, description) VALUES
    ('trading.view_positions', 'View current positions')
ON CONFLICT (endpoint) DO NOTHING;

INSERT INTO permissions (endpoint, description) VALUES
    ('trading.view_orders', 'View order history')
ON CONFLICT (endpoint) DO NOTHING;

-- Configuration Permissions
INSERT INTO permissions (endpoint, description) VALUES
    ('config.update', 'Update system configuration')
ON CONFLICT (endpoint) DO NOTHING;

INSERT INTO permissions (endpoint, description) VALUES
    ('config.view', 'View system configuration')
ON CONFLICT (endpoint) DO NOTHING;

-- Backtesting Permissions
INSERT INTO permissions (endpoint, description) VALUES
    ('backtesting.run', 'Run strategy backtests')
ON CONFLICT (endpoint) DO NOTHING;

INSERT INTO permissions (endpoint, description) VALUES
    ('backtesting.view_results', 'View backtest results')
ON CONFLICT (endpoint) DO NOTHING;

-- ML Training Permissions
INSERT INTO permissions (endpoint, description) VALUES
    ('ml.train_model', 'Train ML models')
ON CONFLICT (endpoint) DO NOTHING;

INSERT INTO permissions (endpoint, description) VALUES
    ('ml.deploy_model', 'Deploy trained models to production')
ON CONFLICT (endpoint) DO NOTHING;

INSERT INTO permissions (endpoint, description) VALUES
    ('ml.view_metrics', 'View ML model metrics')
ON CONFLICT (endpoint) DO NOTHING;

-- Risk Management Permissions
INSERT INTO permissions (endpoint, description) VALUES
    ('risk.update_limits', 'Update risk limits and thresholds')
ON CONFLICT (endpoint) DO NOTHING;

INSERT INTO permissions (endpoint, description) VALUES
    ('risk.view_metrics', 'View risk metrics and VaR')
ON CONFLICT (endpoint) DO NOTHING;

INSERT INTO permissions (endpoint, description) VALUES
    ('risk.circuit_breaker', 'Activate circuit breaker / kill switch')
ON CONFLICT (endpoint) DO NOTHING;

-- ============================================================================
-- Default Role-Permission Mappings
-- ============================================================================

-- Admin: All permissions
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r
CROSS JOIN permissions p
WHERE r.name = 'admin'
ON CONFLICT (role_id, permission_id) DO NOTHING;

-- Trader: Trading operations
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.name = 'trader'
AND p.endpoint IN (
    'trading.submit_order',
    'trading.cancel_order',
    'trading.view_positions',
    'trading.view_orders',
    'config.view',
    'backtesting.view_results'
)
ON CONFLICT (role_id, permission_id) DO NOTHING;

-- Analyst: Read-only access
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.name = 'analyst'
AND p.endpoint IN (
    'trading.view_positions',
    'trading.view_orders',
    'config.view',
    'backtesting.view_results',
    'ml.view_metrics',
    'risk.view_metrics'
)
ON CONFLICT (role_id, permission_id) DO NOTHING;

-- Risk Manager: Risk operations
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.name = 'risk_manager'
AND p.endpoint IN (
    'risk.update_limits',
    'risk.view_metrics',
    'risk.circuit_breaker',
    'trading.view_positions',
    'trading.view_orders',
    'config.view'
)
ON CONFLICT (role_id, permission_id) DO NOTHING;

-- Developer: Development access
INSERT INTO role_permissions (role_id, permission_id)
SELECT r.id, p.id
FROM roles r, permissions p
WHERE r.name = 'developer'
AND p.endpoint IN (
    'backtesting.run',
    'backtesting.view_results',
    'ml.train_model',
    'ml.deploy_model',
    'ml.view_metrics',
    'config.update',
    'config.view'
)
ON CONFLICT (role_id, permission_id) DO NOTHING;

-- ============================================================================
-- Hot-Reload Support: PostgreSQL NOTIFY/LISTEN
-- ============================================================================

-- Function to notify on permission changes
CREATE OR REPLACE FUNCTION notify_permission_change()
RETURNS TRIGGER AS $$
BEGIN
    PERFORM pg_notify('permission_changes', json_build_object(
        'table', TG_TABLE_NAME,
        'operation', TG_OP,
        'timestamp', NOW()
    )::text);
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- Triggers for permission change notifications
DROP TRIGGER IF EXISTS trigger_role_permissions_change ON role_permissions;
CREATE TRIGGER trigger_role_permissions_change
    AFTER INSERT OR UPDATE OR DELETE ON role_permissions
    FOR EACH STATEMENT
    EXECUTE FUNCTION notify_permission_change();

DROP TRIGGER IF EXISTS trigger_user_roles_change ON user_roles;
CREATE TRIGGER trigger_user_roles_change
    AFTER INSERT OR UPDATE OR DELETE ON user_roles
    FOR EACH STATEMENT
    EXECUTE FUNCTION notify_permission_change();

DROP TRIGGER IF EXISTS trigger_permissions_change ON permissions;
CREATE TRIGGER trigger_permissions_change
    AFTER INSERT OR UPDATE OR DELETE ON permissions
    FOR EACH STATEMENT
    EXECUTE FUNCTION notify_permission_change();

DROP TRIGGER IF EXISTS trigger_roles_change ON roles;
CREATE TRIGGER trigger_roles_change
    AFTER INSERT OR UPDATE OR DELETE ON roles
    FOR EACH STATEMENT
    EXECUTE FUNCTION notify_permission_change();

-- ============================================================================
-- Updated_at Triggers
-- ============================================================================

CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS trigger_roles_updated_at ON roles;
CREATE TRIGGER trigger_roles_updated_at
    BEFORE UPDATE ON roles
    FOR EACH ROW
    EXECUTE FUNCTION update_updated_at_column();

DROP TRIGGER IF EXISTS trigger_permissions_updated_at ON permissions;
CREATE TRIGGER trigger_permissions_updated_at
    BEFORE UPDATE ON permissions
    FOR EACH ROW
    EXECUTE FUNCTION update_updated_at_column();

-- ============================================================================
-- Utility Views
-- ============================================================================

-- View: User permissions (flattened for easy querying)
CREATE OR REPLACE VIEW user_permissions_view AS
SELECT
    u.id AS user_id,
    u.username,
    r.name AS role_name,
    p.endpoint AS permission,
    p.description AS permission_description
FROM users u
JOIN user_roles ur ON u.id = ur.user_id
JOIN roles r ON ur.role_id = r.id
JOIN role_permissions rp ON r.id = rp.role_id
JOIN permissions p ON rp.permission_id = p.id
ORDER BY u.username, r.name, p.endpoint;

-- View: Role permission counts
CREATE OR REPLACE VIEW role_permission_counts AS
SELECT
    r.name AS role_name,
    r.description AS role_description,
    COUNT(rp.permission_id) AS permission_count
FROM roles r
LEFT JOIN role_permissions rp ON r.id = rp.role_id
GROUP BY r.id, r.name, r.description
ORDER BY permission_count DESC, r.name;

-- ============================================================================
-- Comments for Documentation
-- ============================================================================

COMMENT ON TABLE roles IS 'System roles for RBAC';
COMMENT ON TABLE permissions IS 'Endpoint permissions for API Gateway';
COMMENT ON TABLE role_permissions IS 'Role-permission mappings (many-to-many)';
COMMENT ON TABLE user_roles IS 'User-role assignments (many-to-many)';

COMMENT ON COLUMN permissions.endpoint IS 'Endpoint identifier (e.g., trading.submit_order)';
COMMENT ON FUNCTION notify_permission_change() IS 'Triggers PostgreSQL NOTIFY on permission changes for cache invalidation';

COMMENT ON VIEW user_permissions_view IS 'Flattened view of user permissions for auditing';
COMMENT ON VIEW role_permission_counts IS 'Summary of permissions per role';

-- ============================================================================
-- Migration Complete
-- ============================================================================

-- Verify migration
DO $$
DECLARE
    role_count INTEGER;
    permission_count INTEGER;
    mapping_count INTEGER;
BEGIN
    SELECT COUNT(*) INTO role_count FROM roles;
    SELECT COUNT(*) INTO permission_count FROM permissions;
    SELECT COUNT(*) INTO mapping_count FROM role_permissions;

    RAISE NOTICE 'RBAC Migration Complete:';
    RAISE NOTICE '  - Roles: %', role_count;
    RAISE NOTICE '  - Permissions: %', permission_count;
    RAISE NOTICE '  - Role-Permission Mappings: %', mapping_count;
END $$;
