/*
 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,
  codecForBoolean,
  codecForConstString,
  codecForEither,
  codecForList,
  codecForString,
  codecOptional,
} from "./codec.js";
import { PaytoString, codecForPaytoString } from "./payto.js";
import {
  AmountString,
  CurrencySpecification,
  codecForCurrencyName,
  codecForCurrencySpecificiation,
  codecForLibtoolVersion,
  codecForURLString,
} from "./types-taler-common.js";

export type WithdrawalOperationStatusFlag =
  | "pending"
  | "selected"
  | "aborted"
  | "confirmed";

export interface BankVersion {
  // 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;

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

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

  // Name of the API.
  name: "taler-bank-integration";
}

export interface BankWithdrawalOperationStatus {
  // Current status of the operation
  // pending: the operation is pending parameters selection (exchange and reserve public key)
  // selected: the operations has been selected and is pending confirmation
  // aborted: the operation has been aborted
  // confirmed: the transfer has been confirmed and registered by the bank
  status: WithdrawalOperationStatusFlag;

  // Currency used for the withdrawal.
  // MUST be present when amount is absent.
  // @since v2, may become mandatory in the future.
  currency?: string;

  // Amount that will be withdrawn with this operation
  // (raw amount without fee considerations).  Only
  // given once the amount is fixed and cannot be changed.
  // Optional since **vC2EC**.
  amount?: AmountString;

  // Suggestion for the amount to be withdrawn with this
  // operation.  Given if a suggestion was made but the
  // user may still change the amount.
  // Optional since **vC2EC**.
  suggested_amount?: AmountString;

  // Minimum amount that the wallet can choose to withdraw.
  // Only applicable when the amount is not fixed.
  // @since **v4**.
  min_amount?: AmountString;

  // Maximum amount that the wallet can choose to withdraw.
  // Only applicable when the amount is not fixed.
  // @since **v4**.
  max_amount?: AmountString;

  // The non-Taler card fees the customer will have
  // to pay to the bank / payment service provider
  // they are using to make the withdrawal.
  // @since **vC2EC**
  card_fees?: AmountString;

  // Bank account of the customer that is debiting, as an
  // RFC 8905 payto URI.
  sender_wire?: PaytoString;

  // Base URL of the suggested exchange.  The bank may have
  // neither a suggestion nor a requirement for the exchange.
  // This value is typically set in the bank's configuration.
  suggested_exchange?: string;

  // Base URL of an exchange that must be used.  Optional,
  // not given *unless* a particular exchange is mandatory.
  // This value is typically set in the bank's configuration.
  // @since **vC2EC**
  required_exchange?: string;

  // URL that the user needs to navigate to in order to
  // complete some final confirmation (e.g. 2FA).
  // Only applicable when status is selected or pending.
  // It may contain the withdrawal operation id.
  confirm_transfer_url?: string;

  // Wire transfer types supported by the bank.
  wire_types: string[];

  // Reserve public key selected by the exchange,
  // only non-null if status is selected or confirmed.
  selected_reserve_pub?: string;

  // Exchange account selected by the wallet;
  // only non-null if status is selected or confirmed.
  // @since **v1**
  selected_exchange_account?: string;

  // If true, tells the wallet not to allow the user to
  // specify an amount to withdraw and to not provide
  // any amount when registering with the withdrawal
  // operation. The amount to withdraw will be set
  // by the final /withdrawals/$WITHDRAWAL_ID/confirm step.
  // @since **v5**
  no_amount_to_wallet?: boolean;
}

export interface BankWithdrawalOperationPostRequest {
  // Reserve public key that should become the wire transfer
  // subject to fund the withdrawal.
  reserve_pub: string;

  // Payto address of the exchange selected for the withdrawal.
  selected_exchange: PaytoString;

  // Selected amount to be transferred. Optional if the
  // backend already knows the amount.
  // @since **v4**
  amount?: AmountString;
}

export interface BankWithdrawalOperationPostResponse {
  // Current status of the operation
  // pending: the operation is pending parameters selection (exchange and reserve public key)
  // selected: the operations has been selected and is pending confirmation
  // aborted: the operation has been aborted
  // confirmed: the transfer has been confirmed and registered by the bank
  status: Omit<"pending", WithdrawalOperationStatusFlag>;

  // URL that the user needs to navigate to in order to
  // complete some final confirmation (e.g. 2FA).
  //
  // Only applicable when status is selected or pending.
  // It may contain withdrawal operation id
  confirm_transfer_url?: string;
}

export const codecForBankVersion = (): Codec<BankVersion> =>
  buildCodecForObject<BankVersion>()
    .property("currency", codecForCurrencyName())
    .property("currency_specification", codecForCurrencySpecificiation())
    .property("name", codecForConstString("taler-bank-integration"))
    .property("version", codecForLibtoolVersion())
    .build("TalerBankIntegrationApi.BankVersion");

export const codecForBankWithdrawalOperationStatus =
  (): Codec<BankWithdrawalOperationStatus> =>
    buildCodecForObject<BankWithdrawalOperationStatus>()
      .property(
        "status",
        codecForEither(
          codecForConstString("pending"),
          codecForConstString("selected"),
          codecForConstString("aborted"),
          codecForConstString("confirmed"),
        ),
      )
      .property("currency", codecOptional(codecForCurrencyName()))
      .property("amount", codecOptional(codecForAmountString()))
      .property("suggested_amount", codecOptional(codecForAmountString()))
      .property("min_amount", codecOptional(codecForAmountString()))
      .property("max_amount", codecOptional(codecForAmountString()))
      .property("card_fees", codecOptional(codecForAmountString()))
      .property("sender_wire", codecOptional(codecForPaytoString()))
      .property("suggested_exchange", codecOptional(codecForURLString()))
      .property("required_exchange", codecOptional(codecForURLString()))
      .property("confirm_transfer_url", codecOptional(codecForURLString()))
      .property("wire_types", codecForList(codecForString()))
      .property("selected_reserve_pub", codecOptional(codecForString()))
      .property("selected_exchange_account", codecOptional(codecForString()))
      .property("no_amount_to_wallet", codecOptional(codecForBoolean()))
      .deprecatedProperty("aborted")
      .deprecatedProperty("selection_done")
      .deprecatedProperty("transfer_done")
      .build("TalerBankIntegrationApi.BankWithdrawalOperationStatus");

export const codecForBankWithdrawalOperationPostResponse =
  (): Codec<BankWithdrawalOperationPostResponse> =>
    buildCodecForObject<BankWithdrawalOperationPostResponse>()
      .property(
        "status",
        codecForEither(
          codecForConstString("selected"),
          codecForConstString("aborted"),
          codecForConstString("confirmed"),
        ),
      )
      .property("confirm_transfer_url", codecOptional(codecForURLString()))
      .deprecatedProperty("transfer_done")
      .build("TalerBankIntegrationApi.BankWithdrawalOperationPostResponse");
