--
-- This file is part of Anastasis
-- Copyright (C) 2026 Anastasis SARL
--
-- ANASTASIS 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.
--
-- ANASTASIS 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
-- ANASTASIS; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
--

DROP FUNCTION IF EXISTS anastasis_do_update_account_lifetime;
CREATE FUNCTION anastasis_do_update_account_lifetime (
  IN in_user_id BYTEA,
  IN in_payment_identifier BYTEA,
  IN in_absolute_lifetime INT8, -- lower bound for the new expiration
  IN in_relative_lifetime INT8, -- added on top of that lower bound
  OUT out_payment_credited BOOLEAN, -- FALSE if the payment was not new
  OUT out_paid_until INT8)
LANGUAGE plpgsql
AS $$
BEGIN

-- Credit the payment.  This is the gate: a payment that is unknown or was
-- already credited must not extend the lifetime a second time.
UPDATE anastasis_recdoc_payment
   SET paid=TRUE
 WHERE payment_identifier=in_payment_identifier
   AND user_id=in_user_id
   AND paid=FALSE;

IF NOT FOUND
THEN
  -- Payment unknown or credited before: report the lifetime the account has
  -- right now and leave it alone.
  out_payment_credited=FALSE;
  SELECT expiration_date
    INTO out_paid_until
    FROM anastasis_user
   WHERE user_id=in_user_id;
  IF NOT FOUND
  THEN
    out_paid_until=0;
  END IF;
  RETURN;
END IF;

out_payment_credited=TRUE;

-- New expiration is MAX(what the account has, the caller's lower bound)
-- plus the caller's increment.  A caller that paid for a duration passes
-- the current time as the lower bound and the duration as the increment; a
-- caller that paid up to a date passes that date and a zero increment.  The
-- LEAST() saturates at "forever" instead of overflowing INT8.
INSERT INTO anastasis_user
  (user_id
  ,expiration_date)
  VALUES
  (in_user_id
  ,LEAST(in_absolute_lifetime,
         9223372036854775807 - in_relative_lifetime)
   + in_relative_lifetime)
ON CONFLICT (user_id) DO UPDATE
  SET expiration_date=
        LEAST(GREATEST(anastasis_user.expiration_date,
                       in_absolute_lifetime),
              9223372036854775807 - in_relative_lifetime)
        + in_relative_lifetime
RETURNING expiration_date INTO out_paid_until;

END $$;

COMMENT ON FUNCTION anastasis_do_update_account_lifetime
  IS 'Credits a recovery-document payment and extends the account lifetime to MAX(current,in_absolute_lifetime)+in_relative_lifetime, creating the account if needed.  Does nothing but report the current lifetime if the payment is not new.';
