--
-- This file is part of TALER
-- Copyright (C) 2024, 2025 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 FUNCTION IF EXISTS merchant_do_insert_transfer_details;
CREATE FUNCTION merchant_do_insert_transfer_details (
  IN in_instance_id TEXT,
  IN in_exchange_url TEXT,
  IN in_payto_uri TEXT,
  IN in_wtid BYTEA,
  IN in_execution_time INT8,
  IN in_exchange_pub BYTEA,
  IN in_exchange_sig BYTEA,
  IN in_total_amount taler_amount_currency,
  IN in_wire_fee taler_amount_currency,
  IN ina_coin_values taler_amount_currency[],
  IN ina_deposit_fees taler_amount_currency[],
  IN ina_coin_pubs BYTEA[],
  IN ina_contract_terms BYTEA[],
  OUT out_no_instance BOOL,
  OUT out_no_account BOOL,
  OUT out_no_exchange BOOL,
  OUT out_duplicate BOOL,
  OUT out_conflict BOOL)
LANGUAGE plpgsql
AS $$
DECLARE
  my_merchant_id INT8;
  my_signkey_serial INT8;
  my_expected_credit_serial INT8;
  my_affected_orders RECORD;
  my_merchant_serial INT8;
  my_decose INT8;
  my_order_id TEXT;
  i INT8;
  curs CURSOR (arg_coin_pub BYTEA) FOR
    SELECT mcon.deposit_confirmation_serial,
           mcon.order_serial
      FROM merchant_deposits dep
      JOIN merchant_deposit_confirmations mcon
        USING (deposit_confirmation_serial)
      WHERE dep.coin_pub=arg_coin_pub;
  ini_coin_pub BYTEA;
  ini_contract_term BYTEA;
  ini_coin_value taler_amount_currency;
  ini_deposit_fee taler_amount_currency;
BEGIN

-- Which instance are we using?
SELECT merchant_serial
  INTO my_merchant_id
  FROM merchant_instances
 WHERE merchant_id=in_instance_id;

IF NOT FOUND
THEN
  out_no_instance=TRUE;
  out_no_account=FALSE;
  out_no_exchange=FALSE;
  out_duplicate=FALSE;
  out_conflict=FALSE;
  RETURN;
END IF;
out_no_instance=FALSE;

-- Determine account that was credited.
SELECT expected_credit_serial
  INTO my_expected_credit_serial
  FROM merchant_expected_transfers
 WHERE exchange_url=in_exchange_url
     AND wtid=in_wtid
     AND account_serial=
     (SELECT account_serial
        FROM merchant_accounts
       WHERE payto_uri=in_payto_uri
         AND exchange_url=in_exchange_url
         AND merchant_serial=my_merchant_id);

IF NOT FOUND
THEN
  out_no_account=TRUE;
  out_no_exchange=FALSE;
  out_duplicate=FALSE;
  out_conflict=FALSE;
  RETURN;
END IF;
out_no_account=FALSE;

-- Find exchange sign key
SELECT signkey_serial
  INTO my_signkey_serial
  FROM merchant_exchange_signing_keys
 WHERE exchange_pub=in_exchange_pub
   ORDER BY start_date DESC
   LIMIT 1;

IF NOT FOUND
THEN
  out_no_exchange=TRUE;
  out_conflict=FALSE;
  out_duplicate=FALSE;
  RETURN;
END IF;
out_no_exchange=FALSE;

-- Add signature first, check for idempotent request
INSERT INTO merchant_transfer_signatures
  (expected_credit_serial
  ,signkey_serial
  ,credit_amount
  ,wire_fee
  ,execution_time
  ,exchange_sig)
  VALUES
   (my_expected_credit_serial
   ,my_signkey_serial
   ,in_total_amount
   ,in_wire_fee
   ,in_execution_time
   ,in_exchange_sig)
  ON CONFLICT DO NOTHING;

