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

import org.junit.Test
import tech.libeufin.common.*
import tech.libeufin.common.db.*
import tech.libeufin.nexus.*
import tech.libeufin.nexus.cli.*
import tech.libeufin.nexus.db.*
import tech.libeufin.nexus.iso20022.*
import tech.libeufin.ebics.*
import java.nio.file.Files
import java.time.Instant
import java.util.UUID
import kotlin.io.path.*
import kotlin.test.*

/** End-to-end test for XML file registration */
class RegistrationTest {

    /** Register batches of initiated payments for reconcciliation */
    suspend fun Database.batches(batches: Map<String, List<InitiatedPayment>>): List<PaymentBatch> {
        val tmp = mutableListOf<PaymentBatch>()
        for ((name, txs) in batches) {
            for (tx in txs) {
                initiated.create(tx)
            }
            this.initiated.batch(Instant.now(), name, false)
            val (batch) = this.initiated.submittable()
            this.initiated.batchSubmissionSuccess(batch.id, Instant.now(), name.replace("BATCH", "ORDER"))
            tmp.add(batch)
        }
        return tmp
    }

    /** Register an XML sample into the database */
    suspend fun Database.register(
        cfg: NexusConfig,
        path: String,
        doc: OrderDoc
    ) {
        registerFile(this, cfg, Files.newInputStream(Path(path)), doc)
    }

    /** Check database content */
    suspend fun Database.check(
        status: Map<String, Pair<SubmissionState, Map<String, SubmissionState>>>,
        incoming: List<IncomingPayment>,
        outgoing: List<OutgoingPayment>,
        bounced: List<OutgoingPayment>
    ) {
        // Check batch status
        val batch_status = this.serializable(
            """
            SELECT message_id, status FROM initiated_outgoing_batches ORDER BY initiated_outgoing_batch_id
            """
        ) {
            all { 
                Pair(
                    it.getString("message_id"),
                    it.getEnum<SubmissionState>("status")
                )
            }
        }
        assertContentEquals(status.map { Pair(it.key, it.value.first) }, batch_status)

        // Check transactions status
        val batch_tx = this.serializable(
            """
            SELECT message_id, end_to_end_id, initiated_outgoing_transactions.status 
            FROM initiated_outgoing_transactions
                JOIN initiated_outgoing_batches USING (initiated_outgoing_batch_id)
            ORDER BY initiated_outgoing_batch_id, initiated_outgoing_transaction_id
            """
        ) {
            all { 
                Triple(
                    it.getString("message_id"),
                    it.getString("end_to_end_id"),
                    it.getEnum<SubmissionState>("status")
                )
            }.groupBy(
                keySelector = { it.first },
                valueTransform = { Pair(it.second, it.third) }
            ).mapValues { it.value.toMap() }
        }
        assertContentEquals(status.mapValues { it.value.second }.toList(), batch_tx.toList())

        // Check incoming transactions
        val incoming_tx = this.serializable(
            """
            SELECT 
                 uetr
                ,tx_id
                ,acct_svcr_ref
                ,(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
                ,execution_time
                ,debit_payto
            FROM incoming_transactions
            ORDER BY incoming_transaction_id
            """
        ) {
            all { 
                IncomingPayment(
                    id = IncomingId(
                        it.getObject("uetr") as UUID?,
                        it.getString("tx_id"),
                        it.getString("acct_svcr_ref"),
                    ),
                    amount = it.getAmount("amount", this@check.currency),
                    creditFee = it.getAmount("credit_fee", this@check.currency).notZeroOrNull(),
                    subject = it.getString("subject"),
                    executionTime = it.getLong("execution_time").asInstant(),
                    debtor = it.getOptIbanPayto("debit_payto"),
                )
            }
        }
        assertContentEquals(incoming, incoming_tx)

        // Check outgoing transactions
        val outgoing_tx = this.serializable(
            """
            SELECT end_to_end_id
                ,acct_svcr_ref
                ,(amount).val as amount_val
                ,(amount).frac AS amount_frac
                ,(debit_fee).val AS debit_fee_val
                ,(debit_fee).frac AS debit_fee_frac
                ,subject
                ,execution_time
                ,credit_payto
            FROM outgoing_transactions
            ORDER BY outgoing_transaction_id
            """
        ) {
            all {
                OutgoingPayment(
                    id = OutgoingId(null, it.getString("end_to_end_id"), it.getString("acct_svcr_ref")),
                    amount = it.getAmount("amount", this@check.currency),
                    debitFee = it.getAmount("debit_fee", this@check.currency).notZeroOrNull(),
                    subject = it.getString("subject"),
                    executionTime = it.getLong("execution_time").asInstant(),
                    creditor = it.getOptIbanPayto("credit_payto"),
                )
            }
        }
        assertContentEquals(outgoing, outgoing_tx)

        // Check outgoing transactions
        val bounced_tx = this.serializable(
            """
            SELECT end_to_end_id
                ,(amount).val as amount_val
                ,(amount).frac as amount_frac
                ,subject
                ,credit_payto
            FROM initiated_outgoing_transactions
                JOIN bounced_transactions USING (initiated_outgoing_transaction_id)
            """
        ) {
            all {
                OutgoingPayment(
                    id = OutgoingId(null, null, null),
                    amount = it.getAmount("amount", this@check.currency),
                    subject = it.getString("subject"),
                    executionTime = Instant.EPOCH,
                    creditor = it.getOptIbanPayto("credit_payto"),
                )
            }
        }
        assertContentEquals(bounced, bounced_tx)
    }

