-- ============================================================================
-- SCHEMA: admin
-- TABLE: admin_roles
-- DESCRIPTION: Roles administrativos de la plataforma
-- VERSION: 1.0.0
-- CREATED: 2026-01-16
-- SPRINT: Sprint 1 - DDL Implementation Roadmap Q1-2026
-- ============================================================================

-- Crear schema si no existe
CREATE SCHEMA IF NOT EXISTS admin;

-- Grant usage
GRANT USAGE ON SCHEMA admin TO trading_app;
GRANT USAGE ON SCHEMA admin TO trading_readonly;

-- Enum para nivel de acceso administrativo
DO $$ BEGIN
    CREATE TYPE admin.admin_level AS ENUM (
        'super_admin',      -- Acceso total a la plataforma
        'platform_admin',   -- Administracion de plataforma (sin acceso a codigo)
        'tenant_admin',     -- Administrador de tenant
        'support',          -- Soporte al cliente
        'analyst',          -- Solo lectura para analisis
        'auditor'           -- Solo lectura para auditorias
    );
EXCEPTION
    WHEN duplicate_object THEN null;
END $$;

-- Tabla de Roles Administrativos
CREATE TABLE IF NOT EXISTS admin.admin_roles (
    -- Identificadores
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id UUID NOT NULL REFERENCES users.users(id) ON DELETE CASCADE,

    -- Nivel administrativo
    level admin.admin_level NOT NULL,

    -- Scope del rol
    is_global BOOLEAN NOT NULL DEFAULT FALSE,           -- Aplica a todos los tenants
    tenant_id UUID REFERENCES tenants.tenants(id),      -- Tenant especifico (NULL si global)

    -- Permisos especificos (override del nivel)
    permissions JSONB DEFAULT '{}'::JSONB,
    denied_permissions JSONB DEFAULT '[]'::JSONB,       -- Permisos explicitamente denegados

    -- Configuracion
    can_impersonate BOOLEAN NOT NULL DEFAULT FALSE,     -- Puede actuar como otro usuario
    can_view_pii BOOLEAN NOT NULL DEFAULT FALSE,        -- Puede ver datos personales
    can_export_data BOOLEAN NOT NULL DEFAULT FALSE,     -- Puede exportar datos masivos
    can_modify_config BOOLEAN NOT NULL DEFAULT FALSE,   -- Puede modificar configuracion
    can_manage_admins BOOLEAN NOT NULL DEFAULT FALSE,   -- Puede gestionar otros admins

    -- Restricciones de IP
    allowed_ips INET[],                                 -- IPs permitidas (NULL = todas)

    -- Restricciones de horario
    allowed_hours_start TIME,                           -- Hora inicio permitida
    allowed_hours_end TIME,                             -- Hora fin permitida
    allowed_days INTEGER[],                             -- Dias permitidos (0=Dom, 6=Sab)

    -- Activacion
    is_active BOOLEAN NOT NULL DEFAULT TRUE,
    activated_at TIMESTAMPTZ,
    deactivated_at TIMESTAMPTZ,
    deactivation_reason TEXT,

    -- Expiracion
    expires_at TIMESTAMPTZ,                             -- Rol temporal

    -- Auditoria de asignacion
    assigned_by UUID REFERENCES users.users(id),
    assigned_at TIMESTAMPTZ DEFAULT NOW(),
    assignment_reason TEXT,

    -- Ultima actividad administrativa
    last_admin_action_at TIMESTAMPTZ,
    total_admin_actions INTEGER NOT NULL DEFAULT 0,

    -- Metadata
    metadata JSONB DEFAULT '{}'::JSONB,

    -- Timestamps
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),

    -- Constraints
    CONSTRAINT admin_roles_unique_user_tenant UNIQUE (user_id, tenant_id),
    CONSTRAINT admin_roles_tenant_check CHECK (
        (is_global = TRUE AND tenant_id IS NULL) OR
        (is_global = FALSE AND tenant_id IS NOT NULL)
    )
);

COMMENT ON TABLE admin.admin_roles IS
'Roles administrativos asignados a usuarios de la plataforma';

COMMENT ON COLUMN admin.admin_roles.level IS
'Nivel de acceso: super_admin, platform_admin, tenant_admin, support, analyst, auditor';

COMMENT ON COLUMN admin.admin_roles.is_global IS
'TRUE si el rol aplica a todos los tenants (solo para super_admin/platform_admin)';

-- Indices
CREATE INDEX IF NOT EXISTS idx_admin_roles_user_id
    ON admin.admin_roles(user_id);

CREATE INDEX IF NOT EXISTS idx_admin_roles_tenant_id
    ON admin.admin_roles(tenant_id)
    WHERE tenant_id IS NOT NULL;

