/*
 This file is part of GNU Taler
 (C) 2021-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/>
 */

/**
 *
 * @author Sebastian Javier Marchano (sebasjm)
 */

import {
  AccountLetter,
  codecForAccountLetter,
  PaytoString,
  PaytoUri,
  stringifyPaytoUri,
} from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { ComponentChildren, Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { useSessionContext } from "../../context/session.js";
import { DEFAULT_REQUEST_TIMEOUT } from "../../utils/constants.js";
import { undefinedIfEmpty } from "../../utils/table.js";
import { Spinner } from "../exception/loading.js";
import { FormErrors, FormProvider } from "../form/FormProvider.js";
import { Input } from "../form/Input.js";

interface Props {
  active?: boolean;
  description?: string;
  onCancel?: () => void;
  onConfirm?: () => void;
  label?: string;
  children?: ComponentChildren;
  danger?: boolean;
  disabled?: boolean;
}

export function ConfirmModal({
  active,
  description,
  onCancel,
  onConfirm,
  children,
  danger,
  disabled,
  label = "Confirm",
}: Props): VNode {
  const { i18n } = useTranslationContext();
  return (
    <div class={active ? "modal is-active" : "modal"}>
      <div class="modal-background " onClick={onCancel} />
      <div class="modal-card" style={{ maxWidth: 700 }}>
        <header class="modal-card-head">
          {!description ? null : (
            <p class="modal-card-title">
              <b>{description}</b>
            </p>
          )}
          <button class="delete " aria-label="close" onClick={onCancel} />
        </header>
        <section class="modal-card-body">{children}</section>
        <footer class="modal-card-foot">
          <div class="buttons is-right" style={{ width: "100%" }}>
            {onConfirm ? (
              <Fragment>
                <button class="button " onClick={onCancel}>
                  <i18n.Translate>Cancel</i18n.Translate>
                </button>

                <button
                  class={danger ? "button is-danger " : "button is-info "}
                  disabled={disabled}
                  onClick={onConfirm}
                >
                  <i18n.Translate>{label}</i18n.Translate>
                </button>
              </Fragment>
            ) : (
              <button class="button " onClick={onCancel}>
                <i18n.Translate>Close</i18n.Translate>
              </button>
            )}
          </div>
        </footer>
      </div>
      <button
        class="modal-close is-large "
        aria-label="close"
        onClick={onCancel}
      />
    </div>
  );
}

export function ContinueModal({
  active,
  description,
  onCancel,
  onConfirm,
  children,
  disabled,
}: Props): VNode {
  const { i18n } = useTranslationContext();
  return (
    <div class={active ? "modal is-active" : "modal"}>
      <div class="modal-background " onClick={onCancel} />
      <div class="modal-card">
        <header class="modal-card-head has-background-success">
          {!description ? null : <p class="modal-card-title">{description}</p>}
          <button class="delete " aria-label="close" onClick={onCancel} />
        </header>
        <section class="modal-card-body">{children}</section>
        <footer class="modal-card-foot">
          <div class="buttons is-right" style={{ width: "100%" }}>
            <button
              class="button is-success "
              disabled={disabled}
              onClick={onConfirm}
            >
              <i18n.Translate>Continue</i18n.Translate>
            </button>
          </div>
        </footer>
      </div>
      <button
        class="modal-close is-large "
        aria-label="close"
        onClick={onCancel}
      />
    </div>
  );
}

export function SimpleModal({
  onCancel,
  children,
}: {
  onCancel: () => void;
  children: ComponentChildren;
}): VNode {
  return (
    <div class="modal is-active">
      <div class="modal-background " onClick={onCancel} />
      <div class="modal-card">
        <section class="modal-card-body is-main-section">{children}</section>
      </div>
      <button
        class="modal-close is-large "
        aria-label="close"
        onClick={onCancel}
      />
    </div>
  );
}

export function ClearConfirmModal({
  description,
  onCancel,
  onClear,
  onConfirm,
  children,
}: Props & { onClear?: () => void }): VNode {
  const { i18n } = useTranslationContext();
  return (
    <div class="modal is-active">
      <div class="modal-background " onClick={onCancel} />
      <div class="modal-card">
        <header class="modal-card-head">
          {!description ? null : <p class="modal-card-title">{description}</p>}
          <button class="delete " aria-label="close" onClick={onCancel} />
        </header>
        <section class="modal-card-body is-main-section">{children}</section>
        <footer class="modal-card-foot">
          {onClear && (
            <button
              class="button is-danger"
              onClick={onClear}
              disabled={onClear === undefined}
            >
              <i18n.Translate>Clear</i18n.Translate>
            </button>
          )}
          <div class="buttons is-right" style={{ width: "100%" }}>
            <button class="button " onClick={onCancel}>
              <i18n.Translate>Cancel</i18n.Translate>
            </button>
            <button
              class="button is-info"
              onClick={onConfirm}
              disabled={onConfirm === undefined}
            >
              <i18n.Translate>Confirm</i18n.Translate>
            </button>
          </div>
        </footer>
      </div>
      <button
        class="modal-close is-large "
        aria-label="close"
        onClick={onCancel}
      />
    </div>
  );
}

interface ImportingAccountModalProps {
  onCancel: () => void;
  onConfirm: (account: AccountLetter) => void;
}

export function ImportingAccountModal({
  onCancel,
  onConfirm,
}: ImportingAccountModalProps): VNode {
  const { i18n } = useTranslationContext();
  const [letter, setLetter] = useState<string>();
  let parsed = undefined;
  try {
    parsed = JSON.parse(letter ?? "");
  } catch (e) {
    parsed = undefined;
  }
  let account: AccountLetter | undefined = undefined;
  let parsingError: string | undefined = undefined;
  try {
    account =
      parsed !== undefined ? codecForAccountLetter().decode(parsed) : undefined;
  } catch (e) {
    account = undefined;
    parsingError = e instanceof Error ? e.message : String(e);
  }
  const errors: FormErrors<{ letter: string }> = {
    letter: !letter
      ? i18n.str`Required`
      : parsed === undefined
        ? i18n.str`Letter must be a JSON string`
        : account === undefined
          ? i18n.str`JSON string is invalid`
          : undefined,
  };
  return (
    <ConfirmModal
      label={i18n.str`Import`}
      description={i18n.str`Importing an account from the bank`}
      active
      onCancel={onCancel}
      disabled={account === undefined}
      onConfirm={() => onConfirm(account!)}
    >
      <p>
        <i18n.Translate>
          You can export your account settings from the Libeufin Bank's account
          profile. Paste the content in the next field.
        </i18n.Translate>
      </p>
      <div class="field is-horizontal">
        <div class="field-label is-normal">
          <label class="label">
            <i18n.Translate>Account information</i18n.Translate>
          </label>
        </div>
        <div class="field-body is-flex-grow-3">
          <div class="field">
            <p class="control">
              <input
                class="input"
                value={letter ?? ""}
                onChange={(e) => {
                  setLetter(e.currentTarget.value);
                }}
              />
            </p>
            {letter !== undefined && errors.letter && (
              <p class="help is-danger">{errors.letter}</p>
            )}
            {parsingError !== undefined && (
              <p class="help is-danger">{parsingError}</p>
            )}
          </div>
        </div>
      </div>
    </ConfirmModal>
  );
}

interface CompareAccountsModalProps {
  onCancel: () => void;
  onConfirm: (account: PaytoString) => void;
  formPayto: PaytoUri | undefined;
  testPayto: PaytoUri;
}

function getHostFromHostPath(s: string | undefined) {
  if (!s) return undefined;
  try {
    const u = new URL(`https://${s}`);
    const endpath = u.pathname.lastIndexOf("/");
    return u.origin + u.pathname.substring(0, endpath);
  } catch (e) {
    return undefined;
  }
}

function getAccountIdFromHostPath(s: string | undefined) {
  if (!s) return undefined;
  try {
    const u = new URL(`https://${s}`);
    const endpath = u.pathname.lastIndexOf("/");
    return u.pathname.substring(endpath + 1);
  } catch (e) {
    return undefined;
  }
}

export function CompareAccountsModal({
  onCancel,
  onConfirm,
  formPayto,
  testPayto,
}: CompareAccountsModalProps): VNode {
  const { i18n } = useTranslationContext();
  return (
    <ConfirmModal
      label={i18n.str`Correct form`}
      description={i18n.str`Comparing account details`}
      active
      onCancel={onCancel}
      onConfirm={() => onConfirm(stringifyPaytoUri(testPayto))}
    >
      <p>
        <i18n.Translate>
          Testing against the account info URL succeeded but the account
          information reported is different with the account details form.
        </i18n.Translate>
      </p>
      <div class="table-container">
        <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
          <thead>
            <tr>
              <td>
                <i18n.Translate>Field</i18n.Translate>
              </td>
              <td>
                <i18n.Translate>In the form</i18n.Translate>
              </td>
              <td>
                <i18n.Translate>Reported</i18n.Translate>
              </td>
            </tr>
          </thead>
          <tbody>
            <tr>
              <td>
                <i18n.Translate>Type</i18n.Translate>
              </td>
              <td>{formPayto?.targetType ?? "--"}</td>
              <td>{testPayto.targetType}</td>
            </tr>
            {testPayto.targetType === "iban" && (
              <tr>
                <td>
                  <i18n.Translate>IBAN</i18n.Translate>
                </td>
                <td>{formPayto?.targetPath ?? "--"}</td>
                <td>{testPayto.targetPath}</td>
              </tr>
            )}
            {testPayto.targetType === "bitcoin" && (
              <tr>
                <td>
                  <i18n.Translate>Address</i18n.Translate>
                </td>
                <td>{formPayto?.targetPath ?? "--"}</td>
                <td>{testPayto.targetPath}</td>
              </tr>
            )}
            {testPayto.targetType === "x-taler-bank" && (
              <Fragment>
                <tr>
                  <td>
                    <i18n.Translate>Host</i18n.Translate>
                  </td>
                  <td>{getHostFromHostPath(formPayto?.targetPath) ?? "--"}</td>
                  <td>{getHostFromHostPath(testPayto.targetPath)}</td>
                </tr>
                <tr>
                  <td>
                    <i18n.Translate>Account id</i18n.Translate>
                  </td>
                  <td>
                    {getAccountIdFromHostPath(formPayto?.targetPath) ?? "--"}
                  </td>
                  <td>{getAccountIdFromHostPath(testPayto.targetPath)}</td>
                </tr>
              </Fragment>
            )}
            <tr>
              <td>
                <i18n.Translate>Owner's name</i18n.Translate>
              </td>
              <td>{formPayto?.params["receiver-name"] ?? "--"}</td>
              <td>{testPayto.params["receiver-name"]}</td>
            </tr>
          </tbody>
        </table>
      </div>
    </ConfirmModal>
  );
}

interface DeleteModalProps {
  element: { id: string; name: string };
  onCancel: () => void;
  onConfirm: (id: string) => void;
}

export function DeleteModal({
  element,
  onCancel,
  onConfirm,
}: DeleteModalProps): VNode {
  const { i18n } = useTranslationContext();
  return (
    <ConfirmModal
      label={`Delete instance`}
      description={`Delete the instance "${element.name}"`}
      danger
      active
      onCancel={onCancel}
      onConfirm={() => onConfirm(element.id)}
    >
      <p>
        <i18n.Translate>
          If you delete the instance named <b>&quot;{element.name}&quot;</b>{" "}
          (ID: <b>{element.id}</b>), the merchant will no longer be able to
          process orders or refunds
        </i18n.Translate>
      </p>
      <p>
        <i18n.Translate>
          This action deletes the instance private key, but preserves all
          transaction data. You can still access that data after deleting the
          instance.
        </i18n.Translate>
      </p>
      <p class="warning">
        <i18n.Translate>
          Deleting an instance{" "}
          <b>
            <i18n.Translate>Can't be undone</i18n.Translate>
          </b>
          .
        </i18n.Translate>
      </p>
    </ConfirmModal>
  );
}

export function PurgeModal({
  element,
  onCancel,
  onConfirm,
}: DeleteModalProps): VNode {
  const { i18n } = useTranslationContext();
  return (
    <ConfirmModal
      label={`Purge the instance`}
      description={`Purge the instance "${element.name}"`}
      danger
      active
      onCancel={onCancel}
      onConfirm={() => onConfirm(element.id)}
    >
      <p>
        <i18n.Translate>
          If you purge the instance named <b>&quot;{element.name}&quot;</b> (ID:{" "}
          <b>{element.id}</b>), you will also delete all it&apos;s transaction
          data.
        </i18n.Translate>
      </p>
      <p>
        <i18n.Translate>
          The instance will disappear from your list, and you will no longer be
          able to access it&apos;s data.
        </i18n.Translate>
      </p>
      <p class="warning">
        <i18n.Translate>
          Purging an instance{" "}
          <b>
            <i18n.Translate>Can't be undone</i18n.Translate>
          </b>
          .
        </i18n.Translate>
      </p>
    </ConfirmModal>
  );
}

interface UpdateTokenModalProps {
  oldToken?: string;
  onCancel: () => void;
  onConfirm: (value: string) => void;
  onClear: () => void;
}

//FIXME: merge UpdateTokenModal with SetTokenNewInstanceModal
export function UpdateTokenModal({
  onCancel,
  onClear,
  onConfirm,
  oldToken,
}: UpdateTokenModalProps): VNode {
  type State = { old_token: string; new_token: string; repeat_token: string };
  const [form, setValue] = useState<Partial<State>>({
    old_token: "",
    new_token: "",
    repeat_token: "",
  });
  const { i18n } = useTranslationContext();

  const hasInputTheCorrectOldToken = oldToken && oldToken !== form.old_token;
  const errors = undefinedIfEmpty({
    old_token: hasInputTheCorrectOldToken
      ? i18n.str`Is not the same as the current access token`
      : undefined,
    new_token: !form.new_token
      ? i18n.str`Required`
      : form.new_token === form.old_token
        ? i18n.str`Can't be the same as the old token`
        : undefined,
    repeat_token:
      form.new_token !== form.repeat_token
        ? i18n.str`Is not the same`
        : undefined,
  });

  const hasErrors = errors !== undefined;

  const { state } = useSessionContext();

  const text = i18n.str`You are updating the access token from instance with id ${state.instance}`;

  return (
    <ClearConfirmModal
      description={text}
      onCancel={onCancel}
      onConfirm={!hasErrors ? () => onConfirm(form.new_token!) : undefined}
      onClear={!hasInputTheCorrectOldToken && oldToken ? onClear : undefined}
    >
      <div class="columns">
        <div class="column" />
        <div class="column is-four-fifths">
          <FormProvider errors={errors} object={form} valueHandler={setValue}>
            {oldToken && (
              <Input<State>
                name="old_token"
                label={i18n.str`Old access token`}
                tooltip={i18n.str`Access token currently in use`}
                inputType="password"
              />
            )}
            <Input<State>
              name="new_token"
              label={i18n.str`New access token`}
              tooltip={i18n.str`Next access token to be used`}
              inputType="password"
            />
            <Input<State>
              name="repeat_token"
              label={i18n.str`Repeat access token`}
              tooltip={i18n.str`Confirm the same access token`}
              inputType="password"
            />
          </FormProvider>
          <p>
            <i18n.Translate>
              Clearing the access token will mean public access to the instance
            </i18n.Translate>
          </p>
        </div>
        <div class="column" />
      </div>
    </ClearConfirmModal>
  );
}

export function SetTokenNewInstanceModal({
  onCancel,
  onClear,
  onConfirm,
}: UpdateTokenModalProps): VNode {
  type State = { old_token: string; new_token: string; repeat_token: string };
  const [form, setValue] = useState<Partial<State>>({
    new_token: "",
    repeat_token: "",
  });
  const { i18n } = useTranslationContext();

  const errors = undefinedIfEmpty({
    new_token: !form.new_token
      ? i18n.str`Required`
      : form.new_token === form.old_token
        ? i18n.str`Can't be the same as the old access token`
        : undefined,
    repeat_token:
      form.new_token !== form.repeat_token
        ? i18n.str`Is not the same`
        : undefined,
  });

  const hasErrors = errors !== undefined;

  return (
    <div class="modal is-active">
      <div class="modal-background " onClick={onCancel} />
      <div class="modal-card">
        <header class="modal-card-head">
          <p class="modal-card-title">{i18n.str`You are setting the access token for the new instance`}</p>
          <button class="delete " aria-label="close" onClick={onCancel} />
        </header>
        <section class="modal-card-body is-main-section">
          <div class="columns">
            <div class="column" />
            <div class="column is-four-fifths">
              <FormProvider
                errors={errors}
                object={form}
                valueHandler={setValue}
              >
                <Input<State>
                  name="new_token"
                  label={i18n.str`New access token`}
                  tooltip={i18n.str`Next access token to be used`}
                  inputType="password"
                />
                <Input<State>
                  name="repeat_token"
                  label={i18n.str`Repeat access token`}
                  tooltip={i18n.str`Confirm the same access token`}
                  inputType="password"
                />
              </FormProvider>
              <p>
                <i18n.Translate>
                  With external authorization method no check will be done by
                  the merchant backend
                </i18n.Translate>
              </p>
            </div>
            <div class="column" />
          </div>
        </section>
        <footer class="modal-card-foot">
          {onClear && (
            <button
              class="button is-danger"
              onClick={onClear}
              disabled={onClear === undefined}
            >
              <i18n.Translate>Set external authorization</i18n.Translate>
            </button>
          )}
          <div class="buttons is-right" style={{ width: "100%" }}>
            <button class="button " onClick={onCancel}>
              <i18n.Translate>Cancel</i18n.Translate>
            </button>
            <button
              class="button is-info"
              onClick={() => onConfirm(form.new_token!)}
              disabled={hasErrors}
            >
              <i18n.Translate>Set access token</i18n.Translate>
            </button>
          </div>
        </footer>
      </div>
      <button
        class="modal-close is-large "
        aria-label="close"
        onClick={onCancel}
      />
    </div>
  );
}

export function LoadingModal({ onCancel }: { onCancel: () => void }): VNode {
  const { i18n } = useTranslationContext();
  return (
    <div class="modal is-active">
      <div class="modal-background " onClick={onCancel} />
      <div class="modal-card">
        <header class="modal-card-head">
          <p class="modal-card-title">
            <i18n.Translate>Operation in progress...</i18n.Translate>
          </p>
        </header>
        <section class="modal-card-body">
          <div class="columns">
            <div class="column" />
            <Spinner />
            <div class="column" />
          </div>
          <p>{i18n.str`The operation will be automatically canceled after ${DEFAULT_REQUEST_TIMEOUT} seconds`}</p>
        </section>
        <footer class="modal-card-foot">
          <div class="buttons is-right" style={{ width: "100%" }}>
            <button class="button " onClick={onCancel}>
              <i18n.Translate>Cancel</i18n.Translate>
            </button>
          </div>
        </footer>
      </div>
      <button
        class="modal-close is-large "
        aria-label="close"
        onClick={onCancel}
      />
    </div>
  );
}
