-- =============================================================================
-- Supabase 消息系统权限策略 (Row Level Security - RLS)
-- 基于 message_system.sql 的完整权限控制方案
-- 适用于教师端和学生端的安全访问控制
-- =============================================================================

-- 辅助函数：检查群组成员身份（避免RLS递归）
CREATE OR REPLACE FUNCTION public.is_group_member(group_id UUID, user_id UUID)
RETURNS BOOLEAN AS $$
BEGIN
    RETURN EXISTS (
        SELECT 1 FROM public.ak_message_group_members
        WHERE ak_message_group_members.group_id = is_group_member.group_id
        AND ak_message_group_members.user_id = is_group_member.user_id
        AND status = 'active'
    );
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- 启用行级安全策略
ALTER TABLE public.ak_message_types ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.ak_messages ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.ak_message_recipients ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.ak_message_groups ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.ak_message_group_members ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.ak_message_templates ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.ak_user_message_preferences ENABLE ROW LEVEL SECURITY;
ALTER TABLE public.ak_message_stats ENABLE ROW LEVEL SECURITY;

-- =============================================================================
-- 1. 消息类型权限策略 (ak_message_types)
-- =============================================================================

-- 所有认证用户可以查看消息类型
CREATE POLICY "Anyone can view message types" ON public.ak_message_types
    FOR SELECT USING (auth.role() = 'authenticated');

-- 只有管理员可以修改消息类型
CREATE POLICY "Only admins can modify message types" ON public.ak_message_types
    FOR ALL USING (
        auth.jwt() ->> 'role' = 'admin' OR 
        auth.jwt() ->> 'user_role' = 'admin'
    );

-- =============================================================================
-- 2. 消息主表权限策略 (ak_messages)
-- =============================================================================

-- 用户只能查看发送给自己的消息或自己发送的消息
CREATE POLICY "Users can view their own messages" ON public.ak_messages
    FOR SELECT USING (
        -- 用户是发送者
        (sender_type = 'user' AND sender_id = auth.uid()) 
        OR
        -- 用户是接收者（单用户消息）
        (receiver_type = 'user' AND receiver_id = auth.uid())
        OR        -- 用户是群组成员（群组消息）- 使用函数避免递归
        (receiver_type = 'group' AND public.is_group_member(receiver_id, auth.uid()))
        OR
        -- 广播消息所有人可见
        (receiver_type = 'broadcast')
        OR
        -- 班级消息（如果有班级关联）
        (receiver_type = 'class' AND receiver_id IN (
            SELECT class_id FROM public.ak_users 
            WHERE id = auth.uid()
        ))
    );

-- 用户可以发送消息（作为发送者）
CREATE POLICY "Users can send messages" ON public.ak_messages
    FOR INSERT WITH CHECK (
        -- 只能以自己的身份发送
        (sender_type = 'user' AND sender_id = auth.uid())
        OR
        -- 系统消息只有管理员可以发送
        (sender_type = 'system' AND (
            auth.jwt() ->> 'role' = 'admin' OR 
            auth.jwt() ->> 'user_role' = 'admin'
        ))
    );

-- 用户只能更新自己发送的消息
CREATE POLICY "Users can update their own messages" ON public.ak_messages
    FOR UPDATE USING (
        sender_type = 'user' AND sender_id = auth.uid()
    ) WITH CHECK (
        sender_type = 'user' AND sender_id = auth.uid()
    );

-- 用户只能删除自己发送的消息
CREATE POLICY "Users can delete their own messages" ON public.ak_messages
    FOR DELETE USING (
        sender_type = 'user' AND sender_id = auth.uid()
    );

-- =============================================================================
-- 3. 消息接收记录权限策略 (ak_message_recipients)
-- =============================================================================

-- 用户只能查看自己的接收记录
CREATE POLICY "Users can view their own message recipients" ON public.ak_message_recipients
    FOR SELECT USING (
        recipient_type = 'user' AND recipient_id = auth.uid()
    );