CREATE INDEX IF NOT EXISTS idx_admin_roles_level
    ON admin.admin_roles(level);

CREATE INDEX IF NOT EXISTS idx_admin_roles_active
    ON admin.admin_roles(is_active)
    WHERE is_active = TRUE;

CREATE INDEX IF NOT EXISTS idx_admin_roles_global
    ON admin.admin_roles(is_global)
    WHERE is_global = TRUE;

CREATE INDEX IF NOT EXISTS idx_admin_roles_expires
    ON admin.admin_roles(expires_at)
    WHERE expires_at IS NOT NULL;

-- Trigger para updated_at
CREATE OR REPLACE FUNCTION admin.update_admin_timestamp()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS admin_role_updated_at ON admin.admin_roles;
CREATE TRIGGER admin_role_updated_at
    BEFORE UPDATE ON admin.admin_roles
    FOR EACH ROW
    EXECUTE FUNCTION admin.update_admin_timestamp();

-- Funcion para verificar acceso admin
CREATE OR REPLACE FUNCTION admin.check_admin_access(
    p_user_id UUID,
    p_tenant_id UUID DEFAULT NULL,
    p_required_level admin.admin_level DEFAULT 'support'
)
RETURNS BOOLEAN AS $$
DECLARE
    v_role RECORD;
    v_level_order INTEGER;
    v_required_order INTEGER;
    v_current_time TIME;
    v_current_day INTEGER;
BEGIN
    -- Orden de niveles (mayor = mas permisos)
    SELECT CASE p_required_level
        WHEN 'super_admin' THEN 6
        WHEN 'platform_admin' THEN 5
        WHEN 'tenant_admin' THEN 4
        WHEN 'support' THEN 3
        WHEN 'analyst' THEN 2
        WHEN 'auditor' THEN 1
    END INTO v_required_order;

    -- Buscar rol activo
    SELECT * INTO v_role
    FROM admin.admin_roles
    WHERE user_id = p_user_id
      AND is_active = TRUE
      AND (expires_at IS NULL OR expires_at > NOW())
      AND (is_global = TRUE OR tenant_id = p_tenant_id)
    ORDER BY is_global DESC  -- Preferir roles globales
    LIMIT 1;

    IF v_role IS NULL THEN
        RETURN FALSE;
    END IF;

    -- Verificar nivel
    SELECT CASE v_role.level
        WHEN 'super_admin' THEN 6
        WHEN 'platform_admin' THEN 5
        WHEN 'tenant_admin' THEN 4
        WHEN 'support' THEN 3
        WHEN 'analyst' THEN 2
        WHEN 'auditor' THEN 1
    END INTO v_level_order;

    IF v_level_order < v_required_order THEN
        RETURN FALSE;
    END IF;

    -- Verificar restricciones de horario si existen
    IF v_role.allowed_hours_start IS NOT NULL AND v_role.allowed_hours_end IS NOT NULL THEN
        v_current_time := CURRENT_TIME;
        IF v_current_time < v_role.allowed_hours_start OR v_current_time > v_role.allowed_hours_end THEN
            RETURN FALSE;
        END IF;
    END IF;

    -- Verificar restricciones de dias si existen
    IF v_role.allowed_days IS NOT NULL AND array_length(v_role.allowed_days, 1) > 0 THEN
        v_current_day := EXTRACT(DOW FROM CURRENT_DATE)::INTEGER;
        IF NOT (v_current_day = ANY(v_role.allowed_days)) THEN
            RETURN FALSE;
        END IF;
    END IF;

    RETURN TRUE;
END;
$$ LANGUAGE plpgsql;

-- Vista de administradores activos
CREATE OR REPLACE VIEW admin.v_active_admins AS
SELECT
    ar.id,
    ar.user_id,
    u.email,
    u.first_name,
    u.last_name,
    ar.level,
    ar.is_global,
    ar.tenant_id,
    t.name AS tenant_name,
    ar.can_impersonate,
    ar.can_view_pii,
    ar.expires_at,
    ar.last_admin_action_at,
    ar.total_admin_actions
FROM admin.admin_roles ar
JOIN users.users u ON ar.user_id = u.id
LEFT JOIN tenants.tenants t ON ar.tenant_id = t.id
WHERE ar.is_active = TRUE
  AND (ar.expires_at IS NULL OR ar.expires_at > NOW())
ORDER BY ar.level, ar.created_at;

-- Grants
GRANT SELECT, INSERT, UPDATE, DELETE ON admin.admin_roles TO trading_app;
GRANT SELECT ON admin.admin_roles TO trading_readonly;
GRANT SELECT ON admin.v_active_admins TO trading_app;
GRANT EXECUTE ON FUNCTION admin.check_admin_access TO trading_app;
