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

import kotlinx.serialization.Serializable
import tech.libeufin.common.*
import tech.libeufin.common.api.*
import tech.libeufin.ebics.*
import tech.libeufin.ebisync.*
import tech.libeufin.ebisync.db.Database
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.server.http.content.*
import io.ktor.http.content.*
import io.ktor.http.*
import io.ktor.utils.io.*
import java.nio.file.Path
import tech.libeufin.common.VERSION

@Serializable
class TalerEbiSyncConfig() {
    val name: String = "taler-ebisync"
    val version: String = "0:0:0"
    val spa_version: String = VERSION
}

@Serializable
data class ListSubmitOrders(
    val orders: List<SubmitOrder>
)

@Serializable
data class SubmitOrder(
    val id: String,
    val description: String
)

@Serializable
data class SyncSubmit(
    val order: String
)

fun Routing.syncApi(auth: AuthMethod, client: EbicsClient, spa: Path) {
    suspend fun orders() = client.download(EbicsOrder.V3.HKD) { stream ->
        val hkd = EbicsAdministrative.parseHKD(stream)
        hkd.partner.orders
            .filter { it.order.isUpload() }
    }

    get("/config") {
        call.respond(TalerEbiSyncConfig())
    }
    apiAuth(auth) {
        get("/") {
            call.respondRedirect("/webui/")
        }
        staticFiles("/webui/", spa.toFile())
        get("/submit") {
            call.respond(ListSubmitOrders(orders().map { SubmitOrder(it.order.description(), it.description) }))
        }
        post("/submit") {
            call.attributes.set(BODY_LIMIT, 10 * 1024 * 1024)
            val multipart = call.receiveMultipart()
            var orderId: String? = null
            var xml: ByteArray? = null
            
            multipart.forEachPart { part ->
                when (part) {
                    is PartData.FormItem -> {
                        if (part.name == "order") {
                            orderId = part.value
                        }
                    }
                    is PartData.FileItem -> {
                        xml = part.provider().toByteArray()
                    }
                    else -> {}
                }
                part.dispose()
            }

            if (xml == null) {
                throw badRequest("Missing file", TalerErrorCode.GENERIC_PARAMETER_MISSING) 
            } else if (orderId == null) {
                throw badRequest("Missing orderId", TalerErrorCode.GENERIC_PARAMETER_MISSING)
            }
            val match = orders().find { it.order.description() == orderId } ?: throw notFound(
                "Unknown order '$orderId'", TalerErrorCode.END
            )

            val order = try {
                client.upload(match.order, xml)
            } catch (e: Exception) {
                if (e is EbicsError.Code) {
                    throw conflict(e.fmt(), TalerErrorCode.END)
                } else if (e is EbicsError) {
                    throw badGateway(e.fmt(), TalerErrorCode.END)
                } else {
                    throw e
                }
            }
            call.respond(SyncSubmit(order))
        }
    }
}