-- 1. Enable RLS on all tables
DO $$ BEGIN
    ALTER TABLE "Admin" ENABLE ROW LEVEL SECURITY;
    ALTER TABLE "Student" ENABLE ROW LEVEL SECURITY;
    ALTER TABLE "Teacher" ENABLE ROW LEVEL SECURITY;
    ALTER TABLE "Parent" ENABLE ROW LEVEL SECURITY;
    ALTER TABLE "Grade" ENABLE ROW LEVEL SECURITY;
    ALTER TABLE "Class" ENABLE ROW LEVEL SECURITY;
    ALTER TABLE "Subject" ENABLE ROW LEVEL SECURITY;
    ALTER TABLE "Lesson" ENABLE ROW LEVEL SECURITY;
    ALTER TABLE "Exam" ENABLE ROW LEVEL SECURITY;
    ALTER TABLE "Assignment" ENABLE ROW LEVEL SECURITY;
    ALTER TABLE "Result" ENABLE ROW LEVEL SECURITY;
    ALTER TABLE "Attendance" ENABLE ROW LEVEL SECURITY;
    ALTER TABLE "Event" ENABLE ROW LEVEL SECURITY;
    ALTER TABLE "Announcement" ENABLE ROW LEVEL SECURITY;
    ALTER TABLE "_SubjectToTeacher" ENABLE ROW LEVEL SECURITY;
EXCEPTION
    WHEN undefined_table THEN
        NULL;
END $$;

-- 2. Utility helper functions connecting to Clerk JWT
CREATE OR REPLACE FUNCTION requesting_user_id()
RETURNS text
LANGUAGE sql STABLE
AS $$
  SELECT NULLIF(current_setting('request.jwt.claims', true)::json->>'sub', '')::text;
$$;

CREATE OR REPLACE FUNCTION requesting_user_role()
RETURNS text
LANGUAGE sql STABLE
AS $$
  SELECT current_setting('request.jwt.claims', true)::json->>'role';
$$;

CREATE OR REPLACE FUNCTION is_admin() RETURNS boolean LANGUAGE sql STABLE AS $$
  SELECT requesting_user_role() = 'admin';
$$;

-- 3. Bypass-RLS Helper Functions for finding accessible IDs
-- We use SECURITY DEFINER SET search_path = public to execute these queries as the database owner,
-- which prevents infinite recursion errors (stack depth limit exceeded) when querying tables that have RLS enabled.
-- Transaction-local variables like `request.jwt.claims` perfectly persist through SECURITY DEFINER calls.

CREATE OR REPLACE FUNCTION auth_user_classes() RETURNS SETOF integer LANGUAGE plpgsql SECURITY DEFINER SET search_path = public STABLE AS $$
BEGIN
    RETURN QUERY
    SELECT "classId" FROM "Student" WHERE id = requesting_user_id() AND requesting_user_role() = 'student'
    UNION
    SELECT "classId" FROM "Student" WHERE "parentId" = requesting_user_id() AND requesting_user_role() = 'parent'
    UNION
    SELECT "classId" FROM "Lesson" WHERE "teacherId" = requesting_user_id() AND requesting_user_role() = 'teacher'
    UNION
    SELECT id FROM "Class" WHERE "supervisorId" = requesting_user_id() AND requesting_user_role() = 'teacher';
END;
$$;

CREATE OR REPLACE FUNCTION auth_user_students() RETURNS SETOF text LANGUAGE plpgsql SECURITY DEFINER SET search_path = public STABLE AS $$
BEGIN
    RETURN QUERY
    SELECT id FROM "Student" WHERE id = requesting_user_id() AND requesting_user_role() = 'student'
    UNION
    SELECT id FROM "Student" WHERE "parentId" = requesting_user_id() AND requesting_user_role() = 'parent'
    UNION
    SELECT id FROM "Student" WHERE "classId" IN (
        SELECT "classId" FROM "Lesson" WHERE "teacherId" = requesting_user_id()
    ) AND requesting_user_role() = 'teacher';
END;
$$;

CREATE OR REPLACE FUNCTION auth_user_lessons() RETURNS SETOF integer LANGUAGE plpgsql SECURITY DEFINER SET search_path = public STABLE AS $$
BEGIN
    RETURN QUERY
    SELECT id FROM "Lesson" WHERE "teacherId" = requesting_user_id() AND requesting_user_role() = 'teacher'
    UNION
    SELECT id FROM "Lesson" WHERE "classId" IN (
        SELECT "classId" FROM "Student" WHERE id = requesting_user_id() AND requesting_user_role() = 'student'
    )
    UNION
    SELECT id FROM "Lesson" WHERE "classId" IN (
        SELECT "classId" FROM "Student" WHERE "parentId" = requesting_user_id() AND requesting_user_role() = 'parent'
    );
END;
$$;

CREATE OR REPLACE FUNCTION auth_user_subjects() RETURNS SETOF integer LANGUAGE plpgsql SECURITY DEFINER SET search_path = public STABLE AS $$
BEGIN
    RETURN QUERY
    SELECT "A" FROM "_SubjectToTeacher" WHERE "B" = requesting_user_id() AND requesting_user_role() = 'teacher'
    UNION
    SELECT "subjectId" FROM "Lesson" WHERE "classId" IN (
        SELECT "classId" FROM "Student" WHERE id = requesting_user_id() AND requesting_user_role() = 'student'
    )
    UNION
    SELECT "subjectId" FROM "Lesson" WHERE "classId" IN (
        SELECT "classId" FROM "Student" WHERE "parentId" = requesting_user_id() AND requesting_user_role() = 'parent'
    );
