/*
 * 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/>
 */

import tech.libeufin.common.*
import kotlin.test.*

class TxMetadataTest {
    fun assertFailsMsg(msg: String, lambda: () -> Unit) {
        val failure = assertFails(lambda)
        assertEquals(msg, failure.message)
    }

    @Test
    fun parse() {
        val upper = "4MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0"
        val (upperL, upperR) = upper.chunked(26)
        val mixed = "4mzt6RS3rvb3b0e2rdmyw0yra3y0vphyv0cyde6xbb0ympfxceg0"
        val (mixedL, mixedR) = mixed.chunked(26)
        val key = TalerIncomingMetadata(TalerIncomingType.reserve, EddsaPublicKey(upper))
        
        // Check succeed if upper or mixed
        for (case in sequenceOf(upper, mixed)) {
            for (test in sequenceOf(
                "noise $case noise",
                "$case noise to the right",
                "noise to the left $case",
                "    $case     ",
                "noise\n$case\nnoise",
                "Test+$case"
            )) {
                assertEquals(key, parseIncomingTxMetadata(test))
            }
        }

        // Check succeed if upper or mixed and split
        for ((L, R) in sequenceOf(upperL to upperR, mixedL to mixedR)) {
            for (case in sequenceOf(
                "left $L$R right",
                "left $L $R right",
                "left $L-$R right",
                "left $L+$R right",
                "left $L\n$R right",
                "left $L-+\n$R right",
                "left $L - $R right",
                "left $L + $R right",
                "left $L \n $R right",
                "left $L - + \n $R right",
            )) {
                assertEquals(key, parseIncomingTxMetadata(case))
            }
        }

        // Check parts
        for (case in sequenceOf(
            upper.chunked(12).joinToString(" "),
            "left ${upper.chunked(1).joinToString(" ")} right",
        ))

        // Check failure when multiple keys match
        for (case in sequenceOf(
            "$upper $upper",
            "$mixed $mixed",
            "$mixed $upper",
            "$mixedL-$mixedR $upperL-$upperR"
        )) {
            assertFailsMsg("Found multiple reserve public key") {
                parseIncomingTxMetadata(case)
            }
        }
       
        // Check failure if malformed or missing
        for (case in sequenceOf(
            "does not contain any reserve",                        // Check fail if none
            upper.substring(0, upper.length-1),                    // Check fail if missing char
            "2MZT6RS3RVB3B0E2RDMYW0YRA3Y0VPHYV0CYDE6XBB0YMPFXCEG0" // Check fail if not a valid key
        )) {
            assertFailsMsg("Missing reserve public key") {
                parseIncomingTxMetadata(case)
            }
        }
    }

    /** Test parsing logic using real use case */
    @Test
    fun real() {
        // Good cases
        for ((subject, key) in sequenceOf(
            "Taler TEGY6d9mh9pgwvwpgs0z0095z854xegfy7j j202yd0esp8p0za60" to "TEGY6d9mh9pgwvwpgs0z0095z854xegfy7jj202yd0esp8p0za60",
            "00Q979QSMJ29S7BJT3DDAVC5A0DR5Z05B7N 0QT1RCBQ8FXJPZ6RG" to "00Q979QSMJ29S7BJT3DDAVC5A0DR5Z05B7N0QT1RCBQ8FXJPZ6RG",
            "Taler NDDCAM9XN4HJZFTBD8V6FNE2FJE8G Y734PJ5AGQMY06C8D4HB3Z0" to "NDDCAM9XN4HJZFTBD8V6FNE2FJE8GY734PJ5AGQMY06C8D4HB3Z0",
        )) {
            assertEquals(
                TalerIncomingMetadata(TalerIncomingType.reserve, EddsaPublicKey(key)),
                parseIncomingTxMetadata(subject)
            )
        }
    }
}