/*
 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,
  HostPortPath,
  HttpStatusCode,
  Paytos,
  TalerCoreBankErrorsByMethod,
  TalerCorebankApi,
  TalerError,
  TalerUris,
  assertUnreachable,
} from "@gnu-taler/taler-util";
import { useBankCoreApiContext, utils } from "@gnu-taler/web-util/browser";
import { useEffect, useState } from "preact/hooks";
import { useSettingsContext } from "../../context/settings.js";
import { useWithdrawalDetails } from "../../hooks/account.js";
import { useBankState } from "../../hooks/bank-state.js";
import { usePreferences } from "../../hooks/preferences.js";
import { useSessionState } from "../../hooks/session.js";
import { Props, State } from "./index.js";

export function useComponentState({
  routeClose,
  onAbort,
  focus,
}: Props): utils.RecursiveState<State> {
  const [preference] = usePreferences();
  const settings = useSettingsContext();
  const [bankState, updateBankState] = useBankState();
  const { state: credentials } = useSessionState();
  const creds = credentials.status !== "loggedIn" ? undefined : credentials;
  const {
    config,
    lib: { bank },
  } = useBankCoreApiContext();

  const [failure, setFailure] = useState<
    TalerCoreBankErrorsByMethod<"createWithdrawal"> | undefined
  >();
  const amount = settings.defaultSuggestedAmount;

  async function doSilentStart() {
    // FIXME: if amount is not enough use balance
    const parsedAmount = Amounts.parseOrThrow(`${config.currency}:${amount}`);
    if (!creds) return;
    const params: TalerCorebankApi.BankAccountCreateWithdrawalRequest =
      preference.fastWithdrawalForm
        ? {
            suggested_amount: Amounts.stringify(parsedAmount),
          }
        : {
            amount: Amounts.stringify(parsedAmount),
          };

    const resp = await bank.createWithdrawal(creds, params);
    if (resp.type === "fail") {
      setFailure(resp);
      return;
    }
    updateBankState("currentWithdrawalOperationId", resp.body.withdrawal_id);
  }

  const withdrawalOperationId = bankState.currentWithdrawalOperationId;
  useEffect(() => {
    if (withdrawalOperationId === undefined) {
      doSilentStart();
    }
  }, [preference.fastWithdrawalForm, amount]);

  if (failure) {
    return {
      status: "failed",
      error: failure,
    };
  }

  if (!withdrawalOperationId) {
    return {
      status: "loading",
      error: undefined,
    };
  }

  const parsedUri = TalerUris.createTalerWithdraw(
    bank.getIntegrationAPI().href as HostPortPath,
    withdrawalOperationId,
  );
  const uri = TalerUris.toString(parsedUri);
  if (!parsedUri) {
    return {
      status: "invalid-withdrawal",
      error: undefined,
      uri,
    };
  }

  return (): utils.RecursiveState<State> => {
    const result = useWithdrawalDetails(withdrawalOperationId);

    const shouldCreateNewOperation =
      result &&
      (result instanceof TalerError ||
        result.type === "fail" ||
        result.body.status === "aborted" ||
        result.body.status === "confirmed");

    useEffect(() => {
      if (shouldCreateNewOperation) {
        doSilentStart();
      }
    }, [shouldCreateNewOperation]);
    if (!result) {
      return {
        status: "loading",
        error: undefined,
      };
    }
    if (result instanceof TalerError) {
      return {
        status: "loading-error",
        error: result,
      };
    }

    if (result.type === "fail") {
      switch (result.case) {
        case HttpStatusCode.BadRequest:
        case HttpStatusCode.NotFound: {
          return {
            status: "aborted",
            error: undefined,
            routeClose,
          };
        }
        default:
          assertUnreachable(result);
      }
    }

    const { body: data } = result;
    if (data.status === "aborted") {
      return {
        status: "aborted",
        error: undefined,
        routeClose,
      };
    }

    if (data.status === "confirmed") {
      if (!preference.showWithdrawalSuccess) {
        updateBankState("currentWithdrawalOperationId", undefined);
        // onClose()
      }
      return {
        status: "confirmed",
        error: undefined,
        routeClose,
      };
    }

    if (data.status === "pending") {
      return {
        status: "ready",
        error: undefined,
        uri: parsedUri,
        routeClose,
        focus,
        operationId: withdrawalOperationId,
        onAbort,
      };
    }

    if (!data.selected_reserve_pub) {
      return {
        status: "invalid-reserve",
        error: undefined,
        reserve: data.selected_reserve_pub,
      };
    }

    const account = !data.selected_exchange_account
      ? undefined
      : Paytos.fromString(data.selected_exchange_account);

    if (!account || account.type === "fail" || !account.body.targetType) {
      return {
        status: "invalid-payto",
        error: undefined,
        payto: data.selected_exchange_account,
      };
    }

    return {
      status: "need-confirmation",
      error: undefined,
      details: {
        account: account.body,
        reserve: data.selected_reserve_pub,
        username: data.username,
        amount: !data.amount ? undefined : Amounts.parse(data.amount),
      },

      account: data.username,
      operationId: withdrawalOperationId,
      onAbort,
    };
  };
}