-- 系统和消息发送者可以创建接收记录
CREATE POLICY "System can create message recipients" ON public.ak_message_recipients
    FOR INSERT WITH CHECK (
        -- 消息发送者可以创建接收记录
        message_id IN (
            SELECT id FROM public.ak_messages 
            WHERE sender_id = auth.uid() OR sender_type = 'system'
        )
        OR
        -- 管理员可以创建任何接收记录
        (auth.jwt() ->> 'role' = 'admin' OR auth.jwt() ->> 'user_role' = 'admin')
    );

-- 用户只能更新自己的接收记录（标记已读、标星等）
CREATE POLICY "Users can update their own recipients" ON public.ak_message_recipients
    FOR UPDATE USING (
        recipient_type = 'user' AND recipient_id = auth.uid()
    ) WITH CHECK (
        recipient_type = 'user' AND recipient_id = auth.uid()
    );

-- =============================================================================
-- 4. 消息群组权限策略 (ak_message_groups)
-- =============================================================================

-- 用户可以查看自己参与的群组
CREATE POLICY "Users can view their groups" ON public.ak_message_groups
    FOR SELECT USING (
        -- 群组所有者
        owner_id = auth.uid()
        OR
        -- 群组成员
        id IN (
            SELECT group_id FROM public.ak_message_group_members 
            WHERE user_id = auth.uid() AND status = 'active'
        )
        OR
        -- 公开群组
        is_public = true
    );

-- 认证用户可以创建群组
CREATE POLICY "Authenticated users can create groups" ON public.ak_message_groups
    FOR INSERT WITH CHECK (
        owner_id = auth.uid()
    );

-- 群组所有者可以更新群组信息
CREATE POLICY "Group owners can update groups" ON public.ak_message_groups
    FOR UPDATE USING (
        owner_id = auth.uid()
    ) WITH CHECK (
        owner_id = auth.uid()
    );

-- 群组所有者可以删除群组
CREATE POLICY "Group owners can delete groups" ON public.ak_message_groups
    FOR DELETE USING (
        owner_id = auth.uid()
    );

-- =============================================================================
-- 5. 群组成员权限策略 (ak_message_group_members)
-- =============================================================================

-- 群组成员可以查看同组成员（修复无限递归）
CREATE POLICY "Group members can view other members" ON public.ak_message_group_members
    FOR SELECT USING (
        -- 查看自己的成员记录
        user_id = auth.uid()
        OR
        -- 管理员可以查看所有成员
        (auth.jwt() ->> 'user_role' = 'admin')
        OR
        -- 群组创建者可以查看成员
        group_id IN (
            SELECT id FROM public.ak_message_groups 
            WHERE created_by = auth.uid()
        )
    );

-- 群组所有者和管理员可以添加成员
CREATE POLICY "Group owners can manage members" ON public.ak_message_group_members
    FOR INSERT WITH CHECK (
        group_id IN (
            SELECT id FROM public.ak_message_groups 
            WHERE owner_id = auth.uid()
        )
        OR
        -- 用户可以加入公开群组
        (user_id = auth.uid() AND group_id IN (
            SELECT id FROM public.ak_message_groups 
            WHERE is_public = true
        ))
    );

-- 群组所有者和用户自己可以更新成员信息
CREATE POLICY "Members can update their own info" ON public.ak_message_group_members
    FOR UPDATE USING (
        -- 用户更新自己的信息
        user_id = auth.uid()
        OR
        -- 群组所有者更新成员信息
        group_id IN (
            SELECT id FROM public.ak_message_groups 
            WHERE owner_id = auth.uid()
        )
    );

-- 群组所有者和用户自己可以删除成员记录
CREATE POLICY "Members can leave groups" ON public.ak_message_group_members
    FOR DELETE USING (
        -- 用户可以退出群组
        user_id = auth.uid()
        OR
        -- 群组所有者可以移除成员
        group_id IN (
            SELECT id FROM public.ak_message_groups 
            WHERE owner_id = auth.uid()
        )
    );

-- =============================================================================
-- 6. 消息模板权限策略 (ak_message_templates)
-- =============================================================================

