--
-- This file is part of TALER
-- Copyright (C) 2024 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 do_insert_issued_receipts;
CREATE FUNCTION do_insert_issued_receipts (
   IN in_charity_id BIGINT -- charity id which made the issue receipts request
  ,IN in_blinded_sig BYTEA[] -- blinded signatures
  ,IN in_receipt_hash BYTEA -- hash over all budi key pairs (primary key)
  ,IN in_amount taler_amount -- total amount of the requested receipts
  ,IN in_year INT4
  ,OUT out_smaller_than_max_per_year BOOLEAN
  ,OUT out_charity_unknown BOOLEAN
)
LANGUAGE plpgsql
AS $$
DECLARE
  old_receipts_to_date taler_amount;
  new_receipts_to_date taler_amount;
  prior_receipts_to_date taler_amount;
  max_per_year taler_amount;
  my_year INT4;
  rolled_over BOOLEAN DEFAULT FALSE;
BEGIN
  out_charity_unknown = FALSE;
  out_smaller_than_max_per_year = FALSE;

  -- Get charity values
  SELECT
     (chari.receipts_to_date).val
    ,(chari.receipts_to_date).frac
    ,(chari.max_per_year).val
    ,(chari.max_per_year).frac
    ,current_year
   INTO
      old_receipts_to_date.val
     ,old_receipts_to_date.frac
     ,max_per_year.val
     ,max_per_year.frac
     ,my_year
   FROM charities chari
   WHERE charity_id=in_charity_id
   -- The annual limit is a read-decide-write on this row.  Take the row
   -- lock here so that the decision is atomic at ANY isolation level; do
   -- not rely on the session default being SERIALIZABLE (that default is
   -- installed best-effort and DONAUDB_start_read_committed() exists).
   FOR UPDATE OF chari;

  IF NOT FOUND
  THEN
    -- Without this arm every local stays NULL, every comparison below is
    -- NULL (i.e. false), and the function falls through to the final ELSE
    -- and answers FALSE -- which the handler reports as "annual limit
    -- exceeded" for a charity that does not exist.
    out_charity_unknown = TRUE;
    RETURN;
  END IF;

  IF (my_year > in_year)
  THEN
    -- server travelled back in time? wild, but
    -- we closed the books on the previous year,
    -- so assume balance was exceeded.
    out_smaller_than_max_per_year = FALSE;
    RETURN;
  END IF;
  IF (my_year < in_year)
  THEN
    -- bump current year; the closing total of the year we are leaving
    -- must be preserved in `history' before it is reset (see the comment
    -- on charities.receipts_to_date).  The row is only written if the
    -- request actually succeeds, see below.
    rolled_over = TRUE;
    prior_receipts_to_date = old_receipts_to_date;
    old_receipts_to_date.val = 0;
    old_receipts_to_date.frac = 0;
  END IF;

  -- calculate sum of the recent amount of receipts and the issued amount
  SELECT *
    FROM amount_add(old_receipts_to_date, in_amount)
    INTO new_receipts_to_date;
  -- check if the new receipts to date is below or equal the limit for the charity
  IF ( (max_per_year.val > new_receipts_to_date.val) OR
       ( (max_per_year.val = new_receipts_to_date.val) AND
         (max_per_year.frac >= new_receipts_to_date.frac) ) )
  THEN
    out_smaller_than_max_per_year=TRUE;
    IF (rolled_over)
    THEN
      INSERT INTO history
        (charity_id
        ,final_amount
        ,donation_year
        ) VALUES (
         in_charity_id
        ,prior_receipts_to_date
        ,my_year
      ) ON CONFLICT (charity_id, donation_year) DO NOTHING;
    END IF;
    UPDATE charities
       SET receipts_to_date=new_receipts_to_date,
           current_year=in_year
     WHERE charity_id=in_charity_id;
    INSERT INTO receipts_issued (
       blinded_sig
      ,charity_id
      ,receipt_hash
      ,amount
    ) VALUES (
       in_blinded_sig
      ,in_charity_id
      ,in_receipt_hash
      ,in_amount
    );
  ELSE
    out_smaller_than_max_per_year=FALSE;
  END IF;
END $$;

COMMENT ON FUNCTION do_insert_issued_receipts
  IS 'This is a transaction for updating the current amount of receipts of a year of a charity and saves the receipts request what makes it idempotent';