    @Test
    fun pain001() = setup { db, cfg -> 
        val (batch) = db.batches(mapOf(
            "MESSAGE_ID" to listOf(
                genInitPay(
                    endToEndId = "TX_FIRST",
                    amount = "EUR:42",
                    subject = "Test 42",
                ),
                genInitPay(
                    endToEndId = "TX_SECOND",
                    amount = "EUR:5.11",
                    subject = "Test 5.11",
                ),
                genInitPay(
                    endToEndId = "TX_THIRD",
                    amount = "EUR:0.21",
                    subject = "Test 0.21",
                ),
            ),
        ))
        val msg = batchToPain001Msg(cfg.ebics.account, batch).copy(timestamp = dateToInstant("2024-09-09"),)
        for (dialect in Dialect.entries) {
            println(dialect)
            assertEquals(
                Path("sample/platform/${dialect}_pain001.xml").readText().replace("VERSION", VERSION),
                createPain001(msg, dialect, false).asUtf8()
            )
        }
    }

    /** HAC order id test */
    @Test
    fun hac() = setup { db, cfg ->
        db.batches(mapOf(
            "BATCH_SUCCESS" to listOf(
                genInitPay("BATCH_SUCCESS_0"),
                genInitPay("BATCH_SUCCESS_1"),
            ),
            "BATCH_FAILURE" to listOf(
                genInitPay("BATCH_FAILURE_0"),
                genInitPay("BATCH_FAILURE_1"),
            )
        ))

        // Register HAC files
        db.register(cfg, "sample/platform/hac.xml", OrderDoc.acknowledgement)
        
        // Check state
        db.check(
            status = mapOf(
                "BATCH_SUCCESS" to Pair(SubmissionState.success, mapOf(
                    "BATCH_SUCCESS_0" to SubmissionState.pending,
                    "BATCH_SUCCESS_1" to SubmissionState.pending,
                )),
                "BATCH_FAILURE" to Pair(SubmissionState.permanent_failure, mapOf(
                    "BATCH_FAILURE_0" to SubmissionState.permanent_failure,
                    "BATCH_FAILURE_1" to SubmissionState.permanent_failure,
                ))
            ),
            incoming = emptyList(),
            outgoing = emptyList(),
            bounced = emptyList()
        )
    }

    /** CreditSuisse dialect test */
    @Test
    fun cs() = setup { db, cfg ->
        db.batches(mapOf(
            "05BD4C5B4A2649B5B08F6EF6A31F197A" to listOf(
                genInitPay("AQCXNCPWD8PHW5JTN65Y5XTF7R"),
                genInitPay("EE9SX76FC5YSC657EK3GMVZ9TC"),
                genInitPay("V5B3MXPEWES9VQW1JDRD6VAET4"),
                genInitPay("M9NGRCAC1FBX3ENX3XEDEPJ2JW"),
            ),
        ))

        // Register pain files
        db.register(cfg, "sample/platform/pain002_part.xml", OrderDoc.status)

        // Check state
        db.check(
            status = mapOf(
                "05BD4C5B4A2649B5B08F6EF6A31F197A" to Pair(SubmissionState.pending, mapOf(
                    "AQCXNCPWD8PHW5JTN65Y5XTF7R" to SubmissionState.permanent_failure,
                    "EE9SX76FC5YSC657EK3GMVZ9TC" to SubmissionState.permanent_failure,
                    "V5B3MXPEWES9VQW1JDRD6VAET4" to SubmissionState.permanent_failure,
                    "M9NGRCAC1FBX3ENX3XEDEPJ2JW" to SubmissionState.pending,
                )),
            ),
            incoming = emptyList(),
            outgoing = emptyList(),
            bounced = emptyList()
        )
    }

