/*
 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 {
  getQrCodesForPayto,
  Paytos,
  PaytoType,
  TranslatedString,
} from "@gnu-taler/taler-util";
import {
  ButtonBetterBulma,
  SafeHandlerTemplate,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { ComponentChildren, Fragment, h, VNode } from "preact";
import { useEffect, useRef, useState } from "preact/hooks";
import { doAutoFocus } from "../../../../web-util/src/components/utils.js";
import { DEFAULT_REQUEST_TIMEOUT } from "../../utils/constants.js";
import { Spinner } from "../exception/loading.js";
import { QR } from "../exception/QR.js";

const TALER_SCREEN_ID = 18;

interface Props {
  active?: boolean;
  description?: TranslatedString;
  onCancel?: () => void;
  confirm?: SafeHandlerTemplate<any, any>;
  label: TranslatedString;
  children?: ComponentChildren;
  danger?: boolean;
  /**
   * sometimes we want to prevent the user to close the dialog by error when clicking outside the box
   *
   * This could have been implemented as a separated component also
   */
  noCancelButton?: boolean;
}

export function ConfirmModal({
  active,
  description,
  onCancel,
  confirm,
  children,
  danger,
  label,
  noCancelButton,
}: 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 "
            type="button"
            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%" }}>
            {confirm ? (
              <Fragment>
                {onCancel && !noCancelButton ? (
                  <button class="button " type="button" onClick={onCancel}>
                    <i18n.Translate>Cancel</i18n.Translate>
                  </button>
                ) : undefined}

                <ButtonBetterBulma
                  type="submit"
                  class={danger ? "button is-danger " : "button is-info "}
                  onClick={confirm}
                >
                  <i18n.Translate>{label}</i18n.Translate>
                </ButtonBetterBulma>
              </Fragment>
            ) : noCancelButton ? undefined : (
              <button class="button " ref={doAutoFocus} onClick={onCancel}>
                <i18n.Translate>Close</i18n.Translate>
              </button>
            )}
          </div>
        </footer>
      </div>
      {noCancelButton ? undefined : (
        <button
          type="button"
          class="modal-close is-large "
          aria-label="close"
          onClick={onCancel}
        />
      )}
    </div>
  );
}

export function ContinueModal({
  active,
  description,
  onCancel,
  confirm,
  children,
}: 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%" }}>
            <ButtonBetterBulma
              ref={doAutoFocus}
              type="submit"
              class="button is-success "
              onClick={confirm}
            >
              <i18n.Translate>Continue</i18n.Translate>
            </ButtonBetterBulma>
          </div>
        </footer>
      </div>
      <button
        type="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}
        type="submit"
        ref={doAutoFocus}
      />
    </div>
  );
}

export function ClearConfirmModal({
  description,
  onCancel,
  onClear,
  confirm,
  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
              type="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 " type="button" onClick={onCancel}>
              <i18n.Translate>Cancel</i18n.Translate>
            </button>
            <ButtonBetterBulma
              class="button is-info"
              type="submit"
              onClick={confirm}
              ref={doAutoFocus}
            >
              <i18n.Translate>Confirm</i18n.Translate>
            </ButtonBetterBulma>
          </div>
        </footer>
      </div>
      <button
        type="button"
        class="modal-close is-large "
        aria-label="close"
        onClick={onCancel}
      />
    </div>
  );
}

