--
-- 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/>

-- @file merchant-0043.sql
-- @brief add missing primary key constraint on merchant_product_categories

BEGIN;

SELECT _v.register_patch('merchant-0043', NULL, NULL);

SET search_path TO merchant;

CREATE PROCEDURE merchant.merchant_0043_init(s TEXT)
  LANGUAGE plpgsql
  AS $OUTER$
BEGIN

  EXECUTE format('SET LOCAL search_path TO %I', s);

  -- Add primary constraint, delete constraint-violating values first

  DELETE FROM merchant_product_categories f
        USING merchant_product_categories d
        WHERE f.product_serial = d.product_serial
          AND f.category_serial = d.category_serial
          AND f.ctid > d.ctid;
  ALTER TABLE merchant_product_categories
    ADD PRIMARY KEY(product_serial, category_serial);

  -- Add unique constraint, delete constraint-violating values first.
  -- Without this constraint the 'ON CONFLICT' in
  -- merchant_do_insert_issued_token had no arbiter it could ever match
  -- (the only unique index was the primary key on the GENERATED ...
  -- AS IDENTITY column, which the INSERT never supplies), so a replayed
  -- issuance duplicated the row and re-incremented the 'issued' counter.

  DELETE FROM merchant_issued_tokens f
        USING merchant_issued_tokens d
        WHERE f.token_family_key_serial = d.token_family_key_serial
          AND f.h_contract_terms = d.h_contract_terms
          AND f.blind_sig = d.blind_sig
          AND f.ctid > d.ctid;
  ALTER TABLE merchant_issued_tokens
    ADD CONSTRAINT merchant_issued_tokens_token_key
        UNIQUE (token_family_key_serial
               ,h_contract_terms
               ,blind_sig);

  -- A lock is identified by its UUID: re-posting a lock with a different
  -- duration or quantity must update the existing lock for the same UUID
  -- instead of stacking a second one.  Without a unique constraint the
  -- locks accumulated and the AFTER INSERT trigger counted each of them
  -- towards merchant_inventory.total_locked.
  -- Delete constraint-violating values first, keeping the most recently
  -- written row (that is the quantity the client last asked for); the
  -- AFTER DELETE trigger corrects total_locked for the removed rows.

  DELETE FROM merchant_inventory_locks f
        USING merchant_inventory_locks d
        WHERE f.product_serial = d.product_serial
          AND f.lock_uuid = d.lock_uuid
          AND f.ctid < d.ctid;
  ALTER TABLE merchant_inventory_locks
    ADD CONSTRAINT merchant_inventory_locks_product_lock_key
        UNIQUE (product_serial, lock_uuid);

  -- merchant_deposits_insert_statistics_trigger() bumps 'deposits-received'
  -- and 'deposits-fees-paid', and GET /private/statistics-report/transactions
  -- reads 'deposits-received'.  Both slugs were registered by merchant-0028,
  -- but the default registration for per-instance schemata introduced with
  -- merchant-0037 was copied from merchant-0014 and hence predates them, so
  -- no instance created since then collects deposit statistics at all (the
  -- bump procedures return silently for an unregistered slug).
  -- Note that 'deposits-fees-paid' overlaps with 'total-deposit-fees-paid':
  -- the former is collected when the deposit is recorded, the latter when the
  -- order is marked as paid, so the two do not necessarily agree.
  INSERT INTO merchant_statistic_bucket_meta
    (slug
    ,description
    ,stype
    ,ranges
    ,ages)
  VALUES
    ('deposits-received'
    ,'total amount customers deposited to us (including deposit fees)'
    ,'amount'
    ,ARRAY['hour'::merchant.statistic_range, 'day', 'week', 'month', 'quarter', 'year']
    ,ARRAY[72, 14, 12, 24, 12, 10]
    ),
    ('deposits-fees-paid'
    ,'total amount in deposit fees paid by us or our customers'
    ,'amount'
    ,ARRAY['hour'::merchant.statistic_range, 'day', 'week', 'month', 'quarter', 'year']
    ,ARRAY[72, 14, 12, 24, 12, 10]
    )
    ON CONFLICT DO NOTHING;

  SET LOCAL search_path TO merchant;

END
$OUTER$;

INSERT INTO merchant.instance_fixups
  (migration_name
  ,version)
  VALUES
  ('merchant_0043_init'
  ,43);
-- Apply new fix-up to existing instances
CALL merchant.fixup_instance_schema (43::INT8);

COMMIT;
