--
-- This file is part of TALER
-- Copyright (C) 2026 Taler Systems SA
--
-- TALER is free software; you can redistribute it and/or modify it under the
-- terms of the GNU General Public License as published by the Free Software
-- Foundation; either version 3, or (at your option) any later version.
--
-- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-- A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License along with
-- TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
--

DROP PROCEDURE IF EXISTS merchant.sync_instance_procedures(BIGINT);
CREATE PROCEDURE merchant.sync_instance_procedures(
  in_merchant_serial BIGINT
)
  LANGUAGE plpgsql
AS $$
DECLARE
  rec RECORD;
  r RECORD;
  my_schema TEXT;
  my_schema_name TEXT;
  v_new_def TEXT;
BEGIN
  my_schema = format('merchant_instance_%s', in_merchant_serial);
  my_schema_name = my_schema || '.';

  -- First remove the existing routines: CREATE OR REPLACE FUNCTION cannot
  -- change the signature or the return type of an existing function, so
  -- without dropping first, syncing a routine whose arguments changed fails
  -- with 'cannot change return type of existing function'.
  -- Trigger functions are deliberately excluded: the triggers of the
  -- instance schema (created by the merchant_NNNN_init() fixups) depend on
  -- them, and are not part of what we copy over here.  Their signature is
  -- fixed at '() RETURNS TRIGGER' anyway, so CREATE OR REPLACE always
  -- suffices for them.
  FOR r IN
    SELECT p.oid::REGPROCEDURE AS signature,
           CASE p.prokind
             WHEN 'p' THEN 'PROCEDURE'
             ELSE 'FUNCTION'
           END AS kind
      FROM pg_proc p
      JOIN pg_namespace n
        ON n.oid = p.pronamespace
     WHERE n.nspname = my_schema
       AND p.prokind IN ('f', 'p')
       AND p.prorettype <> 'pg_catalog.trigger'::REGTYPE
  LOOP
    EXECUTE format ('DROP %s %s',
                    r.kind,
                    r.signature);
  END LOOP;

  FOR r IN
    SELECT pg_get_functiondef(p.oid) AS definition
      FROM pg_proc p
      JOIN pg_namespace n
        ON n.oid = p.pronamespace
     WHERE n.nspname = 'merchant_instances'
  LOOP
    v_new_def := replace(
      r.definition,
      'merchant_instances.',
      my_schema_name
    );
    EXECUTE v_new_def;
  END LOOP;

  FOR r IN
    SELECT pg_get_triggerdef(t.oid, true) AS trigger_def
      FROM pg_trigger t
      JOIN pg_class c
        ON c.oid = t.tgrelid
      JOIN pg_namespace n
        ON n.oid = c.relnamespace
     WHERE n.nspname = 'merchant_instances'
       AND NOT t.tgisinternal
  LOOP
    v_new_def := replace(
      r.trigger_def,
      'merchant_instances.',
      my_schema_name
    );
    EXECUTE v_new_def;
  END LOOP;
END $$;

COMMENT ON PROCEDURE merchant.sync_instance_procedures(BIGINT)
  IS 'Synchronizes procedures and triggers for the given instance by copying the current version from merchant_instances into the per-instance SCHEMA';


DROP PROCEDURE IF EXISTS merchant.sync_all_instance_procedures();
CREATE PROCEDURE merchant.sync_all_instance_procedures()
LANGUAGE plpgsql
AS $$
DECLARE
  merchant_id BIGINT;
BEGIN
  FOR merchant_id IN
    SELECT merchant_serial
      FROM merchant.merchant_instances
  LOOP
    CALL merchant.sync_instance_procedures
     (merchant_id);
  END LOOP;
END $$;