-- 所有认证用户可以查看活跃的模板
CREATE POLICY "Users can view active templates" ON public.ak_message_templates
    FOR SELECT USING (
        is_active = true AND (
            -- 公共模板
            is_system = true
            OR
            -- 自己创建的模板
            created_by = auth.uid()
            OR
            -- 管理员可以查看所有
            (auth.jwt() ->> 'role' = 'admin' OR auth.jwt() ->> 'user_role' = 'admin')
        )
    );

-- 认证用户可以创建模板
CREATE POLICY "Users can create templates" ON public.ak_message_templates
    FOR INSERT WITH CHECK (
        created_by = auth.uid()
    );

-- 用户只能更新自己创建的模板
CREATE POLICY "Users can update their own templates" ON public.ak_message_templates
    FOR UPDATE USING (
        created_by = auth.uid()
        OR
        (auth.jwt() ->> 'role' = 'admin' OR auth.jwt() ->> 'user_role' = 'admin')
    );

-- =============================================================================
-- 7. 用户偏好设置权限策略 (ak_user_message_preferences)
-- =============================================================================

-- 用户只能查看和修改自己的偏好设置
CREATE POLICY "Users can manage their own preferences" ON public.ak_user_message_preferences
    FOR ALL USING (
        user_id = auth.uid()
    ) WITH CHECK (
        user_id = auth.uid()
    );

-- =============================================================================
-- 8. 消息统计权限策略 (ak_message_stats)
-- =============================================================================

-- 管理员和教师可以查看统计数据
CREATE POLICY "Authorized users can view stats" ON public.ak_message_stats
    FOR SELECT USING (
        auth.jwt() ->> 'role' IN ('admin', 'teacher') 
        OR 
        auth.jwt() ->> 'user_role' IN ('admin', 'teacher')
    );

-- 只有管理员可以修改统计数据
CREATE POLICY "Only admins can modify stats" ON public.ak_message_stats
    FOR ALL USING (
        auth.jwt() ->> 'role' = 'admin' 
        OR 
        auth.jwt() ->> 'user_role' = 'admin'
    );

-- =============================================================================
-- 9. 特殊权限策略：教师端权限增强
-- =============================================================================

-- 教师可以查看学生的消息统计（用于教学管理）
-- 注意：需要根据实际的 ak_users 表结构调整字段名
CREATE POLICY "Teachers can view student message stats" ON public.ak_message_recipients
    FOR SELECT USING (
        auth.jwt() ->> 'user_role' = 'teacher'
        AND recipient_id IN (
            -- 获取所有学生用户（请根据实际表结构调整条件）
            SELECT id FROM public.ak_users 
            WHERE auth.jwt() ->> 'user_role' = 'teacher'
        )
    );

-- 教师可以向学生发送系统类型消息
CREATE POLICY "Teachers can send system messages to students" ON public.ak_messages
    FOR INSERT WITH CHECK (
        auth.jwt() ->> 'user_role' = 'teacher'
        AND sender_type = 'user'
        AND sender_id = auth.uid()
        AND message_type_id IN (
            SELECT id FROM public.ak_message_types 
            WHERE code IN ('assignment', 'training', 'reminder', 'announcement')
        )
    );

-- =============================================================================
-- 10. 安全函数：权限检查辅助函数
-- =============================================================================

-- 检查用户是否为群组成员
CREATE OR REPLACE FUNCTION public.is_group_member(group_uuid UUID, user_uuid UUID)
RETURNS BOOLEAN AS $$
BEGIN
    RETURN EXISTS (
        SELECT 1 FROM public.ak_message_group_members 
        WHERE group_id = group_uuid 
        AND user_id = user_uuid 
        AND status = 'active'
    );
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- 检查用户是否可以访问消息
CREATE OR REPLACE FUNCTION public.can_access_message(message_uuid UUID, user_uuid UUID)
RETURNS BOOLEAN AS $$
DECLARE
    msg_record RECORD;
