/*
 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 {
  AbsoluteTime,
  HttpStatusCode,
  TalerErrorCode,
  TranslatedString,
  assertUnreachable,
  stringifyWithdrawUri,
} from "@gnu-taler/taler-util";
import {
  Attention,
  LocalNotificationBanner,
  notifyInfo,
  useLocalNotification,
  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 { useBankState } from "../../hooks/bank-state.js";
import { usePreferences } from "../../hooks/preferences.js";
import { ShouldBeSameUser } from "../WithdrawalConfirmationQuestion.js";
import { State } from "./index.js";

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: doAbort,
  onConfirm: doConfirm,
  routeHere,
  account,
  id,
  onAuthorizationRequired,
}: State.NeedConfirmation) {
  const { i18n } = useTranslationContext();
  const [settings] = usePreferences();
  const [notification, notify, errorHandler] = useLocalNotification();
  const [, updateBankState] = useBankState();

  async function onCancel() {
    errorHandler(async () => {
      if (!doAbort) return;
      const resp = await doAbort();
      if (!resp) return;
      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`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
            when: AbsoluteTime.now(),
          });
        case HttpStatusCode.BadRequest:
          return notify({
            type: "error",
            title: i18n.str`The operation id is invalid.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
            when: AbsoluteTime.now(),
          });
        case HttpStatusCode.NotFound:
          return notify({
            type: "error",
            title: i18n.str`The operation was not found.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
            when: AbsoluteTime.now(),
          });
        default:
          assertUnreachable(resp);
      }
    });
  }

  async function onConfirm() {
    errorHandler(async () => {
      if (!doConfirm) return;
      const resp = await doConfirm();
      if (!resp) {
        if (!settings.showWithdrawalSuccess) {
          notifyInfo(i18n.str`Wire transfer completed!`);
        }
        return;
      }
      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,
            when: AbsoluteTime.now(),
          });
        case TalerErrorCode.BANK_CONFIRM_INCOMPLETE:
          return notify({
            type: "error",
            title: i18n.str`The withdrawal operation can't be confirmed before a wallet accepted the transaction.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
            when: AbsoluteTime.now(),
          });
        case HttpStatusCode.BadRequest:
          return notify({
            type: "error",
            title: i18n.str`The operation id is invalid.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
            when: AbsoluteTime.now(),
          });
        case HttpStatusCode.NotFound:
          return notify({
            type: "error",
            title: i18n.str`The operation was not found.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
            when: AbsoluteTime.now(),
          });
        case TalerErrorCode.BANK_UNALLOWED_DEBIT:
          return notify({
            type: "error",
            title: i18n.str`Your balance is not enough.`,
            description: resp.detail.hint as TranslatedString,
            debug: resp.detail,
            when: AbsoluteTime.now(),
          });
        case HttpStatusCode.Accepted: {
          updateBankState("currentChallenge", {
            operation: "confirm-withdrawal",
            id: String(resp.body.challenge_id),
            sent: AbsoluteTime.never(),
            location: routeHere.url({ wopid: id }),
            request: id,
          });
          return onAuthorizationRequired();
        }
        default:
          assertUnreachable(resp);
      }
    });
  }

  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="flex items-center justify-between gap-x-6 border-t border-gray-900/10 px-4 py-4 sm:px-8">
                <button
                  type="button"
                  name="cancel"
                  class="text-sm font-semibold leading-6 text-gray-900"
                  onClick={(e) => {
                    e.preventDefault();
                    onCancel();
                  }}
                >
                  <i18n.Translate>Cancel</i18n.Translate>
                </button>
                <button
                  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={(e) => {
                    e.preventDefault();
                    onConfirm();
                  }}
                >
                  <i18n.Translate>Transfer</i18n.Translate>
                </button>
              </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.`}
        >
          <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.`}
        >
          <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.`}
        >
          <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 Taler operator has been initiated. You
                will soon 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, onAbort: doAbort }: State.Ready): VNode {
  const { i18n } = useTranslationContext();
  const walletInegrationApi = useTalerWalletIntegrationAPI();
  const [notification, notify, errorHandler] = useLocalNotification();

  const talerWithdrawUri = stringifyWithdrawUri(uri);
  useEffect(() => {
    walletInegrationApi.publishTalerAction(uri);
  }, []);

  async function onAbort() {
    errorHandler(async () => {
      const hasError = await doAbort();
      if (!hasError) return;
      switch (hasError.case) {
        case HttpStatusCode.Conflict:
          return notify({
            type: "error",
            title: i18n.str`The reserve operation has been confirmed previously and can't be aborted`,
            description: hasError.detail.hint as TranslatedString,
            debug: hasError.detail,
            when: AbsoluteTime.now(),
          });
        case HttpStatusCode.BadRequest:
          return notify({
            type: "error",
            title: i18n.str`The operation id is invalid.`,
            description: hasError.detail.hint as TranslatedString,
            debug: hasError.detail,
            when: AbsoluteTime.now(),
          });
        case HttpStatusCode.NotFound:
          return notify({
            type: "error",
            title: i18n.str`The operation was not found.`,
            description: hasError.detail.hint as TranslatedString,
            debug: hasError.detail,
            when: AbsoluteTime.now(),
          });
        default:
          assertUnreachable(hasError);
      }
    });
  }

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

      <div class="flex justify-end mt-4">
        <button
          type="button"
          name="cancel"
          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={onAbort}
        >
          <i18n.Translate>Cancel</i18n.Translate>
        </button>
      </div>

      <div class="bg-white shadow sm:rounded-lg mt-4">
        <div class="p-4">
          <h3 class="text-base font-semibold leading-6 text-gray-900">
            <i18n.Translate>On this device</i18n.Translate>
          </h3>
          <div class="mt-2 sm:flex sm:items-start sm:justify-between">
            <div class="max-w-xl text-sm text-gray-500">
              <p>
                <i18n.Translate>
                  If you are using a web browser on desktop you can also
                </i18n.Translate>
              </p>
            </div>
            <div class="mt-5 sm:ml-6 sm:mt-0 sm:flex sm:flex-shrink-0 sm:items-center">
              <a
                href={talerWithdrawUri}
                name="start"
                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>Start</i18n.Translate>
              </a>
            </div>
          </div>
        </div>
      </div>
      <div class="bg-white shadow sm:rounded-lg mt-2">
        <div class="p-4">
          <h3 class="text-base font-semibold leading-6 text-gray-900">
            <i18n.Translate>On a mobile phone</i18n.Translate>
          </h3>
          <div class="mt-2 sm:flex sm:items-start sm:justify-between">
            <div class="max-w-xl text-sm text-gray-500">
              <p>
                <i18n.Translate>
                  Scan the QR code with your mobile device.
                </i18n.Translate>
              </p>
            </div>
          </div>
          <div class="mt-2 max-w-md ml-auto mr-auto">
            <QR text={talerWithdrawUri} />
          </div>
        </div>
      </div>
    </Fragment>
  );
}