interface CompareAccountsModalProps {
  onCancel: () => void;
  confirm: SafeHandlerTemplate<any, any>;
  formPayto: Paytos.URI | undefined;
  testPayto: Paytos.URI;
}

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,
  confirm,
  formPayto,
  testPayto,
}: CompareAccountsModalProps): VNode {
  const { i18n } = useTranslationContext();
  return (
    <ConfirmModal
      label={i18n.str`Correct form`}
      description={i18n.str`Comparing account details`}
      active
      onCancel={onCancel}
      confirm={confirm}
    >
      <p>
        <i18n.Translate>
          The connection to the account info URL was successful, but the
          reported account information does not match the details in the 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?.displayName ?? "--"}</td>
                <td>{testPayto.iban}</td>
              </tr>
            )}
            {testPayto.targetType === "bitcoin" && (
              <tr>
                <td>
                  <i18n.Translate>Address</i18n.Translate>
                </td>
                <td>{formPayto?.displayName ?? "--"}</td>
                <td>{testPayto.address}</td>
              </tr>
            )}
            {testPayto.targetType === "x-taler-bank" && (
              <Fragment>
                <tr>
                  <td>
                    <i18n.Translate>Host</i18n.Translate>
                  </td>
                  <td>{getHostFromHostPath(formPayto?.fullPath) ?? "--"}</td>
                  <td>{getHostFromHostPath(testPayto.fullPath)}</td>
                </tr>
                <tr>
                  <td>
                    <i18n.Translate>Account ID</i18n.Translate>
                  </td>
                  <td>
                    {getAccountIdFromHostPath(formPayto?.fullPath) ?? "--"}
                  </td>
                  <td>{getAccountIdFromHostPath(testPayto.fullPath)}</td>
                </tr>
              </Fragment>
            )}
            <tr>
              <td>
                <i18n.Translate>The account owner's legal name</i18n.Translate>
              </td>
              <td>{formPayto?.params["receiver-name"] ?? "--"}</td>
              <td>{testPayto.params["receiver-name"]}</td>
            </tr>
            {!!testPayto.params["receiver-postal-code"] && (
              <tr>
                <td>
                  <i18n.Translate>
                    The postal code of the account owner's address
                  </i18n.Translate>
                </td>
                <td>{formPayto?.params["receiver-postal-code"] ?? "--"}</td>
                <td>{testPayto.params["receiver-postal-code"]}</td>
              </tr>
            )}
            {!!testPayto.params["receiver-town"] && (
              <tr>
                <td>
                  <i18n.Translate>Town</i18n.Translate>
                </td>
                <td>{formPayto?.params["receiver-town"] ?? "--"}</td>
                <td>{testPayto.params["receiver-town"]}</td>
              </tr>
            )}
          </tbody>
        </table>
      </div>
    </ConfirmModal>
  );
}

