/*
 * This file is part of LibEuFin.
 * Copyright (C) 2024, 2025, 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.common.db

import tech.libeufin.common.*
import java.sql.*
import java.time.*
import java.util.*

fun optAmount(amount: TalerAmount?): String {
    if (amount != null) {
        return "(?,?)::taler_amount"
    } else {
        return "NULL"
    }
}

fun optDecimal(nb: DecimalNumber?): String {
    if (nb != null) {
        return "(?,?)::taler_amount"
    } else {
        return "NULL"
    }
}

inline fun <reified T : Enum<T>> ResultSet.getEnum(name: String): T
    = java.lang.Enum.valueOf(T::class.java, getString(name))
inline fun <reified T : Enum<T>> ResultSet.getEnum(idx: Int): T
    = java.lang.Enum.valueOf(T::class.java, getString(idx))

inline fun <reified T : Enum<T>> ResultSet.getOptEnum(name: String): T?
    = getString(name)?.run { java.lang.Enum.valueOf(T::class.java, this) }
inline fun <reified T : Enum<T>> ResultSet.getOptEnum(idx: Int): T?
    = getString(idx)?.run { java.lang.Enum.valueOf(T::class.java, this) }

inline fun <reified T : Enum<T>> ResultSet.getEnumSet(name: String): Set<T> {
    val sqlArray: java.sql.Array = this.getArray(name)
    val javaArray: Array<String> = (sqlArray.array as Array<String>)
    val set: Set<T> = javaArray.map { java.lang.Enum.valueOf(T::class.java, it) }.toSet()
    sqlArray.free()
    return set
}

inline fun <reified T> ResultSet.getOptObject(column: String): T? {
    val value = this.getObject(column, T::class.java)
    return if (this.wasNull()) null else value as T
}

fun ResultSet.getOptKey(column: String): EddsaPublicKey? {
    val bytes = this.getBytes(column)
    return if (this.wasNull()) null else EddsaPublicKey(bytes)
}

fun ResultSet.getOptSig(column: String): EddsaSignature? {
    val bytes = this.getBytes(column)
    return if (this.wasNull()) null else EddsaSignature(bytes)
}

fun ResultSet.getOptLong(name: String): Long? {
    val nb = getLong(name)
    if (wasNull()) return null
    return nb
}

fun ResultSet.getAmount(name: String, currency: String): TalerAmount {
    return TalerAmount(
        getLong("${name}_val"),
        getInt("${name}_frac"),
        currency
    )
}

fun ResultSet.getOptAmount(name: String, currency: String): TalerAmount? {
    val amount = getAmount(name, currency)
    if (wasNull()) return null
    return amount
}

fun ResultSet.getDecimal(name: String): DecimalNumber {
    return DecimalNumber(
        getLong("${name}_val"),
        getInt("${name}_frac")
    )
}

fun ResultSet.getOptDecimal(name: String): DecimalNumber? {
    val amount = getDecimal(name)
    if (wasNull()) return null
    return amount
}

fun ResultSet.getTalerTimestamp(name: String): TalerTimestamp{
    return TalerTimestamp(getLong(name).asInstant())
}

fun ResultSet.getBankPayto(payto: String, name: String?, ctx: BankPaytoCtx): String {
    return Payto.parse(getString(payto)).bank(
        name?.let { getString(it) }
    , ctx)
}
fun ResultSet.getOptBankPayto(payto: String, name: String?, ctx: BankPaytoCtx): String? {
    val payto = getString(payto)
    if (payto == null) return null
    return Payto.parse(payto).bank(
        name?.let { getString(it) }
    , ctx)
}

fun ResultSet.getOptIbanPayto(payto: String): IbanPayto? {
    val raw = getString(payto)
    if (raw == null) return null
    return Payto.parse(raw).expectIban()
}

fun ResultSet.getIbanPayto(payto: String): IbanPayto {
    return Payto.parse(getString(payto)).expectIban()
}