-- Social foundation hardening. Apply after 0001 and 0002.
-- This migration is additive: it adds database-side timestamp maintenance and
-- tenant relationship checks without changing or deleting customer data.

begin;

create or replace function mediarouter_social_touch_updated_at()
returns trigger
language plpgsql
as $$
begin
  new.updated_at = now();
  return new;
end;
$$;

do $$
declare
  table_name text;
begin
  foreach table_name in array array[
    'social_accounts', 'social_account_tokens', 'social_account_capabilities',
    'social_campaigns', 'social_posts', 'social_post_targets',
    'social_schedules', 'social_jobs'
  ] loop
    execute format(
      'drop trigger if exists mediarouter_social_touch_updated_at on %I',
      table_name
    );
    execute format(
      'create trigger mediarouter_social_touch_updated_at before update on %I for each row execute function mediarouter_social_touch_updated_at()',
      table_name
    );
  end loop;
end;
$$;

-- SQLAlchemy enums are represented as text for compatibility with the
-- existing schema. These constraints keep direct SQL writes within the same
-- state vocabulary enforced by the application state machine.
do $$
begin
  if not exists (select 1 from pg_constraint where conname = 'ck_social_accounts_status') then
    alter table social_accounts add constraint ck_social_accounts_status
      check (status in ('pending', 'connected', 'reauth_required', 'disconnected', 'error'));
  end if;
  if not exists (select 1 from pg_constraint where conname = 'ck_social_posts_status') then
    alter table social_posts add constraint ck_social_posts_status
      check (status in ('draft', 'scheduled', 'queued', 'preparing', 'processing', 'uploading', 'publishing', 'published', 'partial_success', 'retrying', 'failed', 'cancelled'));
  end if;
  if not exists (select 1 from pg_constraint where conname = 'ck_social_post_targets_status') then
    alter table social_post_targets add constraint ck_social_post_targets_status
      check (status in ('draft', 'scheduled', 'queued', 'preparing', 'processing', 'uploading', 'publishing', 'published', 'partial_success', 'retrying', 'failed', 'cancelled'));
  end if;
  if not exists (select 1 from pg_constraint where conname = 'ck_social_schedules_status') then
    alter table social_schedules add constraint ck_social_schedules_status
      check (status in ('scheduled', 'queued', 'cancelled'));
  end if;
  if not exists (select 1 from pg_constraint where conname = 'ck_social_jobs_status') then
    alter table social_jobs add constraint ck_social_jobs_status
      check (status in ('draft', 'scheduled', 'queued', 'preparing', 'processing', 'uploading', 'publishing', 'published', 'retrying', 'failed', 'cancelled'));
  end if;
  if not exists (select 1 from pg_constraint where conname = 'ck_social_jobs_attempt_bounds') then
    alter table social_jobs add constraint ck_social_jobs_attempt_bounds
      check (attempt_count >= 0 and max_attempts >= 0);
  end if;
end;
$$;

create or replace function mediarouter_social_assert_workspace_integrity()
returns trigger
language plpgsql
as $$
declare
  post_workspace text;
  related_workspace text;
  related_provider text;
  related_post_id text;
begin
  if tg_op = 'UPDATE' and tg_table_name in ('social_posts', 'social_jobs')
     and new.workspace_id is distinct from old.workspace_id then
    raise exception 'social workspace cannot be reassigned'
      using errcode = '23514';
  end if;

  if tg_table_name = 'social_posts' then
    if new.campaign_id is not null then
      select workspace_id into related_workspace
      from social_campaigns where id = new.campaign_id;
      if related_workspace is distinct from new.workspace_id then
        raise exception 'social post campaign must belong to the same workspace'
          using errcode = '23503';
      end if;
    end if;
    if new.source_variant_id is not null then
      select workspace_id into related_workspace
      from media_variants where id = new.source_variant_id;
      if related_workspace is distinct from new.workspace_id then
        raise exception 'social post variant must belong to the same workspace'
          using errcode = '23503';
      end if;
    end if;
  elsif tg_table_name = 'social_post_targets' then
    select workspace_id into post_workspace
    from social_posts where id = new.social_post_id;
    select workspace_id, provider into related_workspace, related_provider
    from social_accounts where id = new.social_account_id;
    if post_workspace is null or related_workspace is distinct from post_workspace then
      raise exception 'social post target account must belong to the post workspace'
        using errcode = '23503';
    end if;
    if new.provider is distinct from related_provider then
      raise exception 'social post target provider must match its account'
        using errcode = '23514';
    end if;
  elsif tg_table_name = 'social_post_media' and new.media_variant_id is not null then
    select workspace_id into post_workspace
    from social_posts where id = new.social_post_id;
    select workspace_id into related_workspace
    from media_variants where id = new.media_variant_id;
    if post_workspace is null or related_workspace is distinct from post_workspace then
      raise exception 'social post media variant must belong to the post workspace'
        using errcode = '23503';
    end if;
  elsif tg_table_name = 'social_jobs' then
    select workspace_id into post_workspace
    from social_posts where id = new.social_post_id;
    if post_workspace is distinct from new.workspace_id then
      raise exception 'social job must belong to the post workspace'
        using errcode = '23503';
    end if;
    if new.social_post_target_id is not null then
      select social_post_id, provider into related_post_id, related_provider
      from social_post_targets where id = new.social_post_target_id;
      if related_post_id is distinct from new.social_post_id then
        raise exception 'social job target must belong to the social post'
          using errcode = '23503';
      end if;
      if new.provider is not null and new.provider is distinct from related_provider then
        raise exception 'social job provider must match its target'
          using errcode = '23514';
      end if;
    end if;
  elsif tg_table_name = 'social_post_metrics' then
    if new.social_post_target_id is not null then
      select social_post_id, provider into related_post_id, related_provider
      from social_post_targets where id = new.social_post_target_id;
      if related_post_id is distinct from new.social_post_id then
        raise exception 'social metric target must belong to the social post'
          using errcode = '23503';
      end if;
      if new.provider is distinct from related_provider then
        raise exception 'social metric provider must match its target'
          using errcode = '23514';
      end if;
    end if;
  end if;
  return new;
end;
$$;

drop trigger if exists mediarouter_social_post_workspace_integrity on social_posts;
create trigger mediarouter_social_post_workspace_integrity
before insert or update of workspace_id, campaign_id, source_variant_id on social_posts
for each row execute function mediarouter_social_assert_workspace_integrity();

drop trigger if exists mediarouter_social_target_workspace_integrity on social_post_targets;
create trigger mediarouter_social_target_workspace_integrity
before insert or update of social_post_id, social_account_id, provider on social_post_targets
for each row execute function mediarouter_social_assert_workspace_integrity();

drop trigger if exists mediarouter_social_post_media_workspace_integrity on social_post_media;
create trigger mediarouter_social_post_media_workspace_integrity
before insert or update of social_post_id, media_variant_id on social_post_media
for each row execute function mediarouter_social_assert_workspace_integrity();

drop trigger if exists mediarouter_social_job_workspace_integrity on social_jobs;
create trigger mediarouter_social_job_workspace_integrity
before insert or update of workspace_id, social_post_id, social_post_target_id, provider on social_jobs
for each row execute function mediarouter_social_assert_workspace_integrity();

drop trigger if exists mediarouter_social_metric_workspace_integrity on social_post_metrics;
create trigger mediarouter_social_metric_workspace_integrity
before insert or update of social_post_id, social_post_target_id, provider on social_post_metrics
for each row execute function mediarouter_social_assert_workspace_integrity();

commit;
