-- Minimum authoritative Project -> Asset / durable Generation Job links.
-- Apply after projects/0001 and security/0003_generation_domain_postgres.sql.

begin;

alter table media_assets add column if not exists project_id text;

do $$
begin
  if not exists (
    select 1 from pg_constraint
    where conname = 'fk_media_assets_project'
      and conrelid = 'media_assets'::regclass
  ) then
    alter table media_assets
      add constraint fk_media_assets_project
      foreign key (project_id) references projects(id) on delete restrict;
  end if;
end $$;

create index if not exists ix_media_assets_workspace_project_created
  on media_assets(workspace_id, project_id, created_at desc);

create or replace function mediarouter_assert_media_asset_project_workspace()
returns trigger language plpgsql as $$
declare project_workspace text;
declare project_status text;
begin
  if tg_op = 'UPDATE' and new.workspace_id is distinct from old.workspace_id then
    raise exception 'canonical asset workspace is immutable' using errcode = '23514';
  end if;
  if new.project_id is not null then
    select workspace_id, status into project_workspace, project_status from projects
      where id = new.project_id;
    if project_workspace is null or project_workspace is distinct from new.workspace_id then
      raise exception 'project asset must belong to its project workspace'
        using errcode = '23503';
    end if;
    if project_status is distinct from 'active' then
      raise exception 'canonical assets cannot be attached to an archived project'
        using errcode = '23514';
    end if;
  end if;
  return new;
end;
$$;

drop trigger if exists mediarouter_media_asset_project_workspace on media_assets;
create trigger mediarouter_media_asset_project_workspace
before insert or update of workspace_id, project_id on media_assets
for each row execute function mediarouter_assert_media_asset_project_workspace();

create table if not exists project_generation_jobs (
  id text primary key default gen_random_uuid()::text,
  workspace_id text not null references workspaces(id) on delete restrict,
  project_id text not null references projects(id) on delete restrict,
  generation_job_id text not null references generation_jobs(id) on delete restrict,
  attached_by text not null references users(id) on delete restrict,
  created_at timestamptz not null default now(),
  constraint uq_project_generation_job unique (generation_job_id)
);

create index if not exists ix_project_generation_jobs_workspace_project_created
  on project_generation_jobs(workspace_id, project_id, created_at desc);

create or replace function mediarouter_assert_project_generation_job_workspace()
returns trigger language plpgsql as $$
declare project_workspace text;
declare project_status text;
declare job_workspace text;
begin
  if tg_op = 'UPDATE' and (
    new.workspace_id is distinct from old.workspace_id
    or new.project_id is distinct from old.project_id
    or new.generation_job_id is distinct from old.generation_job_id
    or new.attached_by is distinct from old.attached_by
  ) then
    raise exception 'project generation job ownership is immutable'
      using errcode = '23514';
  end if;

  select workspace_id, status into project_workspace, project_status
    from projects where id = new.project_id;
  select workspace_id into job_workspace
    from generation_jobs where id = new.generation_job_id;

  if project_workspace is null
     or job_workspace is null
     or project_workspace is distinct from new.workspace_id
     or job_workspace is distinct from new.workspace_id then
    raise exception 'project generation job resources must share a workspace'
      using errcode = '23503';
  end if;
  if project_status is distinct from 'active' then
    raise exception 'generation jobs cannot be attached to an archived project'
      using errcode = '23514';
  end if;
  if not exists (
    select 1 from workspace_memberships m
    where m.workspace_id = new.workspace_id
      and m.user_id = new.attached_by
      and m.status = 'active'
  ) then
    raise exception 'generation job attachment requires active workspace membership'
      using errcode = '23503';
  end if;
  return new;
end;
$$;

drop trigger if exists mediarouter_project_generation_job_workspace
  on project_generation_jobs;
create trigger mediarouter_project_generation_job_workspace
before insert or update on project_generation_jobs
for each row execute function mediarouter_assert_project_generation_job_workspace();

alter table project_generation_jobs enable row level security;
alter table project_generation_jobs force row level security;

drop policy if exists project_generation_jobs_select on project_generation_jobs;
create policy project_generation_jobs_select on project_generation_jobs for select using (
  workspace_id = current_setting('app.workspace_id', true)
  and exists (
    select 1 from workspace_memberships m
    where m.workspace_id = project_generation_jobs.workspace_id
      and m.user_id = current_setting('app.user_id', true)
      and m.status = 'active'
  )
);

drop policy if exists project_generation_jobs_insert on project_generation_jobs;
create policy project_generation_jobs_insert on project_generation_jobs for insert with check (
  workspace_id = current_setting('app.workspace_id', true)
  and attached_by = current_setting('app.user_id', true)
  and exists (
    select 1 from workspace_memberships m
    where m.workspace_id = project_generation_jobs.workspace_id
      and m.user_id = current_setting('app.user_id', true)
      and m.status = 'active'
  )
);

drop policy if exists project_generation_jobs_delete on project_generation_jobs;
create policy project_generation_jobs_delete on project_generation_jobs for delete using (
  workspace_id = current_setting('app.workspace_id', true)
  and exists (
    select 1 from workspace_memberships m
    where m.workspace_id = project_generation_jobs.workspace_id
      and m.user_id = current_setting('app.user_id', true)
      and m.status = 'active'
  )
);

commit;
