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

import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.*
import io.ktor.server.response.*
import io.ktor.server.routing.*
import io.ktor.util.pipeline.*
import tech.libeufin.bank.*
import tech.libeufin.bank.auth.pathUsername
import tech.libeufin.bank.db.Database
import tech.libeufin.bank.db.TransferDAO
import tech.libeufin.bank.db.TransferDAO.RegistrationResult
import tech.libeufin.common.*
import tech.libeufin.common.crypto.CryptoUtil
import java.time.Instant
import java.time.Duration

fun Routing.wireTransferApi(db: Database, cfg: BankConfig) {
    get("/accounts/{USERNAME}/taler-wire-transfer-gateway/config") {
        call.respond(
            WireTransferConfig(
                currency = cfg.regionalCurrency,
                supported_formats = listOf(SubjectFormat.SIMPLE)
            )
        )
    }
    post("/accounts/{USERNAME}/taler-wire-transfer-gateway/registration") {
        val username = call.pathUsername
        val req = call.receive<SubjectRequest>();
        cfg.checkRegionalCurrency(req.credit_amount)

        if (!CryptoUtil.checkEdssaSignature(req.account_pub.raw, req.authorization_sig, req.authorization_pub))
            throw conflict(
                "invalid signature",
                TalerErrorCode.BANK_BAD_SIGNATURE
            )

        when (val result = db.transfer.register(
            username,
            req.type,
            req.account_pub,
            req.authorization_pub,
            req.authorization_sig,
            req.recurrent,
            req.credit_amount,
            Instant.now()
        )) {
            RegistrationResult.UnknownAccount -> throw unknownAccount(username)
            RegistrationResult.NotExchange -> throw notExchange(username)
            RegistrationResult.ReservePubReuse -> throw conflict(
                "reserve_pub used already",
                TalerErrorCode.BANK_DUPLICATE_RESERVE_PUB_SUBJECT
            )
            is RegistrationResult.Success -> {
                val subjects = mutableListOf<TransferSubject>()
                if (result.uuid != null)
                    subjects.add(TransferSubject.Uri(cfg.talerWithdrawUri(result.uuid), req.credit_amount))
                subjects.add(TransferSubject.Simple(fmtIncomingSubject(IncomingType.map, req.authorization_pub), req.credit_amount))
                call.respond(
                    SubjectResult(
                        subjects,
                        TalerTimestamp.never()
                    )
                )
            }
        }
    }
    delete("/accounts/{USERNAME}/taler-wire-transfer-gateway/registration") {
        val req = call.receive<Unregistration>();

        val timestamp = Instant.parse(req.timestamp)

        if (timestamp.isBefore(Instant.now().minus(Duration.ofMinutes(15))))
            throw conflict(
                "timestamp too old",
                TalerErrorCode.BANK_OLD_TIMESTAMP
            )

        if (!CryptoUtil.checkEdssaSignature(req.timestamp.toByteArray(), req.authorization_sig, req.authorization_pub))
            throw conflict(
                "invalid signature",
                TalerErrorCode.BANK_BAD_SIGNATURE
            )
            
        if (db.transfer.unregister(req.authorization_pub, Instant.now())) {
            call.respond(HttpStatusCode.NoContent)
        } else {
            throw notFound(
                "Prepared transfer '${req.authorization_pub}' not found",
                TalerErrorCode.BANK_TRANSACTION_NOT_FOUND
            )
        }
    }
}
