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

const TALER_SCREEN_ID = 112;

const RefAmount = forwardRef(InputAmount);

function ThereIsAnOperationWarning({
  wopid,
  onClose,
  focus,
  routeOperationDetails,
}: {
  focus?: boolean;
  wopid: string;
  onClose: () => void;
  routeOperationDetails: RouteDefinition<{ wopid: string }>;
}): VNode {
  const { i18n } = useTranslationContext();
  const url = routeOperationDetails.url({ wopid });

  return (
    <Attention
      type="warning"
      title={i18n.str`There is an operation already pending`}
      onClose={onClose}
    >
      <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>
  );
}

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

  const [, 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, safeFunctionHandler] = useLocalNotificationBetter();

  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,
  });

  const start = safeFunctionHandler(
    (creds: UserAndToken, amount: AmountString) =>
      api.createWithdrawal(
        creds,
        preference.fastWithdrawalForm
          ? { suggested_amount: amount }
          : { amount: amount },
      ),
    !parsedAmount || !creds
      ? undefined
      : [creds, Amounts.stringify(parsedAmount)],
  );

  start.onSuccess = (success) => {
    const uri = TalerUris.fromString(success.taler_withdraw_uri);
    if (uri.type === "fail" || uri.body.type !== TalerUriAction.Withdraw) {
      return notifyError(
        i18n.str`The server replied with an invalid taler://withdraw URI`,
        i18n.str`Withdraw URI: ${success.taler_withdraw_uri}`,
      );
    } else {
      updateBankState(
        "currentWithdrawalOperationId",
        uri.body.withdrawalOperationId,
      );
      onOperationCreated(uri.body.withdrawalOperationId);
    }
  };

  start.onFail = (fail) => {
    switch (fail.case) {
      case HttpStatusCode.Conflict:
        return i18n.str`The operation was rejected due to insufficient funds`;
      case HttpStatusCode.Unauthorized:
        return i18n.str`The operation was rejected due to insufficient funds`;
      case HttpStatusCode.NotFound:
        return i18n.str`Account not found`;
      default:
        assertUnreachable(fail);
    }
  };

  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);
              }}
              ref={focus ? doAutoFocus : undefined}
            />
          </div>
          <ShowInputErrorLabel
            message={errors?.amount}
            isDirty={amountStr !== undefined}
          />
        </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>
        <ButtonBetter
          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={start}
        >
          <i18n.Translate>Continue</i18n.Translate>
        </ButtonBetter>
      </div>
    </form>
  );
}

export function WalletWithdrawForm({
  focus,
  limit,
  balance,
  routeCancel,
  onOperationCreated,
  onOperationAborted,
}: {
  limit: IntAmountJson;
  balance: AmountJson;
  focus?: boolean;

  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>Use 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}
            limit={limit}
            balance={balance}
            routeCancel={routeCancel}
            onOperationCreated={onOperationCreated}
          />
        ) : (
          <OperationState
            focus={focus}
            routeClose={routeCancel}
            onAbort={onOperationAborted}
          />
        )}
      </div>
    </div>
  );
}