interface ValidateBankAccountModalProps {
  onCancel: () => void;
  origin: Paytos.URI;
  targets: Paytos.URI[];
}
export function ValidBankAccount({
  onCancel,
  origin,
  targets,
}: ValidateBankAccountModalProps): VNode {
  const { i18n } = useTranslationContext();
  const payto = targets[0];
  const subject = payto.params["message"];

  const accountPart =
    payto.targetType === PaytoType.TalerBank ? (
      <Fragment>
        <Row name={i18n.str`Bank host`} value={payto.host} />
        <Row name={i18n.str`Bank account`} value={payto.account} />
      </Fragment>
    ) : payto.targetType === PaytoType.IBAN ? (
      <Fragment>
        {payto.bic !== undefined ? (
          <Row name={i18n.str`BIC`} value={payto.bic} />
        ) : undefined}
        <Row name={i18n.str`IBAN`} value={payto.iban} />
      </Fragment>
    ) : undefined;

  const receiverName =
    payto.params["receiver-name"] || payto.params["receiver"] || undefined;
  const receiverPostalCode = payto.params["receiver-postal-code"] || undefined;
  const receiverTown = payto.params["receiver-town"] || undefined;

  const from =
    origin.targetType === PaytoType.IBAN
      ? origin.iban
      : origin.targetType === PaytoType.TalerReserve ||
          origin.targetType === PaytoType.TalerReserveHttp
        ? origin.reservePub
        : origin.targetType === PaytoType.Bitcoin
          ? `${origin.address.substring(0, 8)}...`
          : origin.targetType === PaytoType.Ethereum
            ? `${origin.address.substring(0, 8)}...`
            : origin.displayName;

  const qrs = getQrCodesForPayto(Paytos.toFullString(payto));
  return (
    <ConfirmModal
      label={i18n.str`OK`}
      description={i18n.str`Validate bank account: ${from}`}
      active
      onCancel={onCancel}
      // onConfirm={onConfirm}
    >
      <p style={{ paddingTop: 0 }}>
        <i18n.Translate>
          In order to prove that you are the beneficial owner of the bank
          account, you are required to wire a small amount to a specified bank
          account with the subject below.
        </i18n.Translate>
      </p>
      <div class="table-container">
        <table>
          <tbody>
            <tr>
              <td colSpan={3}>
                <i18n.Translate>Step 1:</i18n.Translate>
                &nbsp;
                <i18n.Translate>
                  Copy and paste this IBAN and the legal name of the beneficial
                  owner into the respective fields in your preferred banking app
                  or online banking website.
                </i18n.Translate>
              </td>
            </tr>
            {accountPart}
            {receiverName ? (
              <Row name={i18n.str`Beneficiary`} value={receiverName} />
            ) : undefined}
            <tr>
              <td colSpan={3}>
                <i18n.Translate>Step 2:</i18n.Translate>
                &nbsp;
                <i18n.Translate>
                  Copy this string and paste it into the 'Subject' or 'Purpose'
                  field in your preferred banking app or online banking website.
                </i18n.Translate>
              </td>
            </tr>
            <Row name={i18n.str`Subject`} value={subject} literal />
            {receiverPostalCode ? (
              <Row
                name={i18n.str`Receiver postal code`}
                value={receiverPostalCode}
              />
            ) : undefined}
            {receiverTown ? (
              <Row name={i18n.str`Receiver town`} value={receiverTown} />
            ) : undefined}

            <tr>
              <td colSpan={3}>
                <i18n.Translate>Step 3:</i18n.Translate>
                &nbsp;
                <i18n.Translate>
                  Select the smallest possible amount for a wire transfer in
                  your preferred banking app or online banking website.
                </i18n.Translate>
              </td>
            </tr>
            <tr>
              <td colSpan={3}>
                <b>
                  <i18n.Translate>
                    Make sure ALL data is correct, especially the subject, and
                    that you are choosing the bank account from which you
                    definitely want to make the wire transfer. You can use the
                    copy buttons (<CopyIcon />) to avoid typos or make use of
                    the "payto://" URI below to copy just one value.
                  </i18n.Translate>
                </b>
              </td>
            </tr>
            <tr>
              <td colSpan={3}>
                {!qrs.length ? undefined : (
                  <div style={{ overflowY: "auto", height: "95%", padding: 5 }}>
                    <p>
                      <i18n.Translate>If your bank application allows you make a wire transfer using standard QR codes then try scanning the following:</i18n.Translate>
                    </p>
                    <table style={{ width: "100%" }}>
                      {qrs.map((q, idx) => {
                        return (
                          <tr key={idx}>
                            <td
                              style={{
                                padding: 5,
                                borderSpacing: 0,
                                borderRadius: 4,
                                border: "1px solid black",
                              }}
                            >
                              <Accordion name={q.type} >
                                <QR text={q.qrContent} />
                              </Accordion>
                            </td>
                          </tr>
                        );
                      })}
                    </table>
                  </div>
                )}
              </td>
            </tr>
            <tr>
              <td colSpan={2} width="100%">
                <i18n.Translate>
                  As an alternative, in case that your bank already supports the
                  'PayTo URI' standard, you can use this{" "}
                  <a
                    target="_blank"
                    rel="noreferrer"
                    title="RFC 8905 for designating targets for payments"
                    href="https://tools.ietf.org/html/rfc8905"
                  >
                    PayTo URI
                  </a>{" "}
                  link instead
                </i18n.Translate>
              </td>
              <td>
                <CopyButton getContent={() => Paytos.toFullString(payto)} />
              </td>
            </tr>
          </tbody>
        </table>
      </div>
    </ConfirmModal>
  );
}

