/*
 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,
  AmountJson,
  Amounts,
  HttpStatusCode,
  TalerCorebankApi,
  TranslatedString,
  assertUnreachable,
  parseWithdrawUri,
} from "@gnu-taler/taler-util";
import {
  Attention,
  LocalNotificationBanner,
  notifyError,
  useLocalNotification,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { VNode, h } from "preact";
import { forwardRef } from "preact/compat";
import { useState } from "preact/hooks";
import { useBankCoreApiContext } from "@gnu-taler/web-util/browser";
import { useSessionState } from "../hooks/session.js";
import { useBankState } from "../hooks/bank-state.js";
import { usePreferences } from "../hooks/preferences.js";
import { RouteDefinition } from "@gnu-taler/web-util/browser";
import { undefinedIfEmpty } from "../utils.js";
import { OperationState } from "./OperationState/index.js";
import {
  InputAmount,
  RenderAmount,
  doAutoFocus,
} from "./PaytoWireTransferForm.js";
import { useSettingsContext } from "../context/settings.js";

const RefAmount = forwardRef(InputAmount);

function OldWithdrawalForm({
  onOperationCreated,
  limit,
  balance,
  routeCancel,
  focus,
  routeOperationDetails,
}: {
  limit: AmountJson;
  balance: AmountJson;
  focus?: boolean;
  routeOperationDetails: RouteDefinition<{ wopid: string }>;
  onOperationCreated: (wopid: string) => void;
  routeCancel: RouteDefinition;
}): VNode {
  const { i18n } = useTranslationContext();
  const settings = useSettingsContext();
  const [preference] = usePreferences();

  // const walletInegrationApi = useTalerWalletIntegrationAPI()
  // const { navigateTo } = useNavigationContext();

  const [bankState, updateBankState] = useBankState();
  const {
    lib: { bank: api },
    config,
  } = useBankCoreApiContext();

  const { state: credentials } = useSessionState();
  const creds = credentials.status !== "loggedIn" ? undefined : credentials;

  const [amountStr, setAmountStr] = useState<string | undefined>(
    `${settings.defaultSuggestedAmount ?? 1}`,
  );
  const [notification, notify, handleError] = useLocalNotification();

  if (bankState.currentWithdrawalOperationId) {
    // FIXME: doing the preventDefault is not optimal

    // const suri = stringifyWithdrawUri({
    //   bankIntegrationApiBaseUrl: api.getIntegrationAPI().baseUrl,
    //   withdrawalOperationId: bankState.currentWithdrawalOperationId,
    // });
    // const uri = parseWithdrawUri(suri)!
    const url = routeOperationDetails.url({
      wopid: bankState.currentWithdrawalOperationId,
    });
    return (
      <Attention
        type="warning"
        title={i18n.str`There is an operation already`}
        onClose={() => {
          updateBankState("currentWithdrawalOperationId", undefined);
        }}
      >
        <span ref={focus ? doAutoFocus : undefined} />
        <i18n.Translate>Complete the operation in</i18n.Translate>{" "}
        <a
          class="font-semibold text-yellow-700 hover:text-yellow-600"
          name="complete operation"
          href={url}
          // onClick={(e) => {
          //   e.preventDefault()
          //   walletInegrationApi.publishTalerAction(uri, () => {
          //     navigateTo(url)
          //   })
          // }}
        >
          <i18n.Translate>this page</i18n.Translate>
        </a>
      </Attention>
    );
  }

  const trimmedAmountStr = amountStr?.trim();

  const parsedAmount = trimmedAmountStr
    ? Amounts.parse(`${limit.currency}:${trimmedAmountStr}`)
    : undefined;

  const errors = undefinedIfEmpty({
    amount:
      trimmedAmountStr == null
        ? i18n.str`Required`
        : !parsedAmount
          ? i18n.str`Invalid`
          : Amounts.cmp(limit, parsedAmount) === -1
            ? i18n.str`Balance is not enough`
            : undefined,
  });

  async function doStart() {
    if (!parsedAmount || !creds) return;
    await handleError(async () => {
      const params: TalerCorebankApi.BankAccountCreateWithdrawalRequest =
        preference.fastWithdrawalForm
          ? {
              suggested_amount: Amounts.stringify(parsedAmount),
            }
          : {
              amount: Amounts.stringify(parsedAmount),
            };
      const resp = await api.createWithdrawal(creds, params);
      if (resp.type === "ok") {
        const uri = parseWithdrawUri(resp.body.taler_withdraw_uri);
        if (!uri) {
          return notifyError(
            i18n.str`Server responded with an invalid  withdraw URI`,
            i18n.str`Withdraw URI: ${resp.body.taler_withdraw_uri}`,
          );
        } else {
          updateBankState(
            "currentWithdrawalOperationId",
            uri.withdrawalOperationId,
          );
          onOperationCreated(uri.withdrawalOperationId);
        }
      } else {
        switch (resp.case) {
          case HttpStatusCode.Conflict: {
            notify({
              type: "error",
              title: i18n.str`The operation was rejected due to insufficient funds`,
              description: resp.detail.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
            break;
          }
          case HttpStatusCode.Unauthorized: {
            notify({
              type: "error",
              title: i18n.str`The operation was rejected due to insufficient funds`,
              description: resp.detail.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
            break;
          }
          case HttpStatusCode.NotFound: {
            notify({
              type: "error",
              title: i18n.str`Account not found`,
              description: resp.detail.hint as TranslatedString,
              debug: resp.detail,
              when: AbsoluteTime.now(),
            });
            break;
          }
          default:
            assertUnreachable(resp);
        }
      }
    });
  }

  return (
    <form
      class="bg-white shadow-sm ring-1 ring-gray-900/5 sm:rounded-xl md:col-span-2 mt-4"
      autoCapitalize="none"
      autoCorrect="off"
      onSubmit={(e) => {
        e.preventDefault();
      }}
    >
      <LocalNotificationBanner notification={notification} />

      <div class="px-4 py-6 ">
        <div class="grid max-w-xs grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
          <div class="sm:col-span-5">
            <label for="withdraw-amount">{i18n.str`Amount`}</label>
            <RefAmount
              currency={limit.currency}
              value={amountStr}
              name="withdraw-amount"
              onChange={(v) => {
                setAmountStr(v);
              }}
              error={errors?.amount}
              ref={focus ? doAutoFocus : undefined}
            />
          </div>
        </div>
        <p class="mt-2 text-sm text-gray-500">
          <i18n.Translate>
            Current balance is{" "}
            <RenderAmount
              value={balance}
              spec={config.currency_specification}
            />
          </i18n.Translate>
        </p>
        {Amounts.cmp(limit, balance) > 0 ? (
          <p class="mt-2 text-sm text-gray-900">
            <i18n.Translate>
              You can withdraw up to{" "}
              <RenderAmount
                value={limit}
                spec={config.currency_specification}
              />
            </i18n.Translate>
          </p>
        ) : undefined}
        <div class="mt-4">
          <div class="sm:inline">
            <button
              type="button"
              name="set 50"
              class="               inline-flex px-6 py-4 text-sm items-center rounded-l-md bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
              onClick={(e) => {
                e.preventDefault();
                setAmountStr("50.00");
              }}
            >
              50.00
            </button>
            <button
              type="button"
              name="set 25"
              class=" -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center rounded-r-md sm:rounded-none             bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
              onClick={(e) => {
                e.preventDefault();
                setAmountStr("25.00");
              }}
            >
              25.00
            </button>
          </div>
          <div class="mt-4 sm:inline">
            <button
              type="button"
              name="set 10"
              class=" -ml-px -mr-px inline-flex px-6 py-4 text-sm items-center rounded-l-md sm:rounded-none             bg-white text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
              onClick={(e) => {
                e.preventDefault();
                setAmountStr("10.00");
              }}
            >
              10.00
            </button>
            <button
              type="button"
              name="set 5"
              class="               inline-flex px-6 py-4 text-sm items-center rounded-r-md bg-white  text-gray-900 ring-1 ring-inset ring-gray-300 hover:bg-gray-50 focus:z-10"
              onClick={(e) => {
                e.preventDefault();
                setAmountStr("5.00");
              }}
            >
              5.00
            </button>
          </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">
        <a
          href={routeCancel.url({})}
          name="cancel"
          class="text-sm font-semibold leading-6 text-gray-900"
        >
          <i18n.Translate>Cancel</i18n.Translate>
        </a>
        <button
          type="submit"
          name="continue"
          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={isRawPayto ? !!errorsPayto : !!errorsWire}
          onClick={(e) => {
            e.preventDefault();
            doStart();
          }}
        >
          <i18n.Translate>Continue</i18n.Translate>
        </button>
      </div>
    </form>
  );
}

export function WalletWithdrawForm({
  focus,
  limit,
  balance,
  routeCancel,
  onAuthorizationRequired,
  onOperationCreated,
  onOperationAborted,
  routeOperationDetails,
}: {
  limit: AmountJson;
  balance: AmountJson;
  focus?: boolean;
  routeOperationDetails: RouteDefinition<{ wopid: string }>;
  onAuthorizationRequired: () => void;
  onOperationCreated: (wopid: string) => void;
  onOperationAborted: () => void;
  routeCancel: RouteDefinition;
}): VNode {
  const { i18n } = useTranslationContext();
  const [pref, updatePref] = usePreferences();

  return (
    <div class="grid grid-cols-1 gap-x-8 gap-y-8 pt-6 md:grid-cols-3 bg-gray-100 my-4 px-4 pb-4 rounded-lg">
      <div class="px-4 sm:px-0">
        <h2 class="text-base font-semibold leading-7 text-gray-900">
          <i18n.Translate>Prepare your Taler wallet</i18n.Translate>
        </h2>
        <p class="mt-1 text-sm text-gray-500">
          <i18n.Translate>
            After using your wallet you will need to authorize or cancel the
            operation on this site.
          </i18n.Translate>
        </p>
      </div>

      <div class="col-span-2">
        {pref.showInstallWallet && (
          <Attention
            title={i18n.str`You need a Taler wallet`}
            onClose={() => {
              updatePref("showInstallWallet", false);
            }}
          >
            <i18n.Translate>
              If you don't have one yet you can follow the instruction in
            </i18n.Translate>{" "}
            <a
              target="_blank"
              name="wallet page"
              rel="noreferrer noopener"
              class="font-semibold text-blue-700 hover:text-blue-600"
              href="https://taler.net/en/wallet.html"
            >
              <i18n.Translate>this page</i18n.Translate>
            </a>
          </Attention>
        )}

        {!pref.fastWithdrawalForm ? (
          <OldWithdrawalForm
            focus={focus}
            routeOperationDetails={routeOperationDetails}
            limit={limit}
            balance={balance}
            routeCancel={routeCancel}
            onOperationCreated={onOperationCreated}
          />
        ) : (
          <OperationState
            focus={focus}
            currency={limit.currency}
            onAuthorizationRequired={onAuthorizationRequired}
            routeClose={routeCancel}
            routeHere={routeOperationDetails}
            onAbort={onOperationAborted}
            // route={routeCancel}
          />
        )}
      </div>
    </div>
  );
}