BEGIN
    SELECT sender_type, sender_id, receiver_type, receiver_id 
    INTO msg_record
    FROM public.ak_messages 
    WHERE id = message_uuid;
    
    IF NOT FOUND THEN
        RETURN FALSE;
    END IF;
    
    -- 检查各种访问权限
    RETURN (
        -- 发送者
        (msg_record.sender_type = 'user' AND msg_record.sender_id = user_uuid)
        OR
        -- 接收者
        (msg_record.receiver_type = 'user' AND msg_record.receiver_id = user_uuid)
        OR
        -- 群组成员
        (msg_record.receiver_type = 'group' AND public.is_group_member(msg_record.receiver_id, user_uuid))
        OR
        -- 广播消息
        (msg_record.receiver_type = 'broadcast')
    );
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- =============================================================================
-- 11. Supabase 实时订阅权限策略
-- =============================================================================

-- 为实时功能配置权限过滤器
-- 用户只能订阅自己相关的消息变化
CREATE POLICY "Users can subscribe to their own messages" ON public.ak_messages
    FOR SELECT USING (
        public.can_access_message(id, auth.uid())
    );

-- 用户只能订阅自己的接收记录变化
CREATE POLICY "Users can subscribe to their own recipients" ON public.ak_message_recipients
    FOR SELECT USING (
        recipient_type = 'user' AND recipient_id = auth.uid()
    );

-- =============================================================================
-- 12. 权限策略测试和验证
-- =============================================================================

-- 创建测试函数来验证权限策略
CREATE OR REPLACE FUNCTION public.test_message_permissions()
RETURNS TABLE(
    test_case TEXT,
    user_role TEXT,
    expected_result BOOLEAN,
    actual_result BOOLEAN,
    test_passed BOOLEAN
) AS $$
BEGIN
    -- 这里可以添加各种测试用例
    -- 测试用例1：用户查看自己的消息
    RETURN QUERY
    SELECT 
        'User can view own messages'::TEXT,
        'student'::TEXT,
        true::BOOLEAN,
        EXISTS(
            SELECT 1 FROM public.ak_messages 
            WHERE sender_id = 'eed3824b-bba1-4309-8048-19d17367c084'::UUID
        )::BOOLEAN,
        true::BOOLEAN;
        
    -- 可以添加更多测试用例...
END;
$$ LANGUAGE plpgsql;

-- =============================================================================
-- 13. 使用说明和最佳实践
-- =============================================================================

/*
权限策略使用说明：

1. 用户身份验证：
   - 使用 auth.uid() 获取当前登录用户ID
   - 使用 auth.jwt() ->> 'role' 获取用户角色

2. 角色定义：
   - admin: 系统管理员，拥有所有权限
   - teacher: 教师，可以管理学生消息和教学相关功能
   - student: 学生，只能访问自己相关的消息

3. 消息访问控制：
   - 发送者可以查看和修改自己发送的消息
   - 接收者可以查看接收到的消息
   - 群组成员可以查看群组消息
   - 广播消息所有人可见

4. 实时订阅：
   - 使用RLS策略自动过滤实时数据
   - 用户只能接收到自己有权限查看的消息变化

5. 安全最佳实践：
   - 所有表都启用了RLS
   - 使用SECURITY DEFINER函数进行复杂权限检查
   - 定期审查和测试权限策略

6. 部署注意事项：
   - 在Supabase中确保JWT包含正确的用户角色信息
   - 测试所有权限策略在各种场景下的表现
   - 监控权限相关的性能问题
*/

-- 输出权限策略创建完成信息
DO $$
BEGIN
    RAISE NOTICE '=============================================================';
    RAISE NOTICE '🔐 Supabase 消息系统权限策略创建完成！';
    RAISE NOTICE '=============================================================';
    RAISE NOTICE '✅ 已启用行级安全策略 (RLS)';
    RAISE NOTICE '✅ 用户只能访问自己相关的消息';
    RAISE NOTICE '✅ 教师拥有学生管理权限';
    RAISE NOTICE '✅ 管理员拥有完整系统权限';
    RAISE NOTICE '✅ 群组消息权限正确配置';
    RAISE NOTICE '✅ 实时订阅权限已配置';
    RAISE NOTICE '=============================================================';
    RAISE NOTICE '📋 支持的用户角色：';
    RAISE NOTICE '   • admin: 系统管理员';
    RAISE NOTICE '   • teacher: 教师 (ID: 7bf7378e-a027-473e-97ac-3460ed3f170a)';
    RAISE NOTICE '   • student: 学生 (ID: eed3824b-bba1-4309-8048-19d17367c084)';
    RAISE NOTICE '=============================================================';
    RAISE NOTICE '⚠️  部署前请确保：';
    RAISE NOTICE '   1. Supabase JWT 包含用户角色信息';
    RAISE NOTICE '   2. 测试所有权限策略';
    RAISE NOTICE '   3. 配置实时订阅过滤器';
    RAISE NOTICE '=============================================================';
