/*
 * This file is part of LibEuFin.
 * Copyright (C) 2024, 2025, 2026 Taler Systems S.A.

 * LibEuFin is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation; either version 3, or
 * (at your option) any later version.

 * LibEuFin 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 Affero General
 * Public License for more details.

 * You should have received a copy of the GNU Affero General Public
 * License along with LibEuFin; see the file COPYING.  If not, see
 * <http://www.gnu.org/licenses/>
 */

package tech.libeufin.bank.db

import tech.libeufin.common.db.withStatement
import tech.libeufin.common.micros
import java.time.Duration
import java.time.Instant

/** Data access logic for garbage collection */
class GcDAO(private val db: Database) {
    /** Run garbage collection  */
    suspend fun collect(
        timestamp: Instant,
        abortAfter: Duration,
        cleanAfter: Duration,
        deleteAfter: Duration
    ) = db.conn { conn ->
        val abortAfterMicro = timestamp.minus(abortAfter).micros()
        val cleanAfterMicro = timestamp.minus(cleanAfter).micros()
        val deleteAfterMicro = timestamp.minus(deleteAfter).micros()
        
        // Abort pending operations
        conn.withStatement(
            """
                UPDATE taler_withdrawal_operations SET aborted = true WHERE creation_date < ? AND NOT EXISTS(
                    SELECT FROM prepared_transfers JOIN taler_withdrawal_operations USING (withdrawal_id)
                )
            """
        ) {
            bind(abortAfterMicro) 
            executeUpdate()
        }

        // Clean aborted operations, expired challenges and expired tokens
        for (smt in listOf(
            """DELETE FROM taler_withdrawal_operations WHERE aborted = true AND creation_date < ? AND NOT EXISTS(
                SELECT FROM prepared_transfers JOIN taler_withdrawal_operations USING (withdrawal_id)
            )""",
            "DELETE FROM tan_challenges WHERE expiration_date < ?",
            "DELETE FROM bearer_tokens WHERE expiration_time < ?"
        )) {
            conn.withStatement(smt) {
                bind(cleanAfterMicro) 
                executeUpdate()
            }
        }

        // Delete old bank transactions, linked operations are deleted by CASCADE
        conn.withStatement(
            "DELETE FROM bank_account_transactions WHERE transaction_date < ?"
        ) {
            bind(deleteAfterMicro) 
            executeUpdate()
        }

        // Hard delete soft deleted customer without bank transactions, bank account are deleted by CASCADE
        conn.withStatement("""
            DELETE FROM customers WHERE deleted_at IS NOT NULL AND NOT EXISTS(
                SELECT FROM bank_account_transactions NATURAL JOIN bank_accounts
                    WHERE owning_customer_id=customer_id
            )
        """) {
            executeUpdate()
        }

        // TODO clean stats
    }
}