/*
 This file is part of GNU Taler
 (C) 2022 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 { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { useState } from "preact/hooks";
import { alertFromError, useAlertContext } from "../../context/alert.js";
import { useBackendContext } from "../../context/backend.js";
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
import { Props, State } from "./index.js";
import { buildTermsOfServiceState } from "./utils.js";

const supportedFormats = {
  "text/html": "HTML",
  "text/xml": "XML",
  "text/markdown": "Markdown",
  "text/plain": "Plain text",
  "text/pdf": "PDF",
};

export function useComponentState({
  showEvenIfaccepted,
  exchangeUrl,
  readOnly,
  children,
}: Props): State {
  const api = useBackendContext();
  const [showContent, setShowContent] = useState<boolean>(!!readOnly);
  const { i18n, lang } = useTranslationContext();
  const [forceAccepted, setForceAccepted] = useState<boolean>();
  const [tosLang, setTosLang] = useState<string>();
  const { pushAlertOnError } = useAlertContext();

  const [format, setFormat] = useState("text/html");

  const acceptedLang = tosLang ?? lang;
  /**
   * For the exchange selected, bring the status of the terms of service
   */
  const terms = useAsyncAsHook(async () => {
    const exchangeTos = await api.wallet.call(
      WalletApiOperation.GetExchangeTos,
      {
        exchangeBaseUrl: exchangeUrl,
        acceptedFormat: [format],
        acceptLanguage: acceptedLang,
      },
    );

    const supportedLangs = exchangeTos.tosAvailableLanguages.reduce(
      (prev, cur) => {
        prev[cur] = cur;
        return prev;
      },
      {} as Record<string, string>,
    );

    const state = buildTermsOfServiceState(exchangeTos);

    return { state, supportedLangs };
  }, [acceptedLang, format]);

  if (!terms) {
    return {
      status: "loading",
      error: undefined,
    };
  }
  if (terms.hasError) {
    return {
      status: "error",
      error: alertFromError(
        i18n,
        i18n.str`Could not load the status of the term of service`,
        terms,
      ),
    };
  }
  const { state, supportedLangs } = terms.response;

  async function onUpdate(accepted: boolean): Promise<void> {
    if (!state) return;

    if (accepted) {
      await api.wallet.call(WalletApiOperation.SetExchangeTosAccepted, {
        exchangeBaseUrl: exchangeUrl,
      });
    } else {
      // mark as not accepted
    }
    terms?.retry();
  }

  const accepted = state.status === "accepted" || forceAccepted;

  const base = {
    error: undefined,
    showingTermsOfService: {
      value: showContent && (!accepted || showEvenIfaccepted),
      button: {
        onClick:
          accepted && !showEvenIfaccepted
            ? undefined
            : pushAlertOnError(async () => {
                setShowContent(!showContent);
              }),
      },
    },
    terms: state,
    termsAccepted: {
      value: accepted,
      button: {
        onClick: readOnly
          ? undefined
          : pushAlertOnError(async () => {
              const newValue = !accepted; //toggle
              await onUpdate(newValue);
              setShowContent(false);
            }),
      },
    },
  };

  if (accepted) {
    return {
      status: "show-buttons-accepted",
      ...base,
      children,
    };
  }

  if ((accepted && showEvenIfaccepted) || showContent) {
    return {
      status: "show-content",
      error: undefined,
      terms: state,
      showingTermsOfService: readOnly ? undefined : base.showingTermsOfService,
      termsAccepted: base.termsAccepted,
      skipTos: {
        onClick: pushAlertOnError(async () => {
          setForceAccepted(true);
        }),
      },
      tosFormat: {
        onChange: pushAlertOnError(async (s) => {
          setFormat(s);
        }),
        list: supportedFormats,
        value: format ?? "",
      },
      tosLang: {
        onChange: pushAlertOnError(async (s) => {
          setTosLang(s);
        }),
        list: supportedLangs,
        value: tosLang ?? lang,
      },
    };
  }
  //showing buttons
  return {
    status: "show-buttons-not-accepted",
    ...base,
  };
}