    /** Valiant dialect test */
    @Test
    fun valiant() = setup("valiant.conf") { db, cfg ->
        db.batches(mapOf(
            "MJDJO2BDDBL7YSL2P96SXHG3TQZEZQD26L" to listOf(
                genInitPay("4UWWIDGTEIGDU6Z721QE95PYJSIEA48PYE"),
            ),
            "5HIS3433VVIBAANHW3GX9DR1AXRS43KZ4U" to listOf(
                genInitPay("SKMU2891PAAYBDW22DBWX2W7KTFZ1CDFO8"),
                genInitPay("RC9YD301NZ17YKD6WDWLNOROFHIIN29VJN"),
                genInitPay("GKDGTHLB82X6XVHBJIJ1CK8MEGU9XJ2EL7"),
                genInitPay("PXCH2VVVTXEXBVDWICP23HZ4NV0H2CWW28"),
            ),
            "X166701F6RV59LP71RVWVIW9SV2AFZYLG4" to listOf(
                genInitPay("R48UBIIB7B4LX0DMVOSI0ZTJWMMG8FMNKX"),
            ),
            "OLAMDPI6YPMNRZHQ5PQ6JCVUQV2AN5NW6P" to listOf(
                genInitPay("TU2WJ54DR9Z6HT5VE494BNH4EXUSM0DRF7"),
            ),
            "6OZN5T9W7MK6BIZYE01E62NHGP5JLMUD4X" to listOf(
                genInitPay("02WDIX4J90Z1M1WNFHLNSXY59SHXQTQCMQ"),
                genInitPay("XAP5L7HVWPLCEMECU4GZK6GKUPBL0TD13Y"),
                genInitPay("GM8I8GIETR72LP6CFBGRBUDKNO2CEQBGOE")
            ),
        ))

        // Register camt files
        db.register(cfg, "sample/platform/valiant_camt052.xml", OrderDoc.report)

        // Check state
        db.check(
            status = mapOf(
                "MJDJO2BDDBL7YSL2P96SXHG3TQZEZQD26L" to Pair(SubmissionState.success, mapOf(
                    "4UWWIDGTEIGDU6Z721QE95PYJSIEA48PYE" to SubmissionState.success
                )),
                "5HIS3433VVIBAANHW3GX9DR1AXRS43KZ4U" to Pair(SubmissionState.success, mapOf(
                    "SKMU2891PAAYBDW22DBWX2W7KTFZ1CDFO8" to SubmissionState.success,
                    "RC9YD301NZ17YKD6WDWLNOROFHIIN29VJN" to SubmissionState.success,
                    "GKDGTHLB82X6XVHBJIJ1CK8MEGU9XJ2EL7" to SubmissionState.success,
                    "PXCH2VVVTXEXBVDWICP23HZ4NV0H2CWW28" to SubmissionState.success
                )),
                "X166701F6RV59LP71RVWVIW9SV2AFZYLG4" to Pair(SubmissionState.success, mapOf(
                    "R48UBIIB7B4LX0DMVOSI0ZTJWMMG8FMNKX" to SubmissionState.late_failure
                )),
                "OLAMDPI6YPMNRZHQ5PQ6JCVUQV2AN5NW6P" to Pair(SubmissionState.success, mapOf(
                    "TU2WJ54DR9Z6HT5VE494BNH4EXUSM0DRF7" to SubmissionState.success
                )),
                "6OZN5T9W7MK6BIZYE01E62NHGP5JLMUD4X" to Pair(SubmissionState.success, mapOf(
                    "02WDIX4J90Z1M1WNFHLNSXY59SHXQTQCMQ" to SubmissionState.success,
                    "XAP5L7HVWPLCEMECU4GZK6GKUPBL0TD13Y" to SubmissionState.late_failure,
                    "GM8I8GIETR72LP6CFBGRBUDKNO2CEQBGOE" to SubmissionState.success
                )),
            ),
            incoming = listOf(
                IncomingPayment(
                    id = IncomingId(null, "51030655601.0001", "ZV20251030/514778/1"),
                    amount = TalerAmount("CHF:0.85"),
                    subject = "fun stuff",
                    executionTime = dateToInstant("2025-10-30"),
                    debtor = ibanPayto("CH7389144832588726658", "Grothoff Hans"),
                ),
                IncomingPayment(
                    id = IncomingId(null, "51030655601.0002", "ZV20251030/514779/1"),
                    amount = TalerAmount("CHF:0.95"),
                    subject = "Taler PC2MKG0B7CK32K1T7DP08P6E1B7FHB6HY6R Q0PT3VTPBPRPYM1B0",
                    executionTime = dateToInstant("2025-10-30"),
                    debtor = ibanPayto("CH7389144832588726658", "Grothoff Hans"),
                ),
                IncomingPayment(
                    id = IncomingId("7b76d488-05d5-44ab-9d77-31d4165ec158", "00204EQY370", "ZV20251118/685062/1"),
                    amount = TalerAmount("CHF:4.55"),
                    subject = "TEST",
                    executionTime = dateToInstant("2025-11-18"),
                    debtor = null
                ),
            ),
            outgoing = listOf(
                OutgoingPayment(
                    id = OutgoingId(null, "4UWWIDGTEIGDU6Z721QE95PYJSIEA48PYE", "ZV20251030/511372/1"),
                    amount = TalerAmount("CHF:0.1"),
                    creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans"),
                    subject = "single 2025-10-30T09:46:04.55293090 9Z",
                    executionTime = dateToInstant("2025-10-30")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "SKMU2891PAAYBDW22DBWX2W7KTFZ1CDFO8", "ZV20251030/511373/1"),
                    amount = TalerAmount("CHF:0.1"),
                    creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans"),
                    subject = "multi 0 2025-10-30T09:46:10.3877961 30Z",
                    executionTime = dateToInstant("2025-10-30")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "RC9YD301NZ17YKD6WDWLNOROFHIIN29VJN", "ZV20251030/511373/2"),
                    amount = TalerAmount("CHF:0.11"),
                    creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans"),
                    subject = "multi 1 2025-10-30T09:46:10.3877961 30Z",
                    executionTime = dateToInstant("2025-10-30")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "GKDGTHLB82X6XVHBJIJ1CK8MEGU9XJ2EL7", "ZV20251030/511373/3"),
                    amount = TalerAmount("CHF:0.12"),
                    creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans"),
                    subject = "multi 2 2025-10-30T09:46:10.3877961 30Z",
                    executionTime = dateToInstant("2025-10-30")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "PXCH2VVVTXEXBVDWICP23HZ4NV0H2CWW28", "ZV20251030/511373/4"),
                    amount = TalerAmount("CHF:0.13"),
                    creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans"),
                    subject = "multi 3 2025-10-30T09:46:10.3877961 30Z",
                    executionTime = dateToInstant("2025-10-30")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "R48UBIIB7B4LX0DMVOSI0ZTJWMMG8FMNKX", "ZV20251030/524078/1"),
                    amount = TalerAmount("CHF:0.21"),
                    creditor = ibanPayto("CH6208704048981247126", "John Smith"),
                    subject = "bad name 2025-10-30T12:03:24.997478 811Z",
                    executionTime = dateToInstant("2025-10-30")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "02WDIX4J90Z1M1WNFHLNSXY59SHXQTQCMQ", "ZV20251030/524079/1"),
                    amount = TalerAmount("CHF:0.1"),
                    creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans"),
                    subject = "single 2025-10-30T12:04:00.37042083 6Z",
                    executionTime = dateToInstant("2025-10-30")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "XAP5L7HVWPLCEMECU4GZK6GKUPBL0TD13Y", "ZV20251030/524079/2"),
                    amount = TalerAmount("CHF:0.21"),
                    creditor = ibanPayto("CH6208704048981247126", "John Smith"),
                    subject = "bad name 2025-10-30T12:03:53.042190 686Z",
                    executionTime = dateToInstant("2025-10-30")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "TU2WJ54DR9Z6HT5VE494BNH4EXUSM0DRF7", "ZV20251030/524077/1"),
                    amount = TalerAmount("CHF:0.23"),
                    debitFee = TalerAmount("CHF:5"),
                    creditor = ibanPayto("DE48330605920000686018", "Christian Grothoff"),
                    subject = "foreign iban 2025-10-30T12:03:44.0972 63765Z",
                    executionTime = dateToInstant("2025-10-30")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "GM8I8GIETR72LP6CFBGRBUDKNO2CEQBGOE", "ZV20251030/524080/1"),
                    amount = TalerAmount("CHF:0.23"),
                    debitFee = TalerAmount("CHF:5"),
                    creditor = ibanPayto("DE48330605920000686018", "Christian Grothoff"),
                    subject = "foreign iban 2025-10-30T12:03:58.0046 73747Z",
                    executionTime = dateToInstant("2025-10-30")
                ),
            ),
            bounced = listOf(
                OutgoingPayment(
                    id = OutgoingId(null, null, null),
                    amount = TalerAmount("CHF:0.85"),
                    subject = "bounce 51030655601.0001: missing reserve public key",
                    executionTime = Instant.EPOCH,
                    creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans")
                ),
            )
        )
    }

    /** GLS dialect test */
    @Test
    fun gls() = setup("gls.conf") { db, cfg ->
        db.batches(mapOf(
            "COMPAT_SUCCESS" to listOf(
                genInitPay("COMPAT_SUCCESS")
            ),
            "COMPAT_FAILURE" to listOf(
                genInitPay("COMPAT_FAILURE")
            ),
            "BATCH_SINGLE_SUCCESS" to listOf(
                genInitPay("FD622SMXKT5QWSAHDY0H8NYG3G"),
            ),
            // JEYMR3OYZTFM7505OWWENFPAH53LNOWJHS
            "BATCH_SINGLE_FAILURE" to listOf(
                genInitPay("DAFC3NEE4T48WVC560T76ABA2C"),
            ),
            "BATCH_SINGLE_RETURN" to listOf(
                genInitPay("KLJJ28S1LVNDK1R2HCHLN884M7EKM5XGM5"),
            ),
            "BATCH_MANY_SUCCESS" to listOf(
                genInitPay("IVMIGCUIE7Q7VOF73R8GU3KGRYBZPAYC5V"),
                genInitPay("CDFN7I4FVIZ848DGDQ35DZ2K49H9EWXGAW"),
                genInitPay("35M1268GW5ZFHS5JCB41UKDQNPMD40T849"),
                genInitPay("HPOMV7A4E3P1TK9UZJS1WTM94A9V3X2SR1"),
            ),
            "BATCH_MANY_PART" to listOf(
                genInitPay("27SK3166EG36SJ7VP7VFYP0MW8"),
                genInitPay("KGTDBASWTJ6JM89WXD3Q5KFQC4"),
                genInitPay("8XK8Z7RAX224FGWK832FD40GYC"),
            ),
            // ZQOOPJC1DYBP52X119YGO6WMXU6NWIDPJK
            "BATCH_MANY_FAILURE" to listOf(
                genInitPay("4XTPKWE4A9V90PRQJCT8Z3MQZ8"),
                genInitPay("3VZZHVYJ6XP2SNPKWF4D4YVHNG"),
            )
        ))

        // Register camt files
        db.register(cfg, "sample/platform/gls_camt052.xml", OrderDoc.report)
        db.register(cfg, "sample/platform/gls_camt053.xml", OrderDoc.statement)
        // TODO camt054 with missing id before and after

        // Check state
        db.check(
            status = mapOf(
                "COMPAT_SUCCESS" to Pair(SubmissionState.success, mapOf(
                    "COMPAT_SUCCESS" to SubmissionState.success
                )),
                "COMPAT_FAILURE" to Pair(SubmissionState.pending, mapOf(
                    "COMPAT_FAILURE" to SubmissionState.permanent_failure
                )),
                "BATCH_SINGLE_SUCCESS" to Pair(SubmissionState.success, mapOf(
                    "FD622SMXKT5QWSAHDY0H8NYG3G" to SubmissionState.success
                )),
                "BATCH_SINGLE_FAILURE" to Pair(SubmissionState.pending, mapOf( // TODO success
                    "DAFC3NEE4T48WVC560T76ABA2C" to SubmissionState.pending, // TODO failure
                )),
                "BATCH_SINGLE_RETURN" to Pair(SubmissionState.success, mapOf(
                    "KLJJ28S1LVNDK1R2HCHLN884M7EKM5XGM5" to SubmissionState.late_failure,
                )),
                "BATCH_MANY_SUCCESS" to Pair(SubmissionState.success, mapOf(
                    "IVMIGCUIE7Q7VOF73R8GU3KGRYBZPAYC5V" to SubmissionState.success,
                    "CDFN7I4FVIZ848DGDQ35DZ2K49H9EWXGAW" to SubmissionState.success,
                    "35M1268GW5ZFHS5JCB41UKDQNPMD40T849" to SubmissionState.success,
                    "HPOMV7A4E3P1TK9UZJS1WTM94A9V3X2SR1" to SubmissionState.success,
                )),
                "BATCH_MANY_PART" to Pair(SubmissionState.success, mapOf(
                    "27SK3166EG36SJ7VP7VFYP0MW8" to SubmissionState.success,
                    "KGTDBASWTJ6JM89WXD3Q5KFQC4" to SubmissionState.permanent_failure,
                    "8XK8Z7RAX224FGWK832FD40GYC" to SubmissionState.permanent_failure,
                )),
                "BATCH_MANY_FAILURE" to Pair(SubmissionState.pending, mapOf( // TODO success
                    "4XTPKWE4A9V90PRQJCT8Z3MQZ8" to SubmissionState.pending, // TODO failure
                    "3VZZHVYJ6XP2SNPKWF4D4YVHNG" to SubmissionState.pending, // TODO failure
                ))
            ),
            incoming = listOf(
                IncomingPayment(
                    id = IncomingId(null, "BYLADEM1WOR-G2910276709458A2", "2024041210041357000"),
                    amount = TalerAmount("EUR:3"),
                    subject = "Taler FJDQ7W6G7NWX4H9M1MKA12090FRC9K7DA6N0FANDZZFXTR6QHX5G Test.,-",
                    executionTime = dateToInstant("2024-04-12"),
                    debtor = ibanPayto("DE84500105177118117964", "John Smith")
                ),
            ),
            outgoing = listOf(
                OutgoingPayment(
                    id = OutgoingId(null, "COMPAT_SUCCESS", "2024041801514102000"),
                    amount = TalerAmount("EUR:2"),
                    subject = "TestABC123",
                    executionTime = dateToInstant("2024-04-18"),
                    creditor = ibanPayto("DE20500105172419259181", "John Smith")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "FD622SMXKT5QWSAHDY0H8NYG3G", "2024090216552232000"),
                    amount = TalerAmount("EUR:1.1"),
                    subject = "single 2024-09-02T14:29:52.875253314Z",
                    executionTime = dateToInstant("2024-09-02"),
                    creditor = ibanPayto("DE89500105173198527518", "Grothoff Hans")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "YF5QBARGQ0MNY0VK59S477VDG4", "2024041810552821000"),
                    amount = TalerAmount("EUR:1.1"),
                    subject = "Simple tx",
                    executionTime = dateToInstant("2024-04-18"),
                    creditor = ibanPayto("DE20500105172419259181", "John Smith")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "IVMIGCUIE7Q7VOF73R8GU3KGRYBZPAYC5V", null),
                    amount = TalerAmount("EUR:44"),
                    subject = "init payment",
                    executionTime = dateToInstant("2024-09-20"),
                    creditor = ibanPayto("CH4189144589712575493", "Test")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "CDFN7I4FVIZ848DGDQ35DZ2K49H9EWXGAW", null),
                    amount = TalerAmount("EUR:44"),
                    subject = "init payment",
                    executionTime = dateToInstant("2024-09-20"),
                    creditor = ibanPayto("CH4189144589712575493", "Test")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "35M1268GW5ZFHS5JCB41UKDQNPMD40T849", null),
                    amount = TalerAmount("EUR:44"),
                    subject = "init payment",
                    executionTime = dateToInstant("2024-09-20"),
                    creditor = ibanPayto("CH4189144589712575493", "Test")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "HPOMV7A4E3P1TK9UZJS1WTM94A9V3X2SR1", null),
                    amount = TalerAmount("EUR:44"),
                    subject = "init payment",
                    executionTime = dateToInstant("2024-09-20"),
                    creditor = ibanPayto("CH4189144589712575493", "Test")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "KLJJ28S1LVNDK1R2HCHLN884M7EKM5XGM5", "2024092100252498000"),
                    amount = TalerAmount("EUR:0.42"),
                    subject = "This should fail because bad iban",
                    executionTime = dateToInstant("2024-09-23"),
                    creditor = ibanPayto("DE18500105173385245163", "John Smith")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "27SK3166EG36SJ7VP7VFYP0MW8", null),
                    amount = TalerAmount("EUR:44"),
                    subject = "init payment",
                    executionTime = dateToInstant("2024-09-04"),
                    creditor = ibanPayto("CH4189144589712575493", "Test")
                ),
            ),
            bounced = emptyList()
        )
    }

    /** Maerki Baumann dialect test */
    @Test
    fun maerki_baumann() = setup("maerki_baumann.conf") { db, cfg ->
        db.batches(mapOf(
            "BATCH_SINGLE_REPORTING" to listOf(
                genInitPay("5IBJZOWESQGPCSOXSNNBBY49ZURI5W7Q4H"),
                genInitPay("XZ15UR0XU52QWI7Q4XB88EDS44PLH7DYXH"),
                genInitPay("A09R35EW0359SZ51464E7TC37A0P2CBK04"),
                genInitPay("UYXZ78LE9KAIMBY6UNXFYT1K8KNY8VLZLT"),
            ),
        ))

        // Register camt files
        db.register(cfg, "sample/platform/maerki_baumann_camt053.xml", OrderDoc.statement)

        // Check state
        db.check(
            status = mapOf(
                "BATCH_SINGLE_REPORTING" to Pair(SubmissionState.success, mapOf(
                    "5IBJZOWESQGPCSOXSNNBBY49ZURI5W7Q4H" to SubmissionState.success,
                    "XZ15UR0XU52QWI7Q4XB88EDS44PLH7DYXH" to SubmissionState.success,
                    "A09R35EW0359SZ51464E7TC37A0P2CBK04" to SubmissionState.success,
                    "UYXZ78LE9KAIMBY6UNXFYT1K8KNY8VLZLT" to SubmissionState.success,
                )),
            ),
            incoming = listOf(
                IncomingPayment(
                    id = IncomingId("adbe4a5a-6cea-4263-b259-8ab964561a32", "41103099704.0002", "ZV20241104/765446/1"),
                    amount = TalerAmount("CHF:1"),
                    creditFee = TalerAmount("CHF:0.2"),
                    subject = "SFHP6H24C16A5J05Q3FJW2XN1PB3EK70ZPY 5SJ30ADGY68FWN68G",
                    executionTime = dateToInstant("2024-11-04"),
                    debtor = ibanPayto("CH7389144832588726658", "Mr Test")
                ),
                IncomingPayment(
                    id = IncomingId("7371795e-62fa-42dd-93b7-da89cc120faa", "41103099704.0003", "ZV20241104/765447/1"),
                    amount = TalerAmount("CHF:1"),
                    creditFee = TalerAmount("CHF:0.2"),
                    subject = "Random subject",
                    executionTime = dateToInstant("2024-11-04"),
                    debtor = ibanPayto("CH7389144832588726658", "Mr Test")
                ),
                IncomingPayment(
                    id = IncomingId(null, "50523424675.0001", "ZV20250523/851716/1"),
                    amount = TalerAmount("CHF:0.5"),
                    creditFee = TalerAmount("CHF:0.2"),
                    subject = null,
                    executionTime = dateToInstant("2025-05-23"),
                    debtor = ibanPayto("CH7389144832588726658", "Grothoff Hans")
                ),
                IncomingPayment(
                    id = IncomingId("f203fbb4-6e13-4c78-9b2a-d852fea6374a", "41202060702.0001", "ZV20241202/778108/1"),
                    amount = TalerAmount("CHF:0.05"),
                    creditFee = TalerAmount("CHF:0.2"),
                    subject = "mini",
                    executionTime = dateToInstant("2024-12-02"),
                    debtor = ibanPayto("CH7389144832588726658", "Grothoff Hans")
                ),
                IncomingPayment(
                    id = IncomingId("81b0d8c6-a677-4577-b75e-a639dcc03681", "41120636093.0001", "ZV20241121/773118/1"),
                    amount = TalerAmount("CHF:0.1"),
                    creditFee = TalerAmount("CHF:0.2"),
                    subject = "small transfer test",
                    executionTime = dateToInstant("2024-11-21"),
                    debtor = ibanPayto("CH7389144832588726658", "Grothoff Hans")
                ),
                IncomingPayment(
                    id = IncomingId(null, null, "ZV20250114/796191/1"),
                    amount = TalerAmount("CHF:3003"),
                    subject = "Fix bad payment by MB.",
                    executionTime = dateToInstant("2025-01-27"),
                    debtor = null
                ),
                IncomingPayment(
                    id = IncomingId(null, "F000787951230001", "ZV20250526/852733/1"),
                    amount = TalerAmount("CHF:1.38"),
                    creditFee = TalerAmount("CHF:0.2"),
                    subject = "Taler XT3D9MADR4V85JBWX47SMJFDQD2FDZDHHPH8R25YDG1KNVTSEH6G",
                    executionTime = dateToInstant("2025-05-26"),
                    debtor = ibanPayto("DE20500105172419259181", "Mr German")
                ),
            ),
            outgoing = listOf(
                OutgoingPayment(
                    id = OutgoingId(null, "5IBJZOWESQGPCSOXSNNBBY49ZURI5W7Q4H", "ZV20241121/773541/1"),
                    amount = TalerAmount("CHF:0.1"),
                    subject = "multi 0 2024-11-21T15:21:59.8859234 63Z",
                    executionTime = dateToInstant("2024-11-27"),
                    creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "XZ15UR0XU52QWI7Q4XB88EDS44PLH7DYXH", "ZV20241121/773541/4"),    
                    amount = TalerAmount("CHF:0.13"),
                    subject = "multi 3 2024-11-21T15:21:59.8859234 63Z",
                    executionTime = dateToInstant("2024-11-27"),
                    creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "A09R35EW0359SZ51464E7TC37A0P2CBK04", "ZV20241121/773541/3"),
                    amount = TalerAmount("CHF:0.12"),
                    subject = "multi 2 2024-11-21T15:21:59.8859234 63Z",
                    executionTime = dateToInstant("2024-11-27"),
                    creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, "UYXZ78LE9KAIMBY6UNXFYT1K8KNY8VLZLT", "ZV20241121/773541/2"),
                    amount = TalerAmount("CHF:0.11"),
                    subject = "multi 1 2024-11-21T15:21:59.8859234 63Z",
                    executionTime = dateToInstant("2024-11-27"),
                    creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, null, "GB20241220/205792/1"),
                    amount = TalerAmount("CHF:3000"),
                    subject = null,
                    executionTime = dateToInstant("2024-12-20"),
                    creditor = null
                ),
            ),
            bounced = listOf(
                OutgoingPayment(
                    id = OutgoingId(null, null, null),
                    amount = TalerAmount("CHF:1"),
                    subject = "bounce 7371795e-62fa-42dd-93b7-da89cc120faa: missing reserve public key",
                    executionTime = Instant.EPOCH,
                    creditor = ibanPayto("CH7389144832588726658", "Mr Test")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, null, null),
                    amount = TalerAmount("CHF:0.5"),
                    subject = "bounce 50523424675.0001: missing subject",
                    executionTime = Instant.EPOCH,
                    creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, null, null),
                    amount = TalerAmount("CHF:0.05"),
                    subject = "bounce f203fbb4-6e13-4c78-9b2a-d852fea6374a: missing reserve public key",
                    executionTime = Instant.EPOCH,
                    creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, null, null),
                    amount = TalerAmount("CHF:0.1"),
                    subject = "bounce 81b0d8c6-a677-4577-b75e-a639dcc03681: missing reserve public key",
                    executionTime = Instant.EPOCH,
                    creditor = ibanPayto("CH7389144832588726658", "Grothoff Hans")
                ),
                OutgoingPayment(
                    id = OutgoingId(null, null, null),
                    amount = TalerAmount("CHF:1.38"),
                    subject = "bounce F000787951230001: restricted account",
                    executionTime = Instant.EPOCH,
                    creditor = ibanPayto("DE20500105172419259181", "Mr German")
                )
            )
        )
    }
}