-- 77-storage-taxonomy-rls.sql
-- RLS for taxonomy storage buckets.  cc.examboards intentionally gets no
-- direct object-read policy: exam-board PDF bytes are exposed only through
-- API-issued, time-limited signed URLs.

create or replace function public.is_platform_admin()
returns boolean
language sql
stable
security definer
set search_path = public
as $$
  select exists (
    select 1
    from public.admin_profiles ap
    where ap.id = auth.uid()
      and coalesce(ap.is_super_admin, false)
  )
$$;

grant execute on function public.is_platform_admin() to authenticated;

-- Use the first path segment of cc.institutes objects as the institute id.
-- Expected layout: cc.institutes/<institute_uuid>/...
create or replace function public.storage_object_first_path_segment_uuid(p_name text)
returns uuid
language plpgsql
immutable
as $$
declare
  v_segment text;
begin
  v_segment := split_part(coalesce(p_name, ''), '/', 1);
  if v_segment = '' then
    return null;
  end if;
  begin
    return v_segment::uuid;
  exception when invalid_text_representation then
    return null;
  end;
end;
$$;

grant execute on function public.storage_object_first_path_segment_uuid(text) to authenticated;

-- Exam-board catalogue rows are authenticated metadata.  PDF bytes remain
-- protected separately in cc.examboards storage and require signed URLs.
alter table public.eb_specifications enable row level security;
alter table public.eb_exams enable row level security;

drop policy if exists "eb_specifications read by authenticated users" on public.eb_specifications;
drop policy if exists "eb_exams read by authenticated users" on public.eb_exams;

create policy "eb_specifications read by authenticated users"
  on public.eb_specifications
  for select
  to authenticated
  using (true);

create policy "eb_exams read by authenticated users"
  on public.eb_exams
  for select
  to authenticated
  using (true);

alter table storage.objects enable row level security;

-- Replace only the policies owned by this migration.  The absence of any
-- cc.examboards SELECT policy is the deny-by-default posture for raw reads.
drop policy if exists "cc.admin objects read by platform admins" on storage.objects;
drop policy if exists "cc.institutes objects read by institute members" on storage.objects;
drop policy if exists "cc.public objects read by authenticated users" on storage.objects;

create policy "cc.admin objects read by platform admins"
  on storage.objects
  for select
  to authenticated
  using (
    bucket_id = 'cc.admin'
    and public.is_platform_admin()
  );

create policy "cc.institutes objects read by institute members"
  on storage.objects
  for select
  to authenticated
  using (
    bucket_id = 'cc.institutes'
    and public.storage_object_first_path_segment_uuid(name) in (
      select public.user_institute_ids()
    )
  );

create policy "cc.public objects read by authenticated users"
  on storage.objects
  for select
  to authenticated
  using (bucket_id = 'cc.public');

comment on policy "cc.admin objects read by platform admins" on storage.objects
  is 'cc.admin raw reads are limited to platform admins.';
comment on policy "cc.institutes objects read by institute members" on storage.objects
  is 'cc.institutes raw reads are scoped by leading path segment institute uuid.';
comment on policy "cc.public objects read by authenticated users" on storage.objects
  is 'cc.public raw reads are available to any authenticated user.';
comment on table storage.objects
  is 'Classroom Copilot taxonomy buckets use RLS; cc.examboards raw reads stay denied and are served through API signed URLs.';
