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

import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.core.Context
import com.github.ajalt.clikt.parameters.arguments.argument
import com.github.ajalt.clikt.parameters.groups.provideDelegate
import com.github.ajalt.clikt.parameters.options.convert
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.boolean
import tech.libeufin.bank.*
import tech.libeufin.bank.api.patchAccount
import tech.libeufin.bank.db.AccountDAO.AccountPatchResult
import tech.libeufin.common.*


class EditAccount : TalerCmd("edit-account") {
    override fun help(context: Context) = "Edit an existing account"

    private val username: String by argument(
        "username",
        help = "Account username"
    )
    private val name: String? by option(
        help = "Legal name of the account owner"
    )
    private val is_public: Boolean? by option(
        "--public",
        help = "Make this account visible to anyone"
    ).boolean()
    private val exchange: Boolean? by option(
        help = "Make this account a taler exchange"
    ).boolean()
    private val email: String? by option(help = "E-Mail address used for TAN transmission")
    private val phone: String? by option(help = "Phone number used for TAN transmission")
    private val cashout_payto_uri: IbanPayto? by option(
        help = "Payto URI of a fiant account who receive cashout amount"
    ).convert { Payto.parse(it).expectIban() }
    private val debit_threshold: TalerAmount? by option(
        help = "Max debit allowed for this account"
    ).convert { TalerAmount(it) }
    private val tan_channel: Option<TanChannel>? by option(
        help = "Enables 2FA and set the TAN channel used for challenges"
    ).convert { 
        if (it == "") {
            Option.None
        } else {
            Option.Some(TanChannel.valueOf(it))
        }
    }

    override fun run() = cliCmd(logger) {
        bankConfig(config).withDb { db, cfg ->
            val req = AccountReconfiguration(
                name = name,
                is_taler_exchange = exchange,
                is_public = is_public,
                contact_data = ChallengeContactData(
                    // PATCH semantic, if not given do not change, if empty remove
                    email = if (email == null) Option.None else Option.Some(if (email != "") email else null),
                    phone = if (phone == null) Option.None else Option.Some(if (phone != "") phone else null), 
                ),
                cashout_payto_uri = Option.Some(cashout_payto_uri),
                debit_threshold = debit_threshold,
                tan_channel = Option.invert(tan_channel)
            )
            when (val res = patchAccount(db, cfg, req, username, true, true)) {
                AccountPatchResult.Success -> 
                    logger.info("Account '$username' edited")
                AccountPatchResult.UnknownAccount -> 
                    throw Exception("Account '$username' not found")
                AccountPatchResult.MissingTanInfo -> 
                    throw Exception("missing info for tan channel ${req.tan_channel.get()}")
                AccountPatchResult.NonAdminName,
                    AccountPatchResult.NonAdminCashout,
                    AccountPatchResult.NonAdminDebtLimit,
                    AccountPatchResult.NonAdminConversionRateClass,
                    AccountPatchResult.UnknownConversionClass,
                    is AccountPatchResult.Challenges -> 
                        throw IllegalStateException("Those error can never happen $res")
            }
        }
    }
}