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

sealed class EbicsOrder(val schema: String) {
    data class V2_5(
        val type: String,
        val attribute: String
    ): EbicsOrder("H004")
    data class V3(
        val type: String,
        val service: String? = null,
        val scope: String? = null,
        val message: String? = null,
        val version: String? = null,
        val container: String? = null,
        val option: String? = null
    ): EbicsOrder("H005") {
        companion object {
            val WSS_PARAMS = V3(
                type = "BTD",
                service = "OTH",
                scope = "DE",
                message = "wssparam"
            )
            val HAC = V3(type = "HAC")
            val HKD = V3(type = "HKD")
            val HAA = V3(type = "HAA")
        }
    }

    fun description(): String = buildString {
        when (this@EbicsOrder) {
            is V2_5 -> {
                append(type)
                append('-')
                append(attribute)
            }
            is V3 -> {
                append(type)
                for (part in sequenceOf(service, scope, option, container)) {
                    if (part != null) {
                        append('-')
                        append(part)
                    }
                }
                if (message != null) {
                    append('-')
                    append(message)
                    if (version != null) {
                        append('.')
                        append(version)
                    }
                }
            }
        }
    }

    fun doc(): OrderDoc? {
        return when (this) {
            is V2_5 -> {
                when (this.type) {
                    "HAC" -> OrderDoc.acknowledgement
                    "Z01" -> OrderDoc.status
                    "Z52" -> OrderDoc.report
                    "Z53" -> OrderDoc.statement
                    "Z54" -> OrderDoc.notification
                    else -> null
                }
            }
            is V3 -> {
                when (this.type) {
                    "HAC" -> OrderDoc.acknowledgement
                    "BTD" -> when (this.message) {
                        "pain.002" -> OrderDoc.status
                        "camt.052" -> OrderDoc.report
                        "camt.053" -> OrderDoc.statement
                        "camt.054" -> OrderDoc.notification
                        else -> null
                    }
                    else -> null
                }
            }
        }
    }

    /** Check if EBICS order is a downloadable one */
    fun isDownload(): Boolean = when (this) {
        is V2_5 -> this.type in setOf("HAC", "Z01", "Z52", "Z53", "Z54")
        is V3 -> this.type == "HAC" || (
            this.type=="BTD" && this.message in setOf("pain.002", "camt.052", "camt.053", "camt.054")
        )
    }

    /** Check if EBICS order is an uploadable one */
    fun isUpload(): Boolean = when (this) {
        is V2_5 -> false
        is V3 -> this.type == "BTU"
    }

    /** Check if two EBICS order match ignoring the message version */
    fun match(other: EbicsOrder): Boolean = when (this) {
        is V2_5 -> other is V2_5 
                    && type == other.type 
                    && attribute == other.attribute
        is V3 -> other is V3 
                    && type == other.type 
                    && service == other.service 
                    && scope == other.scope 
                    && message == other.message 
                    && container == other.container 
                    && option == other.option
    }
}

enum class OrderDoc {
    /// EBICS acknowledgement - CustomerAcknowledgement HAC pain.002
    acknowledgement,
    /// Payment status - CustomerPaymentStatusReport pain.002
    status,
    /// Account intraday reports - BankToCustomerAccountReport camt.052
    report,
    /// Account statements - BankToCustomerStatement camt.053
    statement,
    /// Debit & credit notifications - BankToCustomerDebitCreditNotification camt.054
    notification;

    fun shortDescription(): String = when (this) {
        acknowledgement -> "EBICS acknowledgement"
        status -> "Payment status"
        report -> "Account intraday reports"
        statement -> "Account statements"
        notification -> "Debit & credit notifications"
    }

    fun fullDescription(): String = when (this) {
        acknowledgement -> "EBICS acknowledgement - CustomerAcknowledgement HAC pain.002"
        status -> "Payment status - CustomerPaymentStatusReport pain.002"
        report -> "Account intraday reports - BankToCustomerAccountReport camt.052"
        statement -> "Account statements - BankToCustomerStatement camt.053"
        notification -> "Debit & credit notifications - BankToCustomerDebitCreditNotification camt.054"
    }
}