--
-- 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_gc;
CREATE FUNCTION anastasis_do_gc (
  IN in_expire_backups INT8, -- accounts and truths expired before this are removed
  IN in_expire_pending_payments INT8) -- unpaid payment records older than this are removed
RETURNS void
LANGUAGE plpgsql
AS $$
BEGIN

-- The children of anastasis_user reference it with NO ACTION, so they have to
-- go first; deleting the account rows on their own raises
-- foreign_key_violation for every user that ever uploaded a document, which
-- used to abort the whole collection and reclaim nothing.
DELETE FROM anastasis_recoverydocument
 WHERE user_id IN
   (SELECT user_id
      FROM anastasis_user
     WHERE expiration_date < in_expire_backups);

DELETE FROM anastasis_recdoc_payment
 WHERE user_id IN
   (SELECT user_id
      FROM anastasis_user
     WHERE expiration_date < in_expire_backups);

DELETE FROM anastasis_user
 WHERE expiration_date < in_expire_backups;

-- Expired truths hold encrypted key shares, so they must not be kept past the
-- date they were paid for.  anastasis_truth has no foreign key to the account.
DELETE FROM anastasis_challengecode
 WHERE expiration_date < in_expire_backups;

DELETE FROM anastasis_truth
 WHERE expiration < in_expire_backups;

DELETE FROM anastasis_truth_payment
 WHERE expiration < in_expire_backups;

-- Payment records that never resulted in a payment (or were refunded) are
-- only useful while the client may still come back with the receipt.
DELETE FROM anastasis_recdoc_payment
 WHERE paid=FALSE
   AND creation_date < in_expire_pending_payments;

-- anastasis_challenge_payment has no expiration column, so the
-- pending-payment horizon doubles as its retention policy.
DELETE FROM anastasis_challenge_payment
 WHERE (paid=FALSE OR refunded)
   AND creation_date < in_expire_pending_payments;

-- A paid challenge payment is not covered by the horizon above, and
-- truth_uuid is not a foreign key, so nothing would ever remove it.  Once the
-- truth it paid for is gone -- deleted just above, or never created -- the
-- record cannot be acted on again, so it is collected here regardless of age.
-- This runs after the truth deletes on purpose, so that a truth expiring in
-- this same pass takes its payments with it.
DELETE FROM anastasis_challenge_payment cp
 WHERE NOT EXISTS
   (SELECT 1
      FROM anastasis_truth t
     WHERE t.truth_uuid = cp.truth_uuid);

-- Bank statements collected by the IBAN authorization helper carry customer
-- account details, so they should not be kept indefinitely either.  The
-- newest row per credit account is retained whatever its age: the helper
-- resumes from the highest wire_reference it finds here, and deleting that
-- would make it re-download the bank history from the beginning.
DELETE FROM anastasis_auth_iban_in ai
 WHERE ai.execution_date < in_expire_backups
   AND ai.wire_reference <>
       (SELECT MAX(x.wire_reference)
          FROM anastasis_auth_iban_in x
         WHERE x.credit_account_details = ai.credit_account_details);

END $$;

COMMENT ON FUNCTION anastasis_do_gc
  IS 'Removes expired accounts with their recovery documents and payments, expired truths with their challenge codes and payments, challenge payments whose truth is gone, payment records that were never paid or were refunded, and stale IBAN authentication transfers.  Runs as a single statement, so it either collects everything or nothing.';
