-- Migration: Banner notification support for announcements
-- Adds banner_enabled, show/hide times, and scheduled push intervals.
-- Hooks into the existing scheduled_notifications queue + pg_cron pipeline.

-- ============================================================================
-- 1. Banner columns on announcements
-- ============================================================================
ALTER TABLE public.announcements
  ADD COLUMN IF NOT EXISTS banner_enabled       boolean    NOT NULL DEFAULT false,
  ADD COLUMN IF NOT EXISTS banner_show_at       timestamptz,          -- null = show immediately
  ADD COLUMN IF NOT EXISTS banner_hide_at       timestamptz,          -- null = manual off only
  ADD COLUMN IF NOT EXISTS push_interval_minutes integer;             -- null = no scheduled push

-- Partial index: only index rows with active banners
CREATE INDEX IF NOT EXISTS idx_announcements_banner_active
  ON public.announcements (banner_show_at, banner_hide_at)
  WHERE banner_enabled = true AND push_interval_minutes IS NOT NULL;

-- ============================================================================
-- 2. Extend scheduled_notifications with announcement_id
-- ============================================================================
ALTER TABLE public.scheduled_notifications
  ADD COLUMN IF NOT EXISTS announcement_id uuid
    REFERENCES public.announcements(id) ON DELETE CASCADE;

CREATE INDEX IF NOT EXISTS idx_sched_notif_announcement
  ON public.scheduled_notifications(announcement_id)
  WHERE announcement_id IS NOT NULL;

-- 2a. Rebuild unique index to include announcement_id
DROP INDEX IF EXISTS uniq_sched_notif;

CREATE UNIQUE INDEX IF NOT EXISTS uniq_sched_notif ON public.scheduled_notifications (
  COALESCE(schedule_id,             '00000000-0000-0000-0000-000000000000'),
  COALESCE(task_id,                 '00000000-0000-0000-0000-000000000000'),
  COALESCE(it_service_request_id,   '00000000-0000-0000-0000-000000000000'),
  COALESCE(pass_slip_id,            '00000000-0000-0000-0000-000000000000'),
  COALESCE(announcement_id,         '00000000-0000-0000-0000-000000000000'),
  user_id,
  notify_type,
  epoch
);

-- 2b. Update CHECK constraint to allow announcement-only rows
ALTER TABLE public.scheduled_notifications
  DROP CONSTRAINT IF EXISTS chk_at_least_one_ref;

ALTER TABLE public.scheduled_notifications
  ADD CONSTRAINT chk_at_least_one_ref CHECK (
    schedule_id           IS NOT NULL
    OR task_id            IS NOT NULL
    OR it_service_request_id IS NOT NULL
    OR pass_slip_id       IS NOT NULL
    OR announcement_id    IS NOT NULL
  );

-- ============================================================================
-- 3. Enqueue function for banner announcement push notifications
-- ============================================================================
-- Called by enqueue_all_notifications() every minute via pg_cron.
-- For each active banner announcement with a push interval, inserts one
-- scheduled_notification per target user per interval epoch.
-- epoch = floor(unix_seconds / interval_seconds) ensures exactly one push
-- per user per interval window with ON CONFLICT DO NOTHING idempotency.
CREATE OR REPLACE FUNCTION public.enqueue_announcement_banner_notifications()
RETURNS void LANGUAGE plpgsql AS $$
DECLARE
  ann      RECORD;
  usr      RECORD;
  v_epoch  int;
BEGIN
  FOR ann IN
    SELECT a.id, a.author_id, a.visible_roles, a.push_interval_minutes
    FROM   public.announcements a
    WHERE  a.banner_enabled           = true
      AND  a.push_interval_minutes   IS NOT NULL
      AND  a.is_template             = false
      AND  (a.banner_show_at IS NULL OR a.banner_show_at <= now())
      AND  (a.banner_hide_at IS NULL OR a.banner_hide_at  > now())
  LOOP
    -- One epoch per interval window; prevents duplicate pushes within the window
    v_epoch := FLOOR(
      EXTRACT(EPOCH FROM now()) / (ann.push_interval_minutes * 60)
    )::int;

    -- Purge rows that are ≥2 epochs stale (unprocessed due to missed cycles).
    -- We keep epoch = v_epoch - 1 because that row's scheduled_for falls
    -- exactly at the current epoch boundary and is about to be processed.
    -- Deleting epoch < v_epoch would remove rows that are due RIGHT NOW
    -- (epoch E's row has scheduled_for = (E+1)*interval = now), causing
    -- all notifications to stop firing.
    DELETE FROM public.scheduled_notifications
    WHERE  announcement_id = ann.id
      AND  notify_type     = 'announcement_banner'
      AND  processed       = false
      AND  epoch           < v_epoch - 1;

    FOR usr IN
      SELECT p.id
      FROM   public.profiles p
      WHERE  p.role::text = ANY(ann.visible_roles)
        AND  p.id <> ann.author_id
    LOOP
      INSERT INTO public.scheduled_notifications
        (announcement_id, user_id, notify_type, scheduled_for, epoch)
      VALUES
        (ann.id, usr.id, 'announcement_banner',
         to_timestamp(((v_epoch + 1)::bigint * ann.push_interval_minutes * 60)),
         v_epoch)
      ON CONFLICT DO NOTHING;
    END LOOP;
  END LOOP;
END;
$$;

-- ============================================================================
-- 4. Re-register master dispatcher to include banner announcements
-- ============================================================================
CREATE OR REPLACE FUNCTION public.enqueue_all_notifications()
RETURNS void LANGUAGE plpgsql AS $$
BEGIN
  PERFORM public.enqueue_due_shift_notifications();
  PERFORM public.enqueue_overtime_idle_notifications();
  PERFORM public.enqueue_overtime_checkout_notifications();
  PERFORM public.enqueue_isr_event_notifications();
  PERFORM public.enqueue_isr_evidence_notifications();
  PERFORM public.enqueue_paused_task_notifications();
  PERFORM public.enqueue_backlog_notifications();
  PERFORM public.enqueue_pass_slip_expiry_notifications();
  PERFORM public.enqueue_pass_slip_expired_notifications();      -- added in 20260322150000; kept here
  PERFORM public.enqueue_announcement_banner_notifications();    -- added in this migration
END;
$$;
