/*
 * This file is part of LibEuFin.
 * Copyright (C) 2024 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.nexus.db

import tech.libeufin.common.*
import tech.libeufin.common.db.*
import tech.libeufin.nexus.iso20022.IncomingPayment
import tech.libeufin.nexus.iso20022.OutgoingPayment
import java.sql.Types
import java.time.Instant

/** Data access logic for metadata listing */
class ListDAO(private val db: Database) {
    /** List incoming transaction metadata for debugging */
    suspend fun incoming(): List<IncomingTxMetadata> = db.serializable(
        """
        SELECT
             (amount).val AS amount_val
            ,(amount).frac AS amount_frac
            ,(credit_fee).val AS credit_fee_val
            ,(credit_fee).frac AS credit_fee_frac
            ,subject
            ,(bounced_transactions.initiated_outgoing_transaction_id IS NOT NULL) AS bounced
            ,execution_time
            ,debit_payto
            ,bank_id
            ,type
            ,reserve_public_key
            ,account_pub
        FROM incoming_transactions AS incoming
            LEFT JOIN talerable_incoming_transactions USING (incoming_transaction_id)
            LEFT JOIN bounced_transactions USING (incoming_transaction_id)
        ORDER BY execution_time
        """
    ) {
        all {
            val type = it.getOptEnum<TalerIncomingType>("type")
            IncomingTxMetadata(
                date = it.getLong("execution_time").asInstant(),
                amount = it.getDecimal("amount"),
                creditFee = it.getDecimal("credit_fee"),
                subject = it.getString("subject"),
                debtor = it.getString("debit_payto"),
                id = it.getString("bank_id"),
                talerable = when (type) {
                    null -> if (it.getBoolean("bounced")) "bounced" else ""
                    TalerIncomingType.reserve -> "reserve ${EddsaPublicKey(it.getBytes("reserve_public_key"))}"
                    TalerIncomingType.kyc -> "kyc ${EddsaPublicKey(it.getBytes("account_pub"))}"
                    TalerIncomingType.wad -> throw UnsupportedOperationException()
                }
            )
        }
    }

    /** List outgoing transaction metadata for debugging */
    suspend fun outgoing(): List<OutgoingTxMetadata> = db.serializable(
        """
        SELECT
             (amount).val AS amount_val
            ,(amount).frac AS amount_frac
            ,subject
            ,execution_time
            ,credit_payto
            ,end_to_end_id
            ,wtid
            ,exchange_base_url
        FROM outgoing_transactions
            LEFT JOIN talerable_outgoing_transactions using (outgoing_transaction_id)
        ORDER BY execution_time
        """
    ) {
        all {
            OutgoingTxMetadata(
                date = it.getLong("execution_time").asInstant(),
                amount = it.getDecimal("amount"),
                subject = it.getString("subject"),
                creditor = it.getString("credit_payto"),
                id = it.getString("end_to_end_id"),
                wtid = it.getBytes("wtid")?.run { ShortHashCode(this) },
                exchangeBaseUrl = it.getString("exchange_base_url")
            )
        }
    }

    /** List initiated transaction metadata for debugging */
    suspend fun initiated(): List<InitiatedTxMetadata> = db.serializable(
        """
        SELECT
             (amount).val AS amount_val
            ,(amount).frac AS amount_frac
            ,subject
            ,initiation_time
            ,submission_date
            ,submission_counter
            ,credit_payto
            ,end_to_end_id
            ,initiated_outgoing_transactions.status
            ,initiated_outgoing_transactions.status_msg
        FROM initiated_outgoing_transactions
            LEFT JOIN initiated_outgoing_batches USING (initiated_outgoing_batch_id)
        ORDER BY initiation_time
        """
    ) {
        all {
            InitiatedTxMetadata(
                date = it.getLong("initiation_time").asInstant(),
                amount = it.getDecimal("amount"),
                subject = it.getString("subject"),
                creditor = it.getString("credit_payto"),
                id = it.getString("end_to_end_id"),
                status = it.getString("status"),
                msg = it.getString("status_msg"),
                submissionTime = it.getLong("submission_date").asInstant(),
                submissionCounter = it.getInt("submission_counter")
            )
        }
    }
}

/** Incoming transaction metadata for debugging */
data class IncomingTxMetadata(
    val date: Instant,
    val amount: DecimalNumber,
    val creditFee: DecimalNumber,
    val subject: String,
    val debtor: String,
    val id: String?,
    val talerable: String?
)

/** Outgoing transaction metadata for debugging */
data class OutgoingTxMetadata(
    val date: Instant,
    val amount: DecimalNumber,
    val subject: String?,
    val creditor: String?,
    val id: String,
    val wtid: ShortHashCode?,
    val exchangeBaseUrl: String?
)

/** Initiated metadata for debugging */
data class InitiatedTxMetadata(
    val date: Instant,
    val amount: DecimalNumber,
    val subject: String,
    val creditor: String,
    val id: String,
    val status: String,
    val msg: String?,
    val submissionTime: Instant,
    val submissionCounter: Int
)