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

import tech.libeufin.nexus.*
import tech.libeufin.ebics.XmlDestructor
import java.io.InputStream
import java.time.Instant
import java.time.ZoneOffset

data class CustomerAck(
    val actionType: HacAction,
    val orderId: String?,
    val code: ExternalStatusReasonCode?,
    val info: String,
    val timestamp: Instant
) {
    fun msg(): String = buildString {
        append("$actionType")
        if (code != null) append(" ${code.isoCode}")
        append(" - '${actionType.description}'")
        if (code != null) append(" '${code.description}'")
        if (info != "") append(" - '$info'")
    }

    override fun toString(): String = buildString {
        append(timestamp.fmtDateTime())
        if (orderId != null) append(" $orderId")
        append(" ${msg()}")
    }
}

/** Parse HAC pain.002 XML file */
fun parseCustomerAck(xml: InputStream): List<CustomerAck> {
    return XmlDestructor.parse(xml, "Document") {
        one("CstmrPmtStsRpt").map("OrgnlPmtInfAndSts") {
            val actionType = one("OrgnlPmtInfId").enum<HacAction>()
            one("StsRsnInf") {
                var timestamp: Instant? = null
                var orderId: String? = null
                one("Orgtr").one("Id").one("OrgId").each("Othr") {
                    val value = one("Id")
                    val key = one("SchmeNm").one("Prtry").text()
                    when (key) {
                        "TimeStamp" -> {
                            timestamp = value.dateTime().toInstant(ZoneOffset.UTC)
                        }
                        "OrderID" -> orderId = value.text()
                    }
                }
                val code = opt("Rsn")?.one("Cd")?.enum<ExternalStatusReasonCode>()
                val info = map("AddtlInf") { text() }.joinToString("")
                CustomerAck(actionType, orderId, code, info, requireNotNull(timestamp))
            }
        }
    }
}