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

import tech.libeufin.common.*
import tech.libeufin.common.db.DatabaseConfig
import tech.libeufin.ebics.EbicsSetupConfig
import tech.libeufin.ebics.EbicsHostConfig
import tech.libeufin.ebics.EbicsKeysConfig
import tech.libeufin.ebisync.db.Database
import java.nio.file.Path
import org.slf4j.Logger
import org.slf4j.LoggerFactory

private val logger: Logger = LoggerFactory.getLogger("libeufin-config")

val EBISYNC_CONFIG_SOURCE = ConfigSource("libeufin-ebisync", "ebisync", "libeufin-ebisync")

class EbisyncSetupConfig(cfg: TalerConfig): EbicsSetupConfig {
    private val sect = cfg.section("ebisync-setup")

    override val bankAuthPubKey = sect.hex("bank_authentication_pub_key_hash").orNull()
    override val bankEncPubKey = sect.hex("bank_encryption_pub_key_hash").orNull()
}

class EbisyncConfig internal constructor (val cfg: TalerConfig): EbicsKeysConfig, EbicsHostConfig {
    private val sect = cfg.section("ebisync")

    /** The bank base URL */
    override val baseUrl = sect.string("host_base_url").require()
    /** The bank EBICS host ID */
    override val hostId = sect.string("host_id").require()
    /** EBICS user ID */
    override val userId = sect.string("user_id").require()
    /** EBICS partner ID */
    override val partnerId = sect.string("partner_id").require()

    /** Path where we store the bank public keys */
    override val bankPublicKeysPath = sect.path("bank_public_keys_file").require()
    /** Path where we store our private keys */
    override val clientPrivateKeysPath = sect.path("client_private_keys_file").require()

    val setup by lazy { EbisyncSetupConfig(cfg) }
    val dbCfg by lazy { cfg.dbConfig() }
    val fetch by lazy { EbisyncFetchConfig(cfg) }
    val submit by lazy { EbisyncSubmitConfig(cfg) }
    val serverCfg by lazy {
        cfg.loadServerConfig("ebisync-httpd")
    }
    val spa by lazy {
        val sect = cfg.section("ebisync-httpd")
        sect.path("SPA").require()
    }
}

class EbisyncFetchConfig(cfg: TalerConfig) {
    private val sect = cfg.section("ebisync-fetch")

    val frequency = sect.duration("frequency").require()
    val frequencyRaw = sect.string("frequency").require()
    val checkpointTime = sect.time("checkpoint_time_of_day").require()

    val destination = sect.mapLambda("destination", "ebics file destination", mapOf(
        "none" to { Destination.None },
        "azure-blob-storage" to {
            Destination.AzureBlobStorage(
                apiUrl = sect.baseURL("azure_api_url").require(),
                accountName = sect.string("azure_account_name").require(),
                accountKey = sect.string("azure_account_key").require(),
                container = sect.string("azure_container").require()
            )
        }
    )).require()
}

class EbisyncSubmitConfig(cfg: TalerConfig) {
    private val sect = cfg.section("ebisync-submit")

    val source = sect.mapLambda("source", "ebics file source", mapOf(
        "none" to { Source.None },
        "ebisync-api" to { Source.SyncAPI(sect.requireAuthMethod()) }
    )).require()
}

private fun TalerConfig.dbConfig(): DatabaseConfig {
    val sect = section("ebisyncdb-postgres")
    return DatabaseConfig(
        dbConnStr = sect.string("config").require(),
        sqlDir = sect.path("sql_dir").require()
    )
}

/** Load ebisync cfg at [configPath] */
fun ebisyncConfig(configPath: Path?): EbisyncConfig {
    val cfg = EBISYNC_CONFIG_SOURCE.fromFile(configPath)
    return EbisyncConfig(cfg)
}

/** Load ebisync db cfg at [configPath] */
fun dbConfig(configPath: Path?): DatabaseConfig =
    EBISYNC_CONFIG_SOURCE.fromFile(configPath).dbConfig()

/** Run [lambda] with access to a database conn pool */
suspend fun EbisyncConfig.withDb(lambda: suspend (Database, EbisyncConfig) -> Unit) {
    Database(dbCfg).use { lambda(it, this) }
}

sealed interface Destination {
    data object None: Destination
    data class AzureBlobStorage(
        val apiUrl: BaseURL,
        val accountName: String,
        val accountKey: String,
        val container: String,
    ): Destination
}

sealed interface Source {
    data object None: Source
    data class SyncAPI(val auth: AuthMethod): Source
}