function Accordion({
  openedByDefault,
  children,
  name,
}: {
  name: string;
  children: ComponentChildren;
  openedByDefault?: boolean;
}) {
  const [opened, setOpened] = useState(!!openedByDefault);
  return (
    <div>
      <button
        style={{
          color: "#444",
          padding: 8,
          fontSize: "large",
          width: "100%",
          cursor: "pointer",
          textAlign: "left",
          border: "none",
        }}
        class={opened ? "accordion active" : "accordion"}
        onClick={() => {
          setOpened(!opened);
        }}
      >
        {name}
      </button>
      <div
        onClick={() => {
          setOpened(!opened);
        }}
        class="dpanel"
        style={{
          display: opened ? "block" : "none",
          padding: "0 18px",
          cursor: "pointer",
        }}
      >
        {children}
      </div>
    </div>
  );
}

export function Row({
  name,
  value,
  literal,
  lineBreak,
}: {
  name: TranslatedString;
  value: string | VNode;
  literal?: boolean;
  lineBreak?: boolean;
}): VNode {
  const preRef = useRef<HTMLPreElement>(null);
  const tdRef = useRef<HTMLTableCellElement>(null);

  function getContent(): string {
    return preRef.current?.textContent || tdRef.current?.textContent || "";
  }

  return (
    <tr>
      <td style={{ padding: 4, width: "1%", whiteSpace: "nowrap" }}>
        <b>{name}</b>
      </td>
      {literal ? (
        <td style={{ padding: 4 }}>
          <pre
            ref={preRef}
            style={{
              whiteSpace: "pre-wrap",
              wordBreak: "break-word",
              lineBreak: lineBreak ? "anywhere" : undefined,
              padding: 4,
            }}
          >
            {value}
          </pre>
        </td>
      ) : (
        <td ref={tdRef} style={{ padding: 4 }}>
          {value}
        </td>
      )}
      <td style={{ padding: 4 }}>
        <CopyButton getContent={getContent} />
      </td>
    </tr>
  );
}

function CopyButton({ getContent }: { getContent: () => string }): VNode {
  const [copied, setCopied] = useState(false);
  function copyText(): void {
    navigator.clipboard.writeText(getContent() || "");
    setCopied(true);
  }
  useEffect(() => {
    if (copied) {
      setTimeout(() => {
        setCopied(false);
      }, 1000);
    }
  }, [copied]);

  if (!copied) {
    return (
      <button onClick={copyText}>
        <CopyIcon />
      </button>
    );
  }
  return (
    // <TooltipLeft content="Copied">
    <button disabled>
      <CopiedIcon />
    </button>
    // </TooltipLeft>
  );
}

export const CopyIcon = (): VNode => (
  <svg height="16" viewBox="0 0 16 16" width="16">
    <path
      fill-rule="evenodd"
      d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 010 1.5h-1.5a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-1.5a.75.75 0 011.5 0v1.5A1.75 1.75 0 019.25 16h-7.5A1.75 1.75 0 010 14.25v-7.5z"
    />
    <path
      fill-rule="evenodd"
      d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0114.25 11h-7.5A1.75 1.75 0 015 9.25v-7.5zm1.75-.25a.25.25 0 00-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 00.25-.25v-7.5a.25.25 0 00-.25-.25h-7.5z"
    />
  </svg>
);

export const CopiedIcon = (): VNode => (
  <svg height="16" viewBox="0 0 16 16" width="16">
    <path
      fill-rule="evenodd"
      d="M13.78 4.22a.75.75 0 010 1.06l-7.25 7.25a.75.75 0 01-1.06 0L2.22 9.28a.75.75 0 011.06-1.06L6 10.94l6.72-6.72a.75.75 0 011.06 0z"
    />
  </svg>
);

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 cancelled 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
        type="button"
        class="modal-close is-large "
        aria-label="close"
        onClick={onCancel}
      />
    </div>
  );
}