IF NOT FOUND
THEN
  PERFORM 1
    FROM merchant_transfer_signatures
    WHERE expected_credit_serial=my_expected_credit_serial
      AND signkey_serial=my_signkey_serial
      AND credit_amount=in_total_amount
      AND wire_fee=in_wire_fee
      AND execution_time=in_execution_time
      AND exchange_sig=in_exchange_sig;
  IF FOUND
  THEN
    -- duplicate case
    out_duplicate=TRUE;
    out_conflict=FALSE;
    RETURN;
  END IF;
  -- conflict case
  out_duplicate=FALSE;
  out_conflict=TRUE;
  RETURN;
END IF;

out_duplicate=FALSE;
out_conflict=FALSE;


FOR i IN 1..array_length(ina_coin_pubs,1)
LOOP
  ini_coin_value=ina_coin_values[i];
  ini_deposit_fee=ina_deposit_fees[i];
  ini_coin_pub=ina_coin_pubs[i];
  ini_contract_term=ina_contract_terms[i];

  INSERT INTO merchant_expected_transfer_to_coin
    (deposit_serial
    ,expected_credit_serial
    ,offset_in_exchange_list
    ,exchange_deposit_value
    ,exchange_deposit_fee)
    SELECT
        dep.deposit_serial
       ,my_expected_credit_serial
       ,i
       ,ini_coin_value
       ,ini_deposit_fee
      FROM merchant_deposits dep
      JOIN merchant_deposit_confirmations dcon
        USING (deposit_confirmation_serial)
      JOIN merchant_contract_terms cterm
        USING (order_serial)
      WHERE dep.coin_pub=ini_coin_pub
        AND cterm.h_contract_terms=ini_contract_term
        AND cterm.merchant_serial=my_merchant_id;

  RAISE NOTICE 'iterating over affected orders';
  OPEN curs (arg_coin_pub:=ini_coin_pub);
  LOOP
    FETCH NEXT FROM curs INTO my_affected_orders;
    EXIT WHEN NOT FOUND;

    RAISE NOTICE 'checking affected order for completion';

    my_decose=my_affected_orders.deposit_confirmation_serial;

    PERFORM FROM merchant_deposits md
       WHERE md.deposit_confirmation_serial=my_decose
         AND settlement_retry_needed
          OR settlement_wtid IS NULL;
    IF NOT FOUND
    THEN
      -- must be all done, clear flag
      UPDATE merchant_deposit_confirmations
         SET wire_pending=FALSE
       WHERE (deposit_confirmation_serial=my_decose);

      IF FOUND
      THEN
        -- Also update contract terms, if all (other) associated
        -- deposit_confirmations are also done.

        RAISE NOTICE 'checking affected contract for completion';
        PERFORM FROM merchant_deposit_confirmations mdc
               WHERE mdc.wire_pending
                 AND mdc.order_serial=my_affected_orders.order_serial;
        IF NOT FOUND
        THEN

          UPDATE merchant_contract_terms
             SET wired=TRUE
           WHERE (order_serial=my_affected_orders.order_serial);

          -- Select merchant_serial and order_id for webhook
          SELECT merchant_serial, order_id
            INTO my_merchant_serial, my_order_id
            FROM merchant_contract_terms
           WHERE order_serial=my_affected_orders.order_serial;

          -- Insert pending webhook if it exists
          INSERT INTO merchant_pending_webhooks
           (merchant_serial
           ,webhook_serial
           ,url
           ,http_method
           ,body)
           SELECT mw.merchant_serial
                 ,mw.webhook_serial
                 ,mw.url
                 ,mw.http_method
                 ,replace_placeholder(
                  replace_placeholder(mw.body_template, 'order_id', my_order_id),
                  'wtid', encode(in_wtid, 'hex')
                  )::TEXT
             FROM merchant_webhook mw
            WHERE mw.event_type = 'order_settled'
              AND mw.merchant_serial = my_merchant_serial;
        END IF; -- no more merchant_deposits waiting for wire_pending
      END IF; -- did clear wire_pending flag for deposit confirmation
    END IF; -- no more merchant_deposits wait for settlement

  END LOOP; -- END curs LOOP
  CLOSE curs;
END LOOP; -- END FOR loop

END $$;
