/*
 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 {
  AbsoluteTime,
  Duration,
  Location,
  MerchantContractTerms,
  MerchantContractVersion,
  TransactionIdStr,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { styled } from "@linaria/react";
import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks";
import { Loading } from "../components/Loading.js";
import { Modal } from "../components/Modal.js";
import { Time } from "../components/Time.js";
import { alertFromError, useAlertContext } from "../context/alert.js";
import { useBackendContext } from "../context/backend.js";
import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { ButtonHandler } from "../mui/handlers.js";
import { compose, StateViewMap } from "../utils/index.js";
import { Amount } from "./Amount.js";
import { ErrorAlertView } from "./CurrentAlerts.js";
import { Link } from "./styled/index.js";

const ContractTermsTable = styled.table`
  width: 100%;
  border-spacing: 0px;
  & > tr > td {
    padding: 5px;
  }
  & > tr > td:nth-child(2n) {
    text-align: right;
    overflow-wrap: anywhere;
  }
  & > tr:nth-child(2n) {
    background: #ebebeb;
  }
`;

function locationAsText(l: Location | undefined): VNode {
  if (!l) return <span />;
  const lines = [
    ...(l.address_lines || []).map((e) => [e]),
    [l.town_location, l.town, l.street],
    [l.building_name, l.building_number],
    [l.country, l.country_subdivision],
    [l.district, l.post_code],
  ];
  //remove all missing value
  //then remove all empty lines
  const curated = lines
    .map((l) => l.filter((v) => !!v))
    .filter((l) => l.length > 0);
  return (
    <span>
      {curated.map((c, i) => (
        <div key={i}>{c.join(",")}</div>
      ))}
    </span>
  );
}

type State = States.Loading | States.Error | States.Hidden | States.Show;

export namespace States {
  export interface Loading {
    status: "loading";
    hideHandler: ButtonHandler;
  }
  export interface Error {
    status: "error";
    transactionId: string;
    error: HookError;
    hideHandler: ButtonHandler;
  }
  export interface Hidden {
    status: "hidden";
    showHandler: ButtonHandler;
  }
  export interface Show {
    status: "show";
    hideHandler: ButtonHandler;
    contractTerms: MerchantContractTerms;
  }
}

interface Props {
  transactionId: TransactionIdStr;
}

function useComponentState({ transactionId }: Props): State {
  const api = useBackendContext();
  const [show, setShow] = useState(false);
  const { pushAlertOnError } = useAlertContext();
  const hook = useAsyncAsHook(async () => {
    if (!show) return undefined;
    return await api.wallet.call(WalletApiOperation.GetTransactionById, {
      transactionId,
      includeContractTerms: true,
    });
  }, [show]);

  const hideHandler = {
    onClick: pushAlertOnError(async () => setShow(false)),
  };
  const showHandler = {
    onClick: pushAlertOnError(async () => setShow(true)),
  };
  if (!show) {
    return {
      status: "hidden",
      showHandler,
    };
  }
  if (!hook) return { status: "loading", hideHandler };
  if (hook.hasError)
    return { status: "error", transactionId, error: hook, hideHandler };
  if (!hook.response) return { status: "loading", hideHandler };
  if (
    !("contractTerms" in hook.response) ||
    hook.response.contractTerms == null
  ) {
    throw Error();
  }
  return {
    status: "show",
    contractTerms: hook.response.contractTerms,
    hideHandler,
  };
}

const viewMapping: StateViewMap<State> = {
  loading: LoadingView,
  error: ErrorView,
  show: ShowView,
  hidden: HiddenView,
};

export const ShowFullContractTermPopup = compose(
  "ShowFullContractTermPopup",
  (p: Props) => useComponentState(p),
  viewMapping,
);

export function LoadingView({ hideHandler }: States.Loading): VNode {
  return (
    <Modal title="Full detail" onClose={hideHandler}>
      <Loading />
    </Modal>
  );
}

export function ErrorView({
  hideHandler,
  error,
  transactionId,
}: States.Error): VNode {
  const { i18n } = useTranslationContext();
  return (
    <Modal title="Full detail" onClose={hideHandler}>
      <ErrorAlertView
        error={alertFromError(
          i18n,
          i18n.str`Could not load purchase proposal details`,
          error,
          { transactionId },
        )}
      />
    </Modal>
  );
}

export function HiddenView({ showHandler }: States.Hidden): VNode {
  return <Link onClick={showHandler?.onClick}>Show full details</Link>;
}

export function ShowView({ contractTerms, hideHandler }: States.Show): VNode {
  const createdAt = AbsoluteTime.fromProtocolTimestamp(contractTerms.timestamp);
  const { i18n } = useTranslationContext();

  return (
    <Modal title="Full detail" onClose={hideHandler}>
      <div style={{ overflowY: "auto", height: "95%", padding: 5 }}>
        <ContractTermsTable>
          <tr>
            <td>
              <i18n.Translate>Order Id</i18n.Translate>
            </td>
            <td>{contractTerms.order_id}</td>
          </tr>
          <tr>
            <td>
              <i18n.Translate>Summary</i18n.Translate>
            </td>
            <td>{contractTerms.summary}</td>
          </tr>
          {"amount" in contractTerms ?
            <tr>
              <td>
                <i18n.Translate>Amount</i18n.Translate>
              </td>
              <td>
                <Amount value={contractTerms.amount} />
              </td>
            </tr>
            : undefined}
          {"choices" in contractTerms ?
            <tr>
              <td>
                <i18n.Translate>Choices</i18n.Translate>
              </td>
              <td>
                <i18n.Translate>This purchase have {contractTerms.choices.length} choices</i18n.Translate>
              </td>
            </tr> : undefined}
          <tr>
            <td>
              <i18n.Translate>Merchant name</i18n.Translate>
            </td>
            <td>{contractTerms.merchant.name}</td>
          </tr>
          <tr>
            <td>
              <i18n.Translate>Merchant jurisdiction</i18n.Translate>
            </td>
            <td>{locationAsText(contractTerms.merchant.jurisdiction)}</td>
          </tr>
          <tr>
            <td>
              <i18n.Translate>Merchant address</i18n.Translate>
            </td>
            <td>{locationAsText(contractTerms.merchant.address)}</td>
          </tr>
          <tr>
            <td>
              <i18n.Translate>Merchant logo</i18n.Translate>
            </td>
            <td>
              <div>
                <img
                  src={contractTerms.merchant.logo}
                  style={{ width: 64, height: 64, margin: 4 }}
                />
              </div>
            </td>
          </tr>
          <tr>
            <td>
              <i18n.Translate>Merchant website</i18n.Translate>
            </td>
            <td>{contractTerms.merchant.website}</td>
          </tr>
          <tr>
            <td>
              <i18n.Translate>Merchant email</i18n.Translate>
            </td>
            <td>{contractTerms.merchant.email}</td>
          </tr>
          <tr>
            <td>
              <i18n.Translate>Merchant public key</i18n.Translate>
            </td>
            <td>
              <span title={contractTerms.merchant_pub}>
                {contractTerms.merchant_pub.substring(0, 6)}...
              </span>
            </td>
          </tr>
          <tr>
            <td>
              <i18n.Translate>Created at</i18n.Translate>
            </td>
            <td>
              {contractTerms.timestamp && (
                <Time
                  timestamp={AbsoluteTime.fromProtocolTimestamp(
                    contractTerms.timestamp,
                  )}
                  format="dd MMMM yyyy, HH:mm"
                />
              )}
            </td>
          </tr>
          <tr>
            <td>
              <i18n.Translate>Refund deadline</i18n.Translate>
            </td>
            <td>
              {
                <Time
                  timestamp={AbsoluteTime.fromProtocolTimestamp(
                    contractTerms.refund_deadline,
                  )}
                  format="dd MMMM yyyy, HH:mm"
                />
              }
            </td>
          </tr>
          <tr>
            <td>
              <i18n.Translate>Auto refund</i18n.Translate>
            </td>
            <td>
              {
                <Time
                  timestamp={AbsoluteTime.addDuration(
                    createdAt,
                    !contractTerms.auto_refund
                      ? Duration.getZero()
                      : Duration.fromTalerProtocolDuration(
                        contractTerms.auto_refund,
                      ),
                  )}
                  format="dd MMMM yyyy, HH:mm"
                />
              }
            </td>
          </tr>
          <tr>
            <td>
              <i18n.Translate>Pay deadline</i18n.Translate>
            </td>
            <td>
              {
                <Time
                  timestamp={AbsoluteTime.fromProtocolTimestamp(
                    contractTerms.pay_deadline,
                  )}
                  format="dd MMMM yyyy, HH:mm"
                />
              }
            </td>
          </tr>
          <tr>
            <td>
              <i18n.Translate>Fulfillment URL</i18n.Translate>
            </td>
            <td>{contractTerms.fulfillment_url}</td>
          </tr>
          <tr>
            <td>
              <i18n.Translate>Fulfillment message</i18n.Translate>
            </td>
            <td>{contractTerms.fulfillment_message}</td>
          </tr>
          {/* <tr>
          <td>Public reorder URL</td>
          <td>{contractTerms.public_reorder_url}</td>
        </tr> */}
          {"max_fee" in contractTerms ?
            <tr>
              <td>
                <i18n.Translate>Max deposit fee</i18n.Translate>
              </td>
              <td>
                <Amount value={contractTerms.max_fee} />
              </td>
            </tr>
            : undefined}
          {/* <tr>
          <td>Extra</td>
          <td>
            <pre>{contractTerms.}</pre>
          </td>
        </tr> */}
          <tr>
            <td>
              <i18n.Translate>Exchanges</i18n.Translate>
            </td>
            <td>
              {(contractTerms.exchanges || []).map((e) => (
                <Fragment key={e.master_pub}>
                  <a href={e.url} title={e.master_pub}>
                    {e.master_pub.substring(0, 6)}...
                  </a>
                  &nbsp;
                </Fragment>
              ))}
            </td>
          </tr>
        </ContractTermsTable>
      </div>
    </Modal>
  );
}