END;
$$;

CREATE OR REPLACE FUNCTION auth_user_teachers() RETURNS SETOF text LANGUAGE plpgsql SECURITY DEFINER SET search_path = public STABLE AS $$
BEGIN
    RETURN QUERY
    SELECT id FROM "Teacher" WHERE id = requesting_user_id() AND requesting_user_role() = 'teacher'
    UNION
    SELECT "teacherId" FROM "Lesson" WHERE "classId" IN (
        SELECT "classId" FROM "Student" WHERE id = requesting_user_id() AND requesting_user_role() = 'student'
    )
    UNION
    SELECT "teacherId" FROM "Lesson" WHERE "classId" IN (
        SELECT "classId" FROM "Student" WHERE "parentId" = requesting_user_id() AND requesting_user_role() = 'parent'
    );
END;
$$;

CREATE OR REPLACE FUNCTION auth_user_parents() RETURNS SETOF text LANGUAGE plpgsql SECURITY DEFINER SET search_path = public STABLE AS $$
BEGIN
    RETURN QUERY
    SELECT id FROM "Parent" WHERE id = requesting_user_id() AND requesting_user_role() = 'parent'
    UNION
    SELECT "parentId" FROM "Student" WHERE id = requesting_user_id() AND requesting_user_role() = 'student';
END;
$$;



-- 4. Define Table Policies using Helpers
CREATE POLICY "Admin has full access" ON "Admin" FOR ALL USING (is_admin());
CREATE POLICY "Admins have full access" ON "Student" FOR ALL USING (is_admin());
CREATE POLICY "Admins have full access" ON "Teacher" FOR ALL USING (is_admin());
CREATE POLICY "Admins have full access" ON "Parent" FOR ALL USING (is_admin());
CREATE POLICY "Admins have full access" ON "Grade" FOR ALL USING (is_admin());
CREATE POLICY "Admins have full access" ON "Class" FOR ALL USING (is_admin());
CREATE POLICY "Admins have full access" ON "Subject" FOR ALL USING (is_admin());
CREATE POLICY "Admins have full access" ON "Lesson" FOR ALL USING (is_admin());
CREATE POLICY "Admins have full access" ON "Exam" FOR ALL USING (is_admin());
CREATE POLICY "Admins have full access" ON "Assignment" FOR ALL USING (is_admin());
CREATE POLICY "Admins have full access" ON "Result" FOR ALL USING (is_admin());
CREATE POLICY "Admins have full access" ON "Attendance" FOR ALL USING (is_admin());
CREATE POLICY "Admins have full access" ON "Event" FOR ALL USING (is_admin());
CREATE POLICY "Admins have full access" ON "Announcement" FOR ALL USING (is_admin());
CREATE POLICY "Admins have full access" ON "_SubjectToTeacher" FOR ALL USING (is_admin());


CREATE POLICY "Anyone can view grades" ON "Grade" FOR SELECT TO authenticated USING (true);
CREATE POLICY "Anyone can view subject-teacher" ON "_SubjectToTeacher" FOR SELECT TO authenticated USING (true);

CREATE POLICY "Users can view permitted students" ON "Student" FOR SELECT USING (
    is_admin() OR id IN (SELECT auth_user_students())
);

CREATE POLICY "Users can view permitted teachers" ON "Teacher" FOR SELECT USING (
    is_admin() OR id IN (SELECT auth_user_teachers())
);

CREATE POLICY "Users can view permitted parents" ON "Parent" FOR SELECT USING (
    is_admin() OR id IN (SELECT auth_user_parents())
);

CREATE POLICY "Users can view permitted subjects" ON "Subject" FOR SELECT USING (
    is_admin() OR id IN (SELECT auth_user_subjects())
);

CREATE POLICY "Users can view permitted classes" ON "Class" FOR SELECT USING (
    is_admin() OR id IN (SELECT auth_user_classes())
);

CREATE POLICY "Users can view permitted lessons" ON "Lesson" FOR SELECT USING (
    is_admin() OR id IN (SELECT auth_user_lessons())
);

CREATE POLICY "Users can view permitted exams" ON "Exam" FOR SELECT USING (
    is_admin() OR "lessonId" IN (SELECT auth_user_lessons())
);

CREATE POLICY "Users can view permitted assignments" ON "Assignment" FOR SELECT USING (
    is_admin() OR "lessonId" IN (SELECT auth_user_lessons())
);

CREATE POLICY "Users can view permitted results" ON "Result" FOR SELECT USING (
    is_admin() OR "studentId" IN (SELECT auth_user_students()) OR (
        requesting_user_role() = 'teacher' AND (
            "examId" IN (SELECT id FROM "Exam" WHERE "lessonId" IN (SELECT auth_user_lessons())) OR
            "assignmentId" IN (SELECT id FROM "Assignment" WHERE "lessonId" IN (SELECT auth_user_lessons()))
        )
    )
);

CREATE POLICY "Users can view permitted attendance" ON "Attendance" FOR SELECT USING (
    is_admin() OR "studentId" IN (SELECT auth_user_students())
);

CREATE POLICY "Users can view permitted events" ON "Event" FOR SELECT USING (
    is_admin() OR "classId" IS NULL OR "classId" IN (SELECT auth_user_classes())
);

CREATE POLICY "Users can view permitted announcements" ON "Announcement" FOR SELECT USING (
    is_admin() OR "classId" IS NULL OR "classId" IN (SELECT auth_user_classes())
);
