/*
 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 {
  Amounts,
  HttpStatusCode,
  TalerError,
  TalerErrorCode,
  TranslatedString
} from "@gnu-taler/taler-util";
import {
  Attention,
  Loading,
  LocalNotificationBanner,
  ShowInputErrorLabel,
  useLocalNotification,
  useTranslationContext
} from "@gnu-taler/web-util/browser";
import { format } from "date-fns";
import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { mutate } from "swr";
import { ErrorLoadingWithDebug } from "../../components/ErrorLoadingWithDebug.js";
import { useBankCoreApiContext } from "../../context/config.js";
import { useBackendState } from "../../hooks/backend.js";
import {
  useCashoutDetails, useConversionInfo
} from "../../hooks/circuit.js";
import {
  undefinedIfEmpty
} from "../../utils.js";
import { RenderAmount } from "../PaytoWireTransferForm.js";
import { assertUnreachable } from "../WithdrawalOperationPage.js";

interface Props {
  id: string;
  onCancel: () => void;
}
export function ShowCashoutDetails({
  id,
  onCancel,
}: Props): VNode {
  const { i18n } = useTranslationContext();
  const { state } = useBackendState();
  const creds = state.status !== "loggedIn" ? undefined : state
  const { api } = useBankCoreApiContext()
  const cid = Number.parseInt(id, 10)

  const result = useCashoutDetails(Number.isNaN(cid) ? undefined : cid);
  const [code, setCode] = useState<string | undefined>(undefined);
  const [notification, notify, handleError] = useLocalNotification()
  const info = useConversionInfo();

  if (Number.isNaN(cid)) {
    //TODO: better error message
    return <div>cashout id should be a number</div>
  }
  if (!result) {
    return <Loading />
  }
  if (result instanceof TalerError) {
    return <ErrorLoadingWithDebug error={result} />
  }
  if (result.type === "fail") {
    switch (result.case) {
      case HttpStatusCode.NotFound: return <Attention type="warning" title={i18n.str`This cashout not found. Maybe already aborted.`}>
      </Attention>
      case HttpStatusCode.NotImplemented: return <Attention type="warning" title={i18n.str`Cashouts are not supported`}>
      </Attention>
      default: assertUnreachable(result)
    }
  }
  if (!info) {
    return <Loading />
  }

  if (info instanceof TalerError) {
    return <ErrorLoadingWithDebug error={info} />
  }

  const errors = undefinedIfEmpty({
    code: !code ? i18n.str`required` : undefined,
  });
  const isPending = String(result.body.status).toUpperCase() === "PENDING";
  const { fiat_currency_specification, regional_currency_specification } = info.body
  // won't implement in retry in old API 3:0:3 since request_uid is missing
  async function doAbortCashout() {
    if (!creds) return;
    await handleError(async () => {
      const resp = await api.abortCashoutById(creds, cid);
      if (resp.type === "ok") {
        onCancel();
      } else {
        switch (resp.case) {
          case HttpStatusCode.NotFound: return notify({
            type: "error",
            title: i18n.str`Cashout not found. It may be also mean that it was already aborted.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          })
          case HttpStatusCode.Conflict: return notify({
            type: "error",
            title: i18n.str`Cashout was already confimed.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          })
          case HttpStatusCode.NotImplemented: return notify({
            type: "error",
            title: i18n.str`Cashout operation is not supported.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          })
          default: {
            assertUnreachable(resp)
          }
        }
      }
    })
  }
  async function doConfirmCashout() {
    if (!creds || !code) return;
    await handleError(async () => {
      const resp = await api.confirmCashoutById(creds, cid, {
        tan: code,
      });
      if (resp.type === "ok") {
        mutate(() => true)//clean cashout state
      } else {
        switch (resp.case) {
          case HttpStatusCode.NotFound: return notify({
            type: "error",
            title: i18n.str`Cashout not found. It may be also mean that it was already aborted.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          })
          case TalerErrorCode.BANK_UNALLOWED_DEBIT: return notify({
            type: "error",
            title: i18n.str`The account does not have sufficient funds`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          });
          case TalerErrorCode.BANK_BAD_CONVERSION: return notify({
            type: "error",
            title: i18n.str`The exchange rate was incorrectly applied`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          });
          case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT: return notify({
            type: "error",
            title: i18n.str`The cashout operation is already aborted.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          });
          case TalerErrorCode.BANK_CONFIRM_INCOMPLETE: return notify({
            type: "error",
            title: i18n.str`Missing destination account.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          })
          case HttpStatusCode.TooManyRequests: return notify({
            type: "error",
            title: i18n.str`Too many failed attempts.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          })
          case HttpStatusCode.NotImplemented: return notify({
            type: "error",
            title: i18n.str`Cashout operation is not supported.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          })
          case TalerErrorCode.BANK_TAN_CHALLENGE_FAILED: return notify({
            type: "error",
            title: i18n.str`The code for this cashout is invalid.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
          })
          default: assertUnreachable(resp)
        }
      }
    })
  }

  return (
    <div>
      <LocalNotificationBanner notification={notification} />
      <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-10 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">

        <section class="rounded-sm px-4">
          <h2 id="summary-heading" class="font-medium text-lg"><i18n.Translate>Cashout detail</i18n.Translate></h2>
          <dl class="mt-8 space-y-4">
            <div class="justify-between items-center flex">
              <dt class="text-sm text-gray-600"><i18n.Translate>Subject</i18n.Translate></dt>
              <dd class="text-sm ">{result.body.subject}</dd>
            </div>


            <div class="flex items-center justify-between border-t-2 afu pt-4">
              <dt class="flex items-center text-sm text-gray-600">
                <span><i18n.Translate>Status</i18n.Translate></span>
              </dt>
              <dd data-status={result.body.status} class="text-sm uppercase data-[status=pending]:text-yellow-600 data-[status=aborted]:text-red-600 data-[status=confirmed]:text-green-600" >
                {result.body.status}
              </dd>
            </div>
          </dl>
        </section>
        <div class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2">
          <div class="px-4 py-6 sm:p-8">
            <div class="grid max-w-2xl grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
              <div class="sm:col-span-5">
                <dl class="space-y-4">

                  {result.body.creation_time.t_s !== "never" ?
                    <div class="justify-between items-center flex ">
                      <dt class=" text-gray-600"><i18n.Translate>Created</i18n.Translate></dt>
                      <dd class="text-sm ">
                        {format(result.body.creation_time.t_s * 1000, "dd/MM/yyyy HH:mm:ss")}
                      </dd>
                    </div>
                    : undefined}

                  <div class="flex justify-between items-center border-t-2 afu pt-4">
                    <dt class="text-gray-600"><i18n.Translate>Debited</i18n.Translate></dt>
                    <dd class=" font-medium">
                      <RenderAmount value={Amounts.parseOrThrow(result.body.amount_debit)} negative withColor spec={regional_currency_specification} />
                    </dd>
                  </div>

                  <div class="flex items-center justify-between border-t-2 afu pt-4">
                    <dt class="flex items-center text-gray-600">
                      <span><i18n.Translate>Credited</i18n.Translate></span>

                    </dt>
                    <dd class="text-sm ">
                      <RenderAmount value={Amounts.parseOrThrow(result.body.amount_credit)} withColor spec={fiat_currency_specification} />
                    </dd>
                  </div>

                  {result.body.confirmation_time && result.body.confirmation_time.t_s !== "never" ?
                    <div class="flex justify-between items-center border-t-2 afu pt-4">
                      <dt class="  font-medium"><i18n.Translate>Confirmed</i18n.Translate></dt>
                      <dd class="  font-medium">
                        {format(result.body.confirmation_time.t_s * 1000, "dd/MM/yyyy HH:mm:ss")}
                      </dd>
                    </div>
                    : undefined}
                </dl>
              </div>
            </div>
          </div>

        </div>

        {!isPending ? undefined :
          <Fragment>

            <div />
            <form
              class="bg-white shadow-sm ring-1 ring-gray-900/5"
              autoCapitalize="none"
              autoCorrect="off"
              onSubmit={e => {
                e.preventDefault()
              }}
            >
              <div class="px-4 py-6 sm:p-8">
                <label for="withdraw-amount">
                  Enter the confirmation code
                </label>
                <div class="mt-2">
                  <div class="relative rounded-md shadow-sm">
                    <input
                      type="text"
                      // class="block w-full rounded-md border-0 py-1.5 pl-16 text-gray-900 ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                      aria-describedby="answer"
                      autoFocus
                      class="block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6"
                      value={code ?? ""}
                      required

                      name="answer"
                      id="answer"
                      autocomplete="off"
                      onChange={(e): void => {
                        setCode(e.currentTarget.value)
                      }}
                    />
                  </div>
                  <ShowInputErrorLabel message={errors?.code} isDirty={code !== undefined} />
                </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="inline-flex items-center rounded-md bg-red-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-red-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-red-500"
                  onClick={doAbortCashout}
                >
                  <i18n.Translate>Abort</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"
                  disabled={!!errors}
                  onClick={(e) => {
                    doConfirmCashout()
                  }}
                >
                  <i18n.Translate>Confirm</i18n.Translate>
                </button>
              </div>

            </form>
          </Fragment>}
      </div>

      <br />
      <div style={{ display: "flex", justifyContent: "space-between" }}>
        <button type="button" class="text-sm font-semibold leading-6 text-gray-900"
          onClick={onCancel}
        >
          <i18n.Translate>Cancel</i18n.Translate></button>
      </div>
    </div>
  );
}
