/*
 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/>
 */

import {
  AbsoluteTime,
  AmountJson,
  HttpStatusCode,
  Logger,
  PaytoUri,
  PaytoUriIBAN,
  PaytoUriTalerBank,
  TalerErrorCode,
  TranslatedString,
  WithdrawUriResult
} from "@gnu-taler/taler-util";
import {
  Attention,
  LocalNotificationBanner,
  notifyInfo,
  useLocalNotification,
  useTranslationContext
} from "@gnu-taler/web-util/browser";
import { ComponentChildren, Fragment, VNode, h } from "preact";
import { mutate } from "swr";
import { useBankCoreApiContext } from "../context/config.js";
import { useBackendState } from "../hooks/backend.js";
import { useBankState } from "../hooks/bank-state.js";
import { usePreferences } from "../hooks/preferences.js";
import { LoginForm } from "./LoginForm.js";
import { RenderAmount } from "./PaytoWireTransferForm.js";
import { assertUnreachable } from "./WithdrawalOperationPage.js";

const logger = new Logger("WithdrawalConfirmationQuestion");

interface Props {
  onAborted: () => void;
  withdrawUri: WithdrawUriResult;
  details: {
    account: PaytoUri,
    reserve: string,
    username: string,
    amount: AmountJson,
  },
  onAuthorizationRequired: () => void,
}
/**
 * Additional authentication required to complete the operation.
 * Not providing a back button, only abort.
 */
