#!/usr/bin/env python3

from taler.util.amount import Amount

from tests import check_single_balance


def test_withdrawal(exchange, bank, wallet):
    bank_user = bank.register_random_user()

    # assert that we start with no transactions
    result = wallet.cmd("getTransactions")
    assert not result["transactions"]

    # test withdrawal
    amount_raw = Amount.parse("TESTKUDOS:5")
    wallet.testing_withdraw(amount_raw.stringify(), exchange.url, bank.url)

    # check that balance is correct
    result = wallet.cmd("getBalances")
    amount_effective = Amount("TESTKUDOS", 4, 84000000)
    check_single_balance(result["balances"], amount_effective)

    # assert that withdrawal shows up properly in transactions
    result = wallet.cmd("getTransactions")
    assert len(result["transactions"]) == 1
    transaction = result["transactions"][0]
    assert transaction["type"] == "withdrawal"
    assert Amount.parse(transaction["amountEffective"]) == amount_effective
    assert Amount.parse(transaction["amountRaw"]) == amount_raw
    assert transaction["exchangeBaseUrl"] == exchange.url
    assert not transaction["pending"]
    withdrawal_details = transaction["withdrawalDetails"]
    assert withdrawal_details["type"] == "manual-transfer"
    payto_list = ["payto://x-taler-bank/localhost/Exchange"]
    assert withdrawal_details["exchangePaytoUris"] == payto_list

    # get a withdrawal URI
    bank_uri_resp = bank.generate_withdraw_uri(bank_user, "TESTKUDOS:5")
    uri = bank_uri_resp.taler_withdraw_uri
    assert uri.startswith("taler+http://withdraw")

    # get withdrawal details from URI
    result = wallet.cmd("getWithdrawalDetailsForUri", {"talerWithdrawUri": uri})
    assert Amount.parse(result["amount"]) == amount_raw
    assert result["defaultExchangeBaseUrl"] == exchange.url
    assert len(result["possibleExchanges"]) == 1
    assert result["possibleExchanges"][0]["exchangeBaseUrl"] == exchange.url
    assert result["possibleExchanges"][0]["currency"] == "TESTKUDOS"
    assert result["possibleExchanges"][0]["paytoUris"] == payto_list

    # check withdrawal details for amount
    request = {"exchangeBaseUrl": exchange.url, "amount": amount_raw.stringify()}
    result = wallet.cmd("getWithdrawalDetailsForAmount", request)
    assert Amount.parse(result["amountRaw"]) == amount_raw
    assert Amount.parse(result["amountEffective"]) == amount_effective
    assert result["paytoUris"] == payto_list
    assert not result["tosAccepted"]

    # get ToS
    result = wallet.cmd("getExchangeTos", {"exchangeBaseUrl": exchange.url})
    assert result["currentEtag"] == exchange.terms_etag
    assert result["tos"] == exchange.tos

    # accept ToS
    request = {"exchangeBaseUrl": exchange.url, "etag": exchange.terms_etag}
    wallet.cmd("setExchangeTosAccepted", request)

    # check that ToS are now shown as accepted
    request = {"exchangeBaseUrl": exchange.url, "amount": amount_raw.stringify()}
    result = wallet.cmd("getWithdrawalDetailsForAmount", request)
    assert result["tosAccepted"]

    # accept withdrawal
    request = {"exchangeBaseUrl": exchange.url, "talerWithdrawUri": uri}
    result = wallet.cmd("acceptBankIntegratedWithdrawal", request)
    assert result["confirmTransferUrl"].startswith(bank.url + "confirm-withdrawal/")
    confirm_url = result["confirmTransferUrl"]

    # Let the wallet do its work.  At this point, the bank-integrated
    # withdrawal won't have succeeded yet, as it's not confirmed at the bank side.
    wallet.run_pending()

    # check that balance is correct
    result = wallet.cmd("getBalances")
    check_single_balance(result["balances"], amount_effective, amount_effective)

    # assert that 2nd withdrawal shows up properly in transactions
    result = wallet.cmd("getTransactions")
    assert len(result["transactions"]) == 2
    transaction = result["transactions"][0]
    assert transaction["type"] == "withdrawal"
    assert Amount.parse(transaction["amountEffective"]) == amount_effective
    assert Amount.parse(transaction["amountRaw"]) == amount_raw
    assert transaction["exchangeBaseUrl"] == exchange.url
    assert transaction["pending"]
    withdrawal_details = transaction["withdrawalDetails"]
    assert withdrawal_details["type"] == "taler-bank-integration-api"
    assert not withdrawal_details["confirmed"]
    assert withdrawal_details["bankConfirmationUrl"] == confirm_url

    # new withdrawal is newer than old one
    timestamp0 = result["transactions"][0]["timestamp"]["t_ms"]
    timestamp1 = result["transactions"][1]["timestamp"]["t_ms"]
    assert timestamp0 > timestamp1

    # now we actually confirm the withdrawal
    bank.confirm_withdrawal(bank_user, bank_uri_resp.withdrawal_id)
    # It might take some time until the exchange knows about the reserve,
    # so we'll try until it works.
    wallet.run_until_done()

    # check that balance is correct
    result = wallet.cmd("getBalances")
    check_single_balance(
        result["balances"], Amount.parse("TESTKUDOS:9.68"), Amount.parse("TESTKUDOS:0"),
    )

    # check that transaction is no longer pending, but confirmed
    result = wallet.cmd("getTransactions")
    assert len(result["transactions"]) == 2
    transaction = result["transactions"][1]  # TODO this transaction should be at the top now
    assert transaction["type"] == "withdrawal"
    assert not transaction["pending"]
    assert transaction["withdrawalDetails"]["confirmed"]

    # one more manual withdrawal
    request = {"exchangeBaseUrl": exchange.url, "amount": amount_raw.stringify()}
    result = wallet.cmd("acceptManualWithdrawal", request)
    assert len(result["exchangePaytoUris"]) == 1
    result["exchangePaytoUris"][0].startswith(payto_list[0])

    # check that balance is correct
    result = wallet.cmd("getBalances")
    check_single_balance(
        result["balances"], amount_effective + amount_effective, amount_effective
    )

    # assert that 3nd withdrawal shows up properly in transactions
    result = wallet.cmd("getTransactions")
    assert len(result["transactions"]) == 3
    transaction = result["transactions"][0]
    assert transaction["type"] == "withdrawal"
    assert Amount.parse(transaction["amountEffective"]) == amount_effective
    assert Amount.parse(transaction["amountRaw"]) == amount_raw
    assert transaction["exchangeBaseUrl"] == exchange.url
    assert transaction["pending"]
    withdrawal_details = transaction["withdrawalDetails"]
    assert withdrawal_details["type"] == "manual-transfer"
    assert len(withdrawal_details["exchangePaytoUris"]) == 1
    assert withdrawal_details["exchangePaytoUris"][0].startswith(payto_list[0])

    # last withdrawal is newest
    timestamp3 = transaction["timestamp"]["t_ms"]
    assert timestamp3 > timestamp0
    assert timestamp3 > timestamp1