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

import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.Context
import com.github.ajalt.clikt.parameters.groups.provideDelegate
import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import io.ktor.client.*
import tech.libeufin.ebics.*
import tech.libeufin.ebisync.*
import tech.libeufin.common.*
import tech.libeufin.common.crypto.CryptoUtil
import java.nio.file.FileAlreadyExistsException
import java.nio.file.Path
import java.nio.file.StandardOpenOption
import java.time.Instant
import kotlin.io.path.Path
import kotlin.io.path.writeBytes

fun expectFullKeys(cfg: EbicsKeysConfig): Pair<ClientPrivateKeysFile, BankPublicKeysFile> =
    expectFullKeys(cfg, "libeufin-ebisync setup")

class Setup: TalerCmd() {
    override fun help(context: Context) = "Set up the EBICS subscriber"

    private val forceKeysResubmission by option(
        help = "Resubmits all the keys to the bank"
    ).flag(default = false)
    private val autoAcceptKeys by option(
        help = "Accepts the bank keys without interactively asking the user"
    ).flag(default = false)
    private val generateRegistrationPdf by option(
        help = "Generates the PDF with the client public keys to send to the bank"
    ).flag(default = false)
    private val ebicsLog by ebicsLogOption()
    /**
     * This function collects the main steps of setting up an EBICS access.
     */
    override fun run() = cliCmd(logger) {
        val cfg = ebisyncConfig(config)

        val httpClient = httpClient()
        val ebicsLogger = EbicsLogger(ebicsLog)

        logger.info("Check EBICS setup")

        val (clientKeys, bankKeys) = ebicsSetup(
            httpClient,
            ebicsLogger,
            cfg,
            cfg,
            cfg.setup,
            forceKeysResubmission,
            generateRegistrationPdf,
            autoAcceptKeys,
            true
        )
        
        // Check account information
        logger.info("Doing administrative request HKD")
        cfg.withDb { db, _ ->
            EbicsClient(
                cfg,
                httpClient, 
                db.ebics,
                ebicsLogger,
                clientKeys,
                bankKeys
            ).download(EbicsOrder.V3.HKD) { stream ->
                val (partner, users) = EbicsAdministrative.parseHKD(stream)
                // Debug logging
                logger.debug {
                    buildString {
                        if (partner.name != null || partner.accounts.isNotEmpty()) {
                            append("Partner Info: ")
                            if (partner.name != null) {
                                append("'")
                                append(partner.name)
                                append("'")
                            }
                            for ((currency, iban, bic) in partner.accounts) {
                                append(' ')
                                append(currency)
                                append('-')
                                append(iban)
                                append('-')
                                append(bic)
                            }
                            append('\n')
                        }
                        append("Supported orders:\n")
                        for ((order, description) in partner.orders) {
                            append("- ")
                            append(order.description())
                            append(": ")
                            append(description)
                            append('\n')
                        }
                    }
                }
            }
        }
        logger.info("EBICS ready")

        logger.info("Check fetch destination setup")
        when (val dest = DestinationClient.prepare(cfg.fetch.destination, httpClient)) {
            null -> logger.warn("No destination configured")
            is DestinationClient.AzureBlobStorage -> {
                dest.client.getContainterMetadata(dest.container)
            }
        }
        logger.info("Fetch destination ready")

        println("setup ready")
    }
}