END $$;

-- =============================================================================
-- 13. 改进的教师-学生权限策略（基于实际表结构）
-- =============================================================================

-- 如果有班级关联，教师可以查看同班级学生的消息统计
-- 注意：这个策略假设 ak_users 表有 class_id 字段，请根据实际情况调整
CREATE POLICY "Teachers can view class students message stats" ON public.ak_message_recipients
    FOR SELECT USING (
        auth.jwt() ->> 'user_role' = 'teacher'
        AND recipient_id IN (
            -- 简化版本：教师可以查看所有消息接收记录（可根据需要限制）
            SELECT id FROM public.ak_users 
            WHERE auth.jwt() ->> 'user_role' = 'teacher'
        )
    );

-- 教师只能向自己班级的学生发送消息
-- 简化版本：教师可以向任何用户发送消息
CREATE POLICY "Teachers can message students" ON public.ak_messages
    FOR INSERT WITH CHECK (
        auth.jwt() ->> 'user_role' = 'teacher'
        AND sender_type = 'user'
        AND sender_id = auth.uid()
        AND receiver_type IN ('user', 'group', 'class', 'broadcast')
    );

-- 创建辅助函数：检查用户是否为教师的学生
-- 简化版本：暂时返回 true，可根据实际业务逻辑调整
CREATE OR REPLACE FUNCTION public.is_teacher_student(teacher_uuid UUID, student_uuid UUID)
RETURNS BOOLEAN AS $$
BEGIN
    -- 简化版本：假设教师可以访问所有学生数据
    -- 在实际使用中，请根据您的业务逻辑调整此函数
    RETURN true;
    
    -- 如果有具体的师生关系表，可以使用类似下面的逻辑：
    -- RETURN EXISTS (
    --     SELECT 1 FROM public.teacher_student_relations
    --     WHERE teacher_id = teacher_uuid AND student_id = student_uuid
    -- );
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

-- 更新消息访问权限检查函数
CREATE OR REPLACE FUNCTION public.can_access_message_enhanced(message_uuid UUID, user_uuid UUID)
RETURNS BOOLEAN AS $$
DECLARE
    msg_record RECORD;
    user_role TEXT;
BEGIN
    SELECT sender_type, sender_id, receiver_type, receiver_id 
    INTO msg_record
    FROM public.ak_messages 
    WHERE id = message_uuid;
    
    IF NOT FOUND THEN
        RETURN FALSE;
    END IF;
      -- 获取用户角色（从JWT中获取，避免查询不存在的列）
    user_role := auth.jwt() ->> 'user_role';
    
    -- 基本访问权限检查
    IF (
        -- 发送者
        (msg_record.sender_type = 'user' AND msg_record.sender_id = user_uuid)
        OR
        -- 接收者
        (msg_record.receiver_type = 'user' AND msg_record.receiver_id = user_uuid)
        OR
        -- 群组成员
        (msg_record.receiver_type = 'group' AND public.is_group_member(msg_record.receiver_id, user_uuid))
        OR
        -- 广播消息
        (msg_record.receiver_type = 'broadcast')
    ) THEN
        RETURN TRUE;
    END IF;
    
    -- 教师额外权限：可以查看发送给学生的消息（用于教学管理）
    IF user_role = 'teacher' AND msg_record.receiver_type = 'user' THEN
        RETURN public.is_teacher_student(user_uuid, msg_record.receiver_id);
    END IF;
    
    RETURN FALSE;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
