/*
 This file is part of GNU Taler
 (C) 2022-2024 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,
  PaytoType,
  TalerErrorCode,
  TalerUris,
  assertUnreachable,
} from "@gnu-taler/taler-util";
import {
  Attention,
  ButtonBetter,
  LocalNotificationBanner,
  notifyInfo,
  useBankCoreApiContext,
  useChallengeHandler,
  useLocalNotificationBetter,
  useTalerWalletIntegrationAPI,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, VNode, h } from "preact";
import { useEffect } from "preact/hooks";
import { QR } from "../../components/QR.js";
import { usePreferences } from "../../hooks/preferences.js";
import { LoggedIn, useSessionState } from "../../hooks/session.js";
import { RenderAmount } from "../PaytoWireTransferForm.js";
import { SolveMFAChallenges } from "../SolveMFA.js";
import { ShouldBeSameUser } from "../WithdrawalConfirmationQuestion.js";
import { State } from "./index.js";

const TALER_SCREEN_ID = 6;

export function InvalidPaytoView({ payto }: State.InvalidPayto) {
  return <div>Payto from server is not valid &quot;{payto}&quot;</div>;
}
export function InvalidWithdrawalView({ uri }: State.InvalidWithdrawal) {
  return <div>Withdrawal uri from server is not valid &quot;{uri}&quot;</div>;
}
export function InvalidReserveView({ reserve }: State.InvalidReserve) {
  return (
    <div>
      Reserve from server is not valid &quot;
      {reserve}&quot;
    </div>
  );
}

export function NeedConfirmationView({
  onAbort,
  account,
  details,
  operationId,
}: State.NeedConfirmation) {
  const { i18n } = useTranslationContext();
  const [settings] = usePreferences();
  const [notification, safeFunctionHandler] = useLocalNotificationBetter();

  const { state: credentials } = useSessionState();
  const creds = credentials.status !== "loggedIn" ? undefined : credentials;
  const mfa = useChallengeHandler();
  const {
    config,
    lib: { bank },
  } = useBankCoreApiContext();
  const wireFee =
    config.wire_transfer_fees === undefined
      ? Amounts.zeroOfCurrency(config.currency)
      : Amounts.parseOrThrow(config.wire_transfer_fees);

  const abort = safeFunctionHandler(
    (creds: LoggedIn) => bank.abortWithdrawalById(creds, operationId),
    !creds ? undefined : [creds],
  );
  abort.onSuccess = onAbort;
  abort.onFail = (fail) => {
    switch (fail.case) {
      case HttpStatusCode.Conflict:
        return i18n.str`The reserve operation has been confirmed previously and can't be aborted`;
      case HttpStatusCode.BadRequest:
        return i18n.str`The operation ID is invalid.`;
      case HttpStatusCode.NotFound:
        return i18n.str`The operation was not found.`;
    }
  };

  const confirm = safeFunctionHandler(
    (creds: LoggedIn, challengeIds: string[]) =>
      bank.confirmWithdrawalById(creds, {}, operationId, { challengeIds }),
    !creds ? undefined : [creds, []],
  );
  confirm.onSuccess = () => {
    if (!settings.showWithdrawalSuccess) {
      notifyInfo(i18n.str`Wire transfer completed!`);
    }
  };
  confirm.onFail = (fail) => {
    switch (fail.case) {
      case TalerErrorCode.BANK_CONFIRM_ABORT_CONFLICT:
        return i18n.str`The withdrawal has been aborted previously and can't be confirmed`;
      case TalerErrorCode.BANK_CONFIRM_INCOMPLETE:
        return i18n.str`The withdrawal operation can't be confirmed before a wallet accepted the transaction.`;
      case HttpStatusCode.BadRequest:
        return i18n.str`The operation ID is invalid.`;
      case HttpStatusCode.NotFound:
        return i18n.str`The operation was not found.`;
      case TalerErrorCode.BANK_UNALLOWED_DEBIT:
        return i18n.str`Your balance is not sufficient for the operation.`;
      case HttpStatusCode.Accepted: {
        mfa.onChallengeRequired(fail.body);
        return i18n.str`A second factor authentication is required.`;
      }
      case TalerErrorCode.BANK_AMOUNT_DIFFERS:
        return i18n.str`The starting withdrawal amount and the confirmation amount differs.`;
      case TalerErrorCode.BANK_AMOUNT_REQUIRED:
        return i18n.str`The bank requires a bank account which has not been specified yet.`;
    }
  };

  const repeatConfirm = confirm.lambda((ids: string[]) => {
    return [confirm.args![0], ids];
  });
  if (mfa.pendingChallenge) {
    return (
      <SolveMFAChallenges
        currentChallenge={mfa.pendingChallenge}
        description={i18n.str`Confirm withdrawal.`}
        username={details.username}
        onCancel={mfa.doCancelChallenge}
        onCompleted={repeatConfirm}
      />
    );
  }

  return (
    <div class="bg-white shadow sm:rounded-lg">
      <LocalNotificationBanner notification={notification} />
      <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={account}>
            <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">
                  <dl class="">
                    {((): VNode => {
                      switch (details.account.targetType) {
                        case undefined:
                        case PaytoType.TalerReserveHttp:
                        case PaytoType.TalerReserve: {
                          // FIXME: support wire transfer to wallet
                          return <div>not yet supported</div>;
                        }
                        case PaytoType.IBAN: {
                          const name = details.account.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">
                                  <i18n.Translate>
                                    Payment Service Provider's account number
                                  </i18n.Translate>
                                </dt>
                                <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
                                  {details.account.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">
                                    <i18n.Translate>
                                      Payment Service Provider's name
                                    </i18n.Translate>
                                  </dt>
                                  <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
                                    {name}
                                  </dd>
                                </div>
                              )}
                            </Fragment>
                          );
                        }
                        case PaytoType.TalerBank: {
                          const name = details.account.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">
                                  <i18n.Translate>
                                    Payment Service Provider's account bank
                                    hostname
                                  </i18n.Translate>
                                </dt>
                                <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
                                  {details.account.host}
                                </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">
                                  <i18n.Translate>
                                    Payment Service Provider's account id
                                  </i18n.Translate>
                                </dt>
                                <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
                                  {details.account.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">
                                    <i18n.Translate>
                                      Payment Service Provider's name
                                    </i18n.Translate>
                                  </dt>
                                  <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
                                    {name}
                                  </dd>
                                </div>
                              )}
                            </Fragment>
                          );
                        }
                        case PaytoType.Bitcoin: {
                          const name = details.account.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">
                                  <i18n.Translate>
                                    Payment Service Provider's account address
                                  </i18n.Translate>
                                </dt>
                                <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
                                  {details.account.address}
                                </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">
                                    <i18n.Translate>
                                      Payment Service Provider's name
                                    </i18n.Translate>
                                  </dt>
                                  <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
                                    {name}
                                  </dd>
                                </div>
                              )}
                            </Fragment>
                          );
                        }
                        case PaytoType.Ethereum: {
                          const name = details.account.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">
                                  <i18n.Translate>
                                    Payment Service Provider's account address
                                  </i18n.Translate>
                                </dt>
                                <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
                                  {details.account.address}
                                </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">
                                    <i18n.Translate>
                                      Payment Service Provider's name
                                    </i18n.Translate>
                                  </dt>
                                  <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
                                    {name}
                                  </dd>
                                </div>
                              )}
                            </Fragment>
                          );
                        }
                        default: {
                          assertUnreachable(details.account);
                        }
                      }
                    })()}
                    <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">
                        <i18n.Translate>Amount</i18n.Translate>
                      </dt>
                      <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
                        {details.amount !== undefined ? (
                          <RenderAmount
                            value={details.amount}
                            spec={config.currency_specification}
                          />
                        ) : (
                          <i18n.Translate>
                            No amount has yet been determined.
                          </i18n.Translate>
                        )}
                      </dd>
                    </div>
                    {Amounts.isZero(wireFee) ? undefined : (
                      <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">
                            <i18n.Translate>Cost</i18n.Translate>
                          </dt>
                          <dd class="mt-1 text-sm leading-6 text-gray-700 sm:col-span-2 sm:mt-0">
                            <RenderAmount
                              value={wireFee}
                              negative
                              withColor
                              spec={config.currency_specification}
                            />
                          </dd>
                        </div>
                      </Fragment>
                    )}
                  </dl>
                </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">
                <ButtonBetter
                  type="button"
                  name="cancel"
                  class="text-sm font-semibold leading-6 text-gray-900"
                  onClick={abort}
                >
                  <i18n.Translate>Cancel</i18n.Translate>
                </ButtonBetter>
                <ButtonBetter
                  type="submit"
                  name="transfer"
                  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={confirm}
                >
                  <i18n.Translate>Transfer</i18n.Translate>
                </ButtonBetter>
              </div>
            </form>
          </ShouldBeSameUser>
        </div>
      </div>
    </div>
  );
}
export function FailedView({ error }: State.Failed) {
  const { i18n } = useTranslationContext();
  switch (error.case) {
    case HttpStatusCode.Unauthorized:
      return (
        <Attention
          type="danger"
          title={i18n.str`Unauthorized to make the operation, maybe the session has expired or the password changed.`}
        >
          {!error.detail ? undefined : (
            <div class="mt-2 text-sm text-red-700">{error.detail.hint}</div>
          )}
        </Attention>
      );
    case HttpStatusCode.Conflict:
      return (
        <Attention
          type="danger"
          title={i18n.str`The operation was rejected due to insufficient funds.`}
        >
          {!error.detail ? undefined : (
            <div class="mt-2 text-sm text-red-700">{error.detail.hint}</div>
          )}
        </Attention>
      );
    case HttpStatusCode.NotFound:
      return (
        <Attention
          type="danger"
          title={i18n.str`The operation was rejected due to insufficient funds.`}
        >
          {!error.detail ? undefined : (
            <div class="mt-2 text-sm text-red-700">{error.detail.hint}</div>
          )}
        </Attention>
      );
    default:
      assertUnreachable(error);
  }
}

export function AbortedView() {
  return <div>aborted</div>;
}

export function ConfirmedView({ routeClose }: State.Confirmed) {
  const { i18n } = useTranslationContext();
  const [settings, updateSettings] = usePreferences();
  return (
    <Fragment>
      <div class="relative ml-auto mr-auto transform overflow-hidden rounded-lg bg-white p-4 text-left shadow-xl transition-all ">
        <div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
          <svg
            class="h-6 w-6 text-green-600"
            fill="none"
            viewBox="0 0 24 24"
            stroke-width="1.5"
            stroke="currentColor"
            aria-hidden="true"
          >
            <path
              stroke-linecap="round"
              stroke-linejoin="round"
              d="M4.5 12.75l6 6 9-13.5"
            />
          </svg>
        </div>
        <div class="mt-3 text-center sm:mt-5">
          <h3
            class="text-base font-semibold leading-6 text-gray-900"
            id="modal-title"
          >
            <i18n.Translate>Withdrawal confirmed</i18n.Translate>
          </h3>
          <div class="mt-2">
            <p class="text-sm text-gray-500">
              <i18n.Translate>
                The wire transfer to the Payment Service Provider has been
                initiated. You will shortly receive the requested amount in your
                Taler wallet.{" "}
              </i18n.Translate>
            </p>
          </div>
        </div>
      </div>
      <div class="mt-4">
        <div class="flex items-center justify-between">
          <span class="flex flex-grow flex-col">
            <span
              class="text-sm text-black font-medium leading-6 "
              id="availability-label"
            >
              <i18n.Translate>Do not show this again</i18n.Translate>
            </span>
          </span>
          <button
            type="button"
            name="toggle withdrawal"
            data-enabled={!settings.showWithdrawalSuccess}
            class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2"
            role="switch"
            aria-checked="false"
            aria-labelledby="availability-label"
            aria-describedby="availability-description"
            onClick={() => {
              updateSettings(
                "showWithdrawalSuccess",
                !settings.showWithdrawalSuccess,
              );
            }}
          >
            <span
              aria-hidden="true"
              data-enabled={!settings.showWithdrawalSuccess}
              class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
            ></span>
          </button>
        </div>
      </div>
      <div class="mt-5 sm:mt-6">
        <a
          href={routeClose.url({})}
          type="button"
          name="close"
          class="inline-flex w-full justify-center 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"
        >
          <i18n.Translate>Close</i18n.Translate>
        </a>
      </div>
    </Fragment>
  );
}

export function ReadyView({
  uri,
  focus,
  onAbort,
  operationId,
}: State.Ready): VNode {
  const { i18n } = useTranslationContext();
  const walletInegrationApi = useTalerWalletIntegrationAPI();
  const [notification, safeFunctionHandler] = useLocalNotificationBetter();

  const { state: credentials } = useSessionState();
  const creds = credentials.status !== "loggedIn" ? undefined : credentials;
  const {
    config,
    lib: { bank },
  } = useBankCoreApiContext();

  const parsedUri = TalerUris.createTalerWithdraw(
    uri.bankIntegrationApiBaseUrl,
    uri.withdrawalOperationId,
  );
  const talerWithdrawUri = TalerUris.toString(parsedUri);
  useEffect(() => {
    walletInegrationApi.publishTalerAction(uri);
  }, []);

  const abort = safeFunctionHandler(
    (creds: LoggedIn) => bank.abortWithdrawalById(creds, operationId),
    !creds ? undefined : [creds],
  );
  abort.onSuccess = onAbort;
  abort.onFail = (fail) => {
    switch (fail.case) {
      case HttpStatusCode.Conflict:
        return i18n.str`The reserve operation has been confirmed previously and can't be aborted`;
      case HttpStatusCode.BadRequest:
        return i18n.str`The operation ID is invalid.`;
      case HttpStatusCode.NotFound:
        return i18n.str`The operation was not found.`;
    }
  };

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

      <div class="bg-white shadow-xl sm:rounded-lg">
        <div class="px-4 py-5 sm:p-6">
          <h3 class="text-base font-semibold leading-6 text-gray-900">
            <i18n.Translate>
              If you have a Taler wallet installed on this device
            </i18n.Translate>
          </h3>
          <div class="mt-4 mb-4 text-sm text-gray-500">
            <p>
              <i18n.Translate>
                Your wallet will display the details of the transaction
                including the fees (if applicable). If you do not yet have a
                wallet, please follow the instructions
              </i18n.Translate>{" "}
              <a
                class="font-semibold text-indigo-600 hover:text-indigo-900"
                name="wallet page"
                href="https://taler.net/en/wallet.html"
              >
                <i18n.Translate>on this page</i18n.Translate>
              </a>
              .
            </p>
          </div>
          <div class="flex items-center justify-between gap-x-6  pt-2 mt-2 ">
            <ButtonBetter
              type="button"
              name="cancel"
              class="text-sm font-semibold leading-6 text-gray-900"
              // class="inline-flex items-center rounded-md bg-white px-3 py-2 text-sm font-semibold text-black shadow-sm "
              onClick={abort}
            >
              <i18n.Translate>Cancel</i18n.Translate>
            </ButtonBetter>

            <a
              href={talerWithdrawUri}
              name="withdraw"
              class="inline-flex items-center  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"
            >
              <i18n.Translate>Withdraw</i18n.Translate>
            </a>
          </div>
        </div>
      </div>

      <div class="bg-white shadow-xl sm:rounded-lg mt-8">
        <div class="px-4 py-5 sm:p-6">
          <h3 class="text-base font-semibold leading-6 text-gray-900">
            <i18n.Translate>
              In case you have a Taler wallet on another device
            </i18n.Translate>
          </h3>
          <div class="mt-4 max-w-xl text-sm text-gray-500">
            <i18n.Translate>
              Scan the QR below to start the withdrawal.
            </i18n.Translate>
          </div>
          <div class="mt-2 max-w-md ml-auto mr-auto">
            <QR text={talerWithdrawUri} />
          </div>
        </div>
        <div class="flex items-center justify-center gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
          <ButtonBetter
            type="button"
            class="text-sm font-semibold leading-6 text-gray-900"
            onClick={abort}
          >
            <i18n.Translate>Cancel</i18n.Translate>
          </ButtonBetter>
        </div>
      </div>
    </Fragment>
  );
}
