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

 GNU Taler is free software; you can redistribute it and/or modify it under the
 terms of the GNU 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 General Public License along with
 GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */

/**
 *
 * @author Sebastian Javier Marchano (sebasjm)
 */

import {
  AmountString,
  Amounts,
  ExchangeEntryStatus,
  ExchangeListItem,
  ExchangeTosStatus,
  ScopeType,
  TransactionIdStr,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import * as tests from "@gnu-taler/web-util/testing";
import { expect } from "chai";
import { createWalletApiMock } from "../../test-utils.js";
import { useComponentStateFromURI } from "./state.js";

const exchanges: ExchangeListItem[] = [
  {
    currency: "ARS",
    exchangeBaseUrl: "http://exchange.demo.taler.net",
    paytoUris: [],
    tosStatus: ExchangeTosStatus.Accepted,
    exchangeStatus: ExchangeEntryStatus.Used,
    permanent: true,
    auditors: [
      {
        auditor_pub: "pubpubpubpubpub",
        auditor_url: "https://audotor.taler.net",
        denomination_keys: [],
      },
    ],
    denomFees: {
      deposit: [],
      refresh: [],
      refund: [],
      withdraw: [],
    },
    globalFees: [],
    transferFees: {},
    wireInfo: {
      accounts: [],
      feesForType: {},
    },
  } as Partial<ExchangeListItem> as ExchangeListItem,
];

const nullFunction = async (): Promise<void> => {
  null;
};

describe("Withdraw CTA states", () => {
  it("should tell the user that the URI is missing", async () => {
    const { handler, TestingContext } = createWalletApiMock();

    const props = {
      talerWithdrawUri: undefined,
      cancel: nullFunction,
      onSuccess: nullFunction,
    };

    const hookBehavior = await tests.hookBehaveLikeThis(
      useComponentStateFromURI,
      props,
      [
        ({ status }) => {
          expect(status).equals("loading");
        },
        ({ status, error }) => {
          if (status != "error") expect.fail();
          if (!error) expect.fail();
          // if (!error.hasError) expect.fail();
          // if (error.operational) expect.fail();
          expect(error.description).eq("ERROR_NO-URI-FOR-WITHDRAWAL");
        },
      ],
      TestingContext,
    );

    expect(hookBehavior).deep.equal({ result: "ok" });
    expect(handler.getCallingQueueState()).eq("empty");
  });

  it.skip("should tell the user that there is not known exchange", async () => {
    const { handler, TestingContext } = createWalletApiMock();
    const props = {
      talerWithdrawUri: "taler-withdraw://",
      cancel: nullFunction,
      onSuccess: nullFunction,
    };

    handler.addWalletCallResponse(
      WalletApiOperation.PrepareBankIntegratedWithdrawal,
      undefined,
      {
        transactionId: "123" as TransactionIdStr,
        info: {
          status: "pending",
          operationId: "123",
          currency: "ARS",
          amount: "EUR:2" as AmountString,
          possibleExchanges: [],
          editableAmount: false,
          editableExchange: false,
          maxAmount: "ARS:1",
          wireFee: "ARS:0",
        },
      },
    );

    const hookBehavior = await tests.hookBehaveLikeThis(
      useComponentStateFromURI,
      props,
      [
        ({ status }) => {
          expect(status).equals("loading");
        },
        ({ status, error }) => {
          expect(status).equals("no-exchange-found");
          expect(error).undefined;
        },
      ],
      TestingContext,
    );

    expect(hookBehavior).deep.equal({ result: "ok" });
    expect(handler.getCallingQueueState()).eq("empty");
  });

  it.skip("should be able to withdraw if tos are ok", async () => {
    const { handler, TestingContext } = createWalletApiMock();
    const props = {
      talerWithdrawUri: "taler-withdraw://",
      cancel: nullFunction,
      onSuccess: nullFunction,
    };

    handler.addWalletCallResponse(
      WalletApiOperation.PrepareBankIntegratedWithdrawal,
      undefined,
      {
        transactionId: "123" as TransactionIdStr,
        info: {
          status: "pending",
          operationId: "123",
          currency: "ARS",
          amount: "ARS:2" as AmountString,
          possibleExchanges: exchanges,
          defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl,
          editableAmount: false,
          editableExchange: false,
          maxAmount: "ARS:1",
          wireFee: "ARS:0",
        },
      },
    );
    handler.addWalletCallResponse(
      WalletApiOperation.GetWithdrawalDetailsForAmount,
      undefined,
      {
        amountRaw: "ARS:2" as AmountString,
        amountEffective: "ARS:2" as AmountString,
        paytoUris: ["payto://"],
        tosAccepted: true,
        scopeInfo: {
          currency: "ARS",
          type: ScopeType.Exchange,
          url: "http://asd",
        },
        exchangeBaseUrl: "http://asd",
        withdrawalAccountsList: [],
        ageRestrictionOptions: [],
        numCoins: 42,
      },
    );

    const hookBehavior = await tests.hookBehaveLikeThis(
      useComponentStateFromURI,
      props,
      [
        ({ status }) => {
          expect(status).equals("loading");
        },
        ({ status, error }) => {
          expect(status).equals("loading");
          expect(error).undefined;
        },
        (state) => {
          expect(state.status).equals("success");
          if (state.status !== "success") return;

          expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
          expect(state.toBeSent).deep.equal(Amounts.parseOrThrow("ARS:2"));
          expect(state.amount.value).deep.equal(Amounts.parseOrThrow("ARS:2"));

          expect(state.doWithdrawal.onClick).not.undefined;
        },
      ],
      TestingContext,
    );

    expect(hookBehavior).deep.equal({ result: "ok" });
    expect(handler.getCallingQueueState()).eq("empty");
  });

  it.skip("should accept the tos before withdraw", async () => {
    const { handler, TestingContext } = createWalletApiMock();
    const props = {
      talerWithdrawUri: "taler-withdraw://",
      cancel: nullFunction,
      onSuccess: nullFunction,
    };

    const exchangeWithNewTos = exchanges.map((e) => ({
      ...e,
      tosStatus: ExchangeTosStatus.Proposed,
    }));

    handler.addWalletCallResponse(
      WalletApiOperation.GetWithdrawalDetailsForUri,
      undefined,
      {
        status: "pending",
        operationId: "123",
        currency: "ARS",
        amount: "ARS:2" as AmountString,
        possibleExchanges: exchangeWithNewTos,
        defaultExchangeBaseUrl: exchangeWithNewTos[0].exchangeBaseUrl,
        editableAmount: false,
        editableExchange: false,
        maxAmount: "ARS:1",
        wireFee: "ARS:0",
      },
    );
    handler.addWalletCallResponse(
      WalletApiOperation.GetWithdrawalDetailsForAmount,
      undefined,
      {
        amountRaw: "ARS:2" as AmountString,
        amountEffective: "ARS:2" as AmountString,
        paytoUris: ["payto://"],
        scopeInfo: {
          currency: "ARS",
          type: ScopeType.Exchange,
          url: "http://asd",
        },
        exchangeBaseUrl: "http://asd",
        tosAccepted: false,
        withdrawalAccountsList: [],
        ageRestrictionOptions: [],
        numCoins: 42,
      },
    );

    handler.addWalletCallResponse(
      WalletApiOperation.GetWithdrawalDetailsForUri,
      undefined,
      {
        status: "pending",
        operationId: "123",
        currency: "ARS",
        amount: "ARS:2" as AmountString,
        possibleExchanges: exchanges,
        defaultExchangeBaseUrl: exchanges[0].exchangeBaseUrl,
        editableAmount: false,
        editableExchange: false,
        maxAmount: "ARS:1",
        wireFee: "ARS:0",
      },
    );

    const hookBehavior = await tests.hookBehaveLikeThis(
      useComponentStateFromURI,
      props,
      [
        ({ status }) => {
          expect(status).equals("loading");
        },
        ({ status, error }) => {
          expect(status).equals("loading");
          expect(error).undefined;
        },
        (state) => {
          expect(state.status).equals("success");
          if (state.status !== "success") return;

          expect(state.toBeReceived).deep.equal(Amounts.parseOrThrow("ARS:2"));
          expect(state.toBeSent).deep.equal(Amounts.parseOrThrow("ARS:2"));
          expect(state.amount.value).deep.equal(Amounts.parseOrThrow("ARS:2"));

          expect(state.doWithdrawal.onClick).not.undefined;
        },
      ],
      TestingContext,
    );

    expect(hookBehavior).deep.equal({ result: "ok" });
    expect(handler.getCallingQueueState()).eq("empty");
  });
});
