/*
 This file is part of GNU Taler
 (C) 2024 Taler Systems S.A.

 GNU Taler 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.

 GNU Taler 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 General Public License for more details.

 You should have received a copy of the GNU Affero General Public License along with
 GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>

 SPDX-License-Identifier: AGPL-3.0-or-later
 */

import { codecForAmountString } from "./amounts.js";
import {
  Codec,
  buildCodecForObject,
  codecForConstString,
  codecForEither,
  codecForString,
} from "./codec.js";
import {
  AmountString,
  CurrencySpecification,
  DecimalNumber,
  codecForCurrencySpecificiation,
  codecForDecimalNumber,
} from "./types-taler-common.js";

// export interface ConversionInfo {
//   // Exchange rate to buy regional currency from fiat
//   cashin_ratio: DecimalNumber;

//   // Exchange rate to sell regional currency for fiat
//   cashout_ratio: DecimalNumber;

//   // Fee to subtract after applying the cashin ratio.
//   cashin_fee: AmountString;

//   // Fee to subtract after applying the cashout ratio.
//   cashout_fee: AmountString;

//   // Minimum amount authorised for cashin, in fiat before conversion
//   cashin_min_amount: AmountString;

//   // Minimum amount authorised for cashout, in regional before conversion
//   cashout_min_amount: AmountString;

//   // Smallest possible regional amount, converted amount is rounded to this amount
//   cashin_tiny_amount: AmountString;

//   // Smallest possible fiat amount, converted amount is rounded to this amount
//   cashout_tiny_amount: AmountString;

//   // Rounding mode used during cashin conversion
//   cashin_rounding_mode: RoundingMode;

//   // Rounding mode used during cashout conversion
//   cashout_rounding_mode: RoundingMode;
// }

export interface TalerConversionInfoConfig {
  // libtool-style representation of the Bank protocol version, see
  // https://www.gnu.org/software/libtool/manual/html_node/Versioning.html#Versioning
  // The format is "current:revision:age".
  version: string;

  // Name of the API.
  name: "taler-conversion-info";

  // URN of the implementation (needed to interpret 'revision' in version).
  // @since v4, may become mandatory in the future.
  implementation?: string;

  // Currency used by this bank.
  regional_currency: string;

  // How the bank SPA should render this currency.
  regional_currency_specification: CurrencySpecification;

  // External currency used during conversion.
  fiat_currency: string;

  // How the bank SPA should render this currency.
  fiat_currency_specification: CurrencySpecification;

  // Global exchange rate between the regional currency and the fiat
  // currency of the banking system. Use /rate to get the user specific
  // rate.
  // FIXME spec: changed on v2, breaking change insteand of deprecating
  conversion_rate: ConversionRate;
}

export interface CashinConversionResponse {
  // Amount that the user will get deducted from their fiat
  // bank account, according to the 'amount_credit' value.
  amount_debit: AmountString;
  // Amount that the user will receive in their regional
  // bank account, according to 'amount_debit'.
  amount_credit: AmountString;
}

export interface CashoutConversionResponse {
  // Amount that the user will get deducted from their regional
  // bank account, according to the 'amount_credit' value.
  amount_debit: AmountString;
  // Amount that the user will receive in their fiat
  // bank account, according to 'amount_debit'.
  amount_credit: AmountString;
}

export type RoundingMode = "zero" | "up" | "nearest";

export interface ConversionRate {
  // Minimum amount authorised for cashin, in fiat before conversion
  cashin_min_amount: AmountString;

  // Exchange rate to buy regional currency from fiat
  cashin_ratio: DecimalNumber;

  // Fee to subtract after applying the cashin ratio.
  cashin_fee: AmountString;

  // Rounding mode used during cashin conversion
  cashin_rounding_mode: RoundingMode;

  // Smallest possible regional amount, converted amount is rounded to this amount
  cashin_tiny_amount: AmountString;

  // Minimum amount authorised for cashout, in regional before conversion
  cashout_min_amount: AmountString;

  // Exchange rate to sell regional currency for fiat
  cashout_ratio: DecimalNumber;

  // Fee to subtract after applying the cashout ratio.
  cashout_fee: AmountString;

  // Rounding mode used during cashout conversion
  cashout_rounding_mode: RoundingMode;

  // Smallest possible fiat amount, converted amount is rounded to this amount
  cashout_tiny_amount: AmountString;
}

export const codecForCashoutConversionResponse =
  (): Codec<CashoutConversionResponse> =>
    buildCodecForObject<CashoutConversionResponse>()
      .property("amount_credit", codecForAmountString())
      .property("amount_debit", codecForAmountString())
      .build("TalerCorebankApi.CashoutConversionResponse");

export const codecForCashinConversionResponse =
  (): Codec<CashinConversionResponse> =>
    buildCodecForObject<CashinConversionResponse>()
      .property("amount_credit", codecForAmountString())
      .property("amount_debit", codecForAmountString())
      .build("TalerCorebankApi.CashinConversionResponse");

export const codecForConversionRate = (): Codec<ConversionRate> =>
  buildCodecForObject<ConversionRate>()
    .property("cashin_fee", codecForAmountString())
    .property("cashin_min_amount", codecForAmountString())
    .property("cashin_ratio", codecForDecimalNumber())
    .property(
      "cashin_rounding_mode",
      codecForEither(
        codecForConstString("zero"),
        codecForConstString("up"),
        codecForConstString("nearest"),
      ),
    )
    .property("cashin_tiny_amount", codecForAmountString())
    .property("cashout_fee", codecForAmountString())
    .property("cashout_min_amount", codecForAmountString())
    .property("cashout_ratio", codecForDecimalNumber())
    .property(
      "cashout_rounding_mode",
      codecForEither(
        codecForConstString("zero"),
        codecForConstString("up"),
        codecForConstString("nearest"),
      ),
    )
    .property("cashout_tiny_amount", codecForAmountString())
    .build("ConversionBankConfig.ConversionInfo");

export const codecForConversionBankConfig =
  (): Codec<TalerConversionInfoConfig> =>
    buildCodecForObject<TalerConversionInfoConfig>()
      .property("name", codecForConstString("taler-conversion-info"))
      .property("version", codecForString())
      .property("regional_currency", codecForString())
      .property(
        "regional_currency_specification",
        codecForCurrencySpecificiation(),
      )
      .property("fiat_currency", codecForString())
      .property("fiat_currency_specification", codecForCurrencySpecificiation())

      .property("conversion_rate", codecForConversionRate())
      .build("ConversionBankConfig.IntegrationConfig");