export function WithdrawalConfirmationQuestion({
  onAborted,
  details,
  onAuthorizationRequired,
  withdrawUri,
}: Props): VNode {
  const { i18n } = useTranslationContext();
  const [settings] = usePreferences()
  const { state: credentials } = useBackendState();
  const creds = credentials.status !== "loggedIn" ? undefined : credentials
  const [, updateBankState] = useBankState()

  const [notification, notify, handleError] = useLocalNotification()

  const { config, api } = useBankCoreApiContext()

  async function doTransfer() {
    await handleError(async () => {
      if (!creds) return;
      const resp = await api.confirmWithdrawalById(creds, withdrawUri.withdrawalOperationId);
      if (resp.type === "ok") {
        mutate(() => true)// clean any info that we have
        if (!settings.showWithdrawalSuccess) {
          notifyInfo(i18n.str`Wire transfer completed!`)
        }
      } else {
        switch (resp.case) {
          case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT: return notify({
            type: "error",
            title: i18n.str`The withdrawal has been aborted previously and can't be confirmed`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          });
          // FIXME: remove exchange word
          case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return notify({
            type: "error",
            title: i18n.str`The withdraw operation cannot be confirmed because no exchange and reserve public key selection happened before`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          });
          case HttpStatusCode.BadRequest: return notify({
            type: "error",
            title: i18n.str`The operation id is invalid.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          })
          case HttpStatusCode.NotFound: return notify({
            type: "error",
            title: i18n.str`The operation was not found.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          })
          case TalerErrorCode.BANK_UNALLOWED_DEBIT: return notify({
            type: "error",
            title: i18n.str`Your balance is not enough for the operation.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          })
          case HttpStatusCode.Accepted: {
            updateBankState("currentChallenge", {
              operation: "confirm-withdrawal",
              id: String(resp.body.challenge_id),
              sent: AbsoluteTime.never(),
              request: withdrawUri.withdrawalOperationId,
            })
            return onAuthorizationRequired()
          }
          default: assertUnreachable(resp)
        }
      }
    })
  }

  async function doCancel() {
    await handleError(async () => {
      if (!creds) return;
      const resp = await api.abortWithdrawalById(creds, withdrawUri.withdrawalOperationId);
      if (resp.type === "ok") {
        onAborted();
      } else {
        switch (resp.case) {
          case HttpStatusCode.Conflict: return notify({
            type: "error",
            title: i18n.str`The reserve operation has been confirmed previously and can't be aborted`
          });
          case HttpStatusCode.BadRequest: return notify({
            type: "error",
            title: i18n.str`The operation id is invalid.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          })
          case HttpStatusCode.NotFound: return notify({
            type: "error",
            title: i18n.str`The operation was not found.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          })
          default: {
            assertUnreachable(resp)
          }
        }
      }
    })
  }

  return (
    <Fragment>
      <LocalNotificationBanner notification={notification} />

      <div class="bg-white shadow sm:rounded-lg">
        <div class="px-4 py-5 sm:p-6">
          <h3 class="text-base font-semibold text-gray-900">
            <i18n.Translate>Confirm the withdrawal operation</i18n.Translate>
          </h3>
          <div class="mt-3 text-sm leading-6">

            <ShouldBeSameUser username={details.username}>
              <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-2 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
                <form
                  class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2"
                  autoCapitalize="none"
                  autoCorrect="off"
                  onSubmit={e => {
                    e.preventDefault()
                  }}
                >
                  <div class="px-4 mt-4">
                    <div class="w-full">
                      <div class="px-4 sm:px-0 text-sm">
                        <p><i18n.Translate>Wire transfer details</i18n.Translate></p>
                      </div>
                      <div class="mt-6 border-t border-gray-100">
                        <dl class="divide-y divide-gray-100">
                          {((): VNode => {
                            switch (details.account.targetType) {
                              case "iban": {
                                const p = details.account as PaytoUriIBAN
                                const name = p.params["receiver-name"]
                                return <Fragment>
                                  <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
                                    <dt class="text-sm font-medium leading-6 text-gray-900">Exchange account</dt>
                                    <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.iban}</dd>
                                  </div>
                                  {name &&
                                    <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
                                      <dt class="text-sm font-medium leading-6 text-gray-900">Exchange name</dt>
                                      <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.params["receiver-name"]}</dd>
                                    </div>
                                  }
                                </Fragment>
                              }
                              case "x-taler-bank": {
                                const p = details.account as PaytoUriTalerBank
                                const name = p.params["receiver-name"]
                                return <Fragment>
                                  <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
                                    <dt class="text-sm font-medium leading-6 text-gray-900">Exchange account</dt>
                                    <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.account}</dd>
                                  </div>
                                  {name &&
                                    <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
                                      <dt class="text-sm font-medium leading-6 text-gray-900">Exchange name</dt>
                                      <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{p.params["receiver-name"]}</dd>
                                    </div>
                                  }
                                </Fragment>
                              }
                              default:
                                return <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
                                  <dt class="text-sm font-medium leading-6 text-gray-900">Exchange account</dt>
                                  <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">{details.account.targetPath}</dd>
                                </div>

                            }
                          })()}
                          <div class="px-4 py-2 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
                            <dt class="text-sm font-medium leading-6 text-gray-900">Amount</dt>
                            <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
                              <RenderAmount value={details.amount} spec={config.currency_specification} />
                            </dd>
                          </div>
                        </dl>
                      </div>
                    </div>

                  </div>

                  <div class="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
                    <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
                      onClick={doCancel}
                    >
                      <i18n.Translate>Cancel</i18n.Translate></button>
                    <button type="submit"
                      class="disabled:opacity-50 disabled:cursor-default cursor-pointer rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
                      onClick={(e) => {
                        e.preventDefault()
                        doTransfer()
                      }}
                    >
                      <i18n.Translate>Transfer</i18n.Translate>
                    </button>
                  </div>

                </form>
              </div>
            </ShouldBeSameUser>
          </div>
        </div>
      </div>

    </Fragment >
  );
}

export function ShouldBeSameUser({ username, children }: { username: string, children: ComponentChildren }): VNode {
  const { state: credentials } = useBackendState();
  const { i18n } = useTranslationContext()
  if (credentials.status === "loggedOut") {
    return <Fragment>
      <Attention type="info" title={i18n.str`Authentication required`} />
      <LoginForm currentUser={username} fixedUser />
    </Fragment>
  }
  if (credentials.username !== username) {
    return <Fragment>
      <Attention type="warning" title={i18n.str`This operation was created with other username`} />
      <LoginForm currentUser={username} fixedUser />
    </Fragment>
  }
  return <Fragment>
    {children}
  </Fragment>
}