/*
 * 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/>
 */
package tech.libeufin.nexus

import tech.libeufin.ebics.*

infix fun Collection<EbicsOrder>.select(other: Collection<EbicsOrder>): List<EbicsOrder> 
    = this.flatMap { filter -> other.filter { order -> filter.match(order) } }

/** Supported EBICS standard */
enum class Standard {
    /// Swiss Payment Standards
    SIX,
    /// German Banking Industry Committee
    GBIC;

    fun downloadDoc(doc: OrderDoc): List<EbicsOrder> = when (this) {
        SIX -> when (doc) {
            OrderDoc.acknowledgement -> listOf(EbicsOrder.V3.HAC)
            OrderDoc.status -> listOf(EbicsOrder.V3("BTD", "PSR", "CH", "pain.002", "10", "ZIP"))
            OrderDoc.report -> listOf(EbicsOrder.V3("BTD", "STM", "CH", "camt.052", "08", "ZIP"))
            OrderDoc.statement -> listOf(EbicsOrder.V3("BTD", "EOP", "CH", "camt.053", "08", "ZIP"))
            OrderDoc.notification -> listOf(EbicsOrder.V3("BTD", "REP", "CH", "camt.054", "08", "ZIP"))
        }
        GBIC -> when (doc) {
            OrderDoc.acknowledgement -> listOf(EbicsOrder.V3.HAC)
            OrderDoc.status -> listOf(
                EbicsOrder.V3("BTD", "REP", "DE", "pain.002", null, "ZIP", "SCI"),
                EbicsOrder.V3("BTD", "REP", "DE", "pain.002", null, "ZIP", "SCT")
            )
            OrderDoc.report -> listOf(EbicsOrder.V3("BTD", "STM", "DE", "camt.052", null, "ZIP"))
            OrderDoc.statement -> listOf(EbicsOrder.V3("BTD", "EOP", "DE", "camt.053", null, "ZIP"))
            OrderDoc.notification -> listOf(
                EbicsOrder.V3("BTD", "STM", "DE", "camt.054", null, "ZIP"),
                EbicsOrder.V3("BTD", "STM", "DE", "camt.054", null, "ZIP", "SCI")
            )
        }
    }

    fun directDebit(): EbicsOrder = when (this) {
        SIX -> EbicsOrder.V3("BTU", "MCT", "CH", "pain.001", "09")
        GBIC -> EbicsOrder.V3("BTU", "SCT", null, "pain.001")
    }

    fun instantDirectDebit(): EbicsOrder? = when (this) {
        SIX -> null
        GBIC -> EbicsOrder.V3("BTU", "SCI", "DE", "pain.001")
    }
} 

/** Supported bank dialects */
enum class Dialect {
    valiant,
    raiffeisen,
    postfinance,
    gls,
    maerki_baumann;

    fun standard(): Standard = when (this) {
        valiant, postfinance, raiffeisen, maerki_baumann -> Standard.SIX
        gls -> Standard.GBIC
    }

    fun downloadDoc(doc: OrderDoc): List<EbicsOrder> {
        if (this == maerki_baumann) throw IllegalArgumentException("Maerki Baumann does not have EBICS access")
        return this.standard().downloadDoc(doc)
    }

    fun directDebit(): EbicsOrder {
        if (this == maerki_baumann) throw IllegalArgumentException("Maerki Baumann does not have EBICS access")
        return this.standard().directDebit()
    }

    fun instantDirectDebit(): EbicsOrder? {
        if (this == maerki_baumann) throw IllegalArgumentException("Maerki Baumann does not have EBICS access")
        return this.standard().instantDirectDebit()
    }

    /** All orders required for a dialect implementation to work */
    fun downloadOrders(): Set<EbicsOrder> = (
        // Administrative orders
        sequenceOf(EbicsOrder.V3.HAA, EbicsOrder.V3.HKD)
        // and documents orders
        + OrderDoc.entries.flatMap { downloadDoc(it) }
    ).toSet()
}