-- =====================================================
-- Notification Rules + Scheduled Jobs
--
-- Configurable notification triggers per account.
-- Scheduled job runner with tracking.
-- Pending notifications queue for async dispatch.
-- =====================================================

-- 1. Notification rules — configurable triggers per account
CREATE TABLE IF NOT EXISTS public.member_notification_rules (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  account_id uuid NOT NULL REFERENCES public.accounts(id) ON DELETE CASCADE,
  trigger_event text NOT NULL CHECK (trigger_event IN (
    'application.submitted', 'application.approved', 'application.rejected',
    'member.created', 'member.status_changed',
    'member.birthday', 'member.anniversary',
    'dues.unpaid', 'mandate.revoked'
  )),
  channel text NOT NULL DEFAULT 'in_app' CHECK (channel IN ('in_app', 'email', 'both')),
  recipient_type text NOT NULL CHECK (recipient_type IN (
    'admin', 'member', 'specific_user', 'role_holder'
  )),
  recipient_config jsonb NOT NULL DEFAULT '{}',
  subject_template text,
  message_template text NOT NULL,
  is_active boolean NOT NULL DEFAULT true,
  created_at timestamptz NOT NULL DEFAULT now()
);

CREATE INDEX ix_notification_rules_account
  ON public.member_notification_rules(account_id, trigger_event)
  WHERE is_active = true;

ALTER TABLE public.member_notification_rules ENABLE ROW LEVEL SECURITY;
REVOKE ALL ON public.member_notification_rules FROM authenticated, service_role;
GRANT SELECT, INSERT, UPDATE, DELETE ON public.member_notification_rules TO authenticated;
GRANT ALL ON public.member_notification_rules TO service_role;

CREATE POLICY notification_rules_select
  ON public.member_notification_rules FOR SELECT TO authenticated
  USING (public.has_role_on_account(account_id));

CREATE POLICY notification_rules_mutate
  ON public.member_notification_rules FOR ALL TO authenticated
  USING (public.has_permission(auth.uid(), account_id, 'members.write'::public.app_permissions));

-- 2. Scheduled job configuration per account
CREATE TABLE IF NOT EXISTS public.scheduled_job_configs (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  account_id uuid NOT NULL REFERENCES public.accounts(id) ON DELETE CASCADE,
  job_type text NOT NULL CHECK (job_type IN (
    'birthday_notification', 'anniversary_notification',
    'dues_reminder', 'data_quality_check', 'gdpr_retention_check'
  )),
  is_enabled boolean NOT NULL DEFAULT true,
  config jsonb NOT NULL DEFAULT '{}',
  last_run_at timestamptz,
  next_run_at timestamptz,
  created_at timestamptz NOT NULL DEFAULT now(),
  UNIQUE(account_id, job_type)
);

ALTER TABLE public.scheduled_job_configs ENABLE ROW LEVEL SECURITY;
REVOKE ALL ON public.scheduled_job_configs FROM authenticated, service_role;
GRANT SELECT, INSERT, UPDATE ON public.scheduled_job_configs TO authenticated;
GRANT ALL ON public.scheduled_job_configs TO service_role;

CREATE POLICY scheduled_jobs_select
  ON public.scheduled_job_configs FOR SELECT TO authenticated
  USING (public.has_role_on_account(account_id));

CREATE POLICY scheduled_jobs_mutate
  ON public.scheduled_job_configs FOR ALL TO authenticated
  USING (public.has_permission(auth.uid(), account_id, 'members.write'::public.app_permissions));

-- 3. Job run history
CREATE TABLE IF NOT EXISTS public.scheduled_job_runs (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  job_config_id uuid NOT NULL REFERENCES public.scheduled_job_configs(id) ON DELETE CASCADE,
  status text NOT NULL DEFAULT 'running' CHECK (status IN ('running', 'completed', 'failed')),
  result jsonb,
  started_at timestamptz NOT NULL DEFAULT now(),
  completed_at timestamptz
);

CREATE INDEX ix_job_runs_config
  ON public.scheduled_job_runs(job_config_id, started_at DESC);

ALTER TABLE public.scheduled_job_runs ENABLE ROW LEVEL SECURITY;
REVOKE ALL ON public.scheduled_job_runs FROM authenticated, service_role;
GRANT SELECT ON public.scheduled_job_runs TO authenticated;
GRANT ALL ON public.scheduled_job_runs TO service_role;

CREATE POLICY job_runs_select
  ON public.scheduled_job_runs FOR SELECT TO authenticated
  USING (EXISTS (
    SELECT 1 FROM public.scheduled_job_configs jc
    WHERE jc.id = scheduled_job_runs.job_config_id
      AND public.has_role_on_account(jc.account_id)
  ));

-- 4. Pending notifications queue (lightweight, processed by cron)
CREATE TABLE IF NOT EXISTS public.pending_member_notifications (
  id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  account_id uuid NOT NULL,
  trigger_event text NOT NULL,
  member_id uuid,
  context jsonb NOT NULL DEFAULT '{}',
  created_at timestamptz NOT NULL DEFAULT now(),
  processed_at timestamptz
);

CREATE INDEX ix_pending_notifications_unprocessed
  ON public.pending_member_notifications(created_at)
  WHERE processed_at IS NULL;

-- No RLS — only service_role accesses this table
REVOKE ALL ON public.pending_member_notifications FROM authenticated;
GRANT ALL ON public.pending_member_notifications TO service_role;

-- 5. Trigger: queue notifications when audit events fire
CREATE OR REPLACE FUNCTION public.queue_notification_on_audit()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
DECLARE
  v_event text;
BEGIN
  -- Map audit action to notification trigger event
  v_event := CASE NEW.action
    WHEN 'created' THEN 'member.created'
    WHEN 'status_changed' THEN 'member.status_changed'
    WHEN 'application_approved' THEN 'application.approved'
    WHEN 'application_rejected' THEN 'application.rejected'
    ELSE NULL
  END;

  IF v_event IS NULL THEN
    RETURN NEW;
  END IF;

  -- Only queue if there are active rules for this event
  IF EXISTS (
    SELECT 1 FROM public.member_notification_rules
    WHERE account_id = NEW.account_id
      AND trigger_event = v_event
      AND is_active = true
  ) THEN
    INSERT INTO public.pending_member_notifications (account_id, trigger_event, member_id, context)
    VALUES (
      NEW.account_id,
      v_event,
      NEW.member_id,
      jsonb_build_object(
        'audit_action', NEW.action,
        'changes', NEW.changes,
        'metadata', NEW.metadata,
        'user_id', NEW.user_id
      )
    );
  END IF;

  RETURN NEW;
END;
$$;

CREATE TRIGGER trg_audit_queue_notifications
  AFTER INSERT ON public.member_audit_log
  FOR EACH ROW
  EXECUTE FUNCTION public.queue_notification_on_audit();

-- 6. Queue trigger for application submissions (from membership_applications, not audit log)
CREATE OR REPLACE FUNCTION public.queue_notification_on_application()
RETURNS trigger
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = ''
AS $$
BEGIN
  IF NEW.status = 'submitted' AND (TG_OP = 'INSERT' OR OLD.status IS DISTINCT FROM NEW.status) THEN
    IF EXISTS (
      SELECT 1 FROM public.member_notification_rules
      WHERE account_id = NEW.account_id
        AND trigger_event = 'application.submitted'
        AND is_active = true
    ) THEN
      INSERT INTO public.pending_member_notifications (account_id, trigger_event, context)
      VALUES (
        NEW.account_id,
        'application.submitted',
        jsonb_build_object(
          'application_id', NEW.id,
          'first_name', NEW.first_name,
          'last_name', NEW.last_name,
          'email', NEW.email
        )
      );
    END IF;
  END IF;
  RETURN NEW;
END;
$$;

CREATE TRIGGER trg_application_queue_notifications
  AFTER INSERT OR UPDATE OF status ON public.membership_applications
  FOR EACH ROW
  EXECUTE FUNCTION public.queue_notification_on_application();
