/*
 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 {
  AmountJson,
  Amounts,
  HostPortPath,
  MerchantContractVersion,
  TalerMerchantApi,
  stringifyRefundUri,
} from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { format, formatDistance } from "date-fns";
import { Fragment, VNode, h } from "preact";
import { useState } from "preact/hooks";
import { FormProvider } from "../../../../components/form/FormProvider.js";
import { Input } from "../../../../components/form/Input.js";
import { InputCurrency } from "../../../../components/form/InputCurrency.js";
import { InputDate } from "../../../../components/form/InputDate.js";
import { InputDuration } from "../../../../components/form/InputDuration.js";
import { InputGroup } from "../../../../components/form/InputGroup.js";
import { InputLocation } from "../../../../components/form/InputLocation.js";
import { TextField } from "../../../../components/form/TextField.js";
import { ProductList } from "../../../../components/product/ProductList.js";
import { useSessionContext } from "../../../../context/session.js";
import {
  datetimeFormatForSettings,
  usePreference,
} from "../../../../hooks/preference.js";
import { mergeRefunds } from "../../../../utils/amount.js";
import { RefundModal } from "../list/Table.js";
import { Event, Timeline } from "./Timeline.js";


const TALER_SCREEN_ID = 44;

type Entity = TalerMerchantApi.MerchantOrderStatusResponse;
type CT = TalerMerchantApi.MerchantContractTermsV0;

export interface Props {
  onBack: () => void;
  selected: Entity;
  id: string;
  onRefunded: () => void;
}

type Paid = TalerMerchantApi.CheckPaymentPaidResponse & {
  refund_taken: string;
};
type Unpaid = TalerMerchantApi.CheckPaymentUnpaidResponse;
type Claimed = TalerMerchantApi.CheckPaymentClaimedResponse;

function ContractTerms({ value }: { value: CT }) {
  const { i18n } = useTranslationContext();

  return (
    <InputGroup name="contract_terms" label={i18n.str`Contract terms`}>
      <FormProvider<CT> object={value} valueHandler={null}>
        <Input<CT>
          readonly
          name="summary"
          label={i18n.str`Summary`}
          tooltip={i18n.str`Human-readable description of the whole purchase`}
        />
        <InputCurrency<CT>
          readonly
          name="amount"
          label={i18n.str`Amount`}
          tooltip={i18n.str`Total price for the transaction`}
        />
        {value.fulfillment_url && (
          <Input<CT>
            readonly
            name="fulfillment_url"
            label={i18n.str`Fulfillment URL`}
            tooltip={i18n.str`URL for this purchase`}
          />
        )}
        <Input<CT>
          readonly
          name="max_fee"
          label={i18n.str`Max fee`}
          tooltip={i18n.str`Maximum total deposit fee accepted by the merchant for this contract`}
        />
        <InputDate<CT>
          readonly
          name="timestamp"
          label={i18n.str`Created at`}
          tooltip={i18n.str`Time when this contract was generated`}
        />
        <InputDate<CT>
          readonly
          name="refund_deadline"
          label={i18n.str`Refund deadline`}
          tooltip={i18n.str`After this deadline has passed, no refunds will be accepted.`}
        />
        <InputDate<CT>
          readonly
          name="pay_deadline"
          label={i18n.str`Payment deadline`}
          tooltip={i18n.str`After this deadline, the merchant won't accept payments for the contract`}
        />
        <InputDate<CT>
          readonly
          name="wire_transfer_deadline"
          label={i18n.str`Wire transfer deadline`}
          tooltip={i18n.str`Transfer deadline for the exchange`}
        />
        <InputDate<CT>
          readonly
          name="delivery_date"
          label={i18n.str`Delivery date`}
          tooltip={i18n.str`Time indicating when the order should be delivered`}
        />
        <InputGroup
          name="delivery_location"
          label={i18n.str`Delivery location`}
          tooltip={i18n.str`Where the order will be delivered`}
        >
          <InputLocation name="delivery_location" />
        </InputGroup>
        <InputDuration<CT>
          readonly
          name="auto_refund"
          label={i18n.str`Auto-refund delay`}
          tooltip={i18n.str`How long the wallet should try to get an automatic refund for the purchase`}
        />
        <Input<CT>
          readonly
          name="extra"
          label={i18n.str`Extra info`}
          tooltip={i18n.str`Extra data that is only interpreted by the merchant frontend`}
        />
      </FormProvider>
    </InputGroup>
  );
}

function ClaimedPage({
  id,
  order,
}: {
  id: string;
  order: TalerMerchantApi.CheckPaymentClaimedResponse;
}) {
  if (order.contract_terms.version !== undefined &&
    order.contract_terms.version !== MerchantContractVersion.V0
  ) {
    return <div>Unsupported contract version {order.contract_terms.version}</div>;
  }

  const now = new Date();
  const refundable =
    order.contract_terms.refund_deadline.t_s !== "never" &&
    now.getTime() < order.contract_terms.refund_deadline.t_s * 1000;
  const events: Event[] = [];
  if (order.contract_terms.timestamp.t_s !== "never") {
    events.push({
      when: new Date(order.contract_terms.timestamp.t_s * 1000),
      description: "order created",
      type: "start",
    });
  }
  if (order.contract_terms.pay_deadline.t_s !== "never") {
    events.push({
      when: new Date(order.contract_terms.pay_deadline.t_s * 1000),
      description: "pay deadline",
      type: "deadline",
    });
  }
  if (order.contract_terms.refund_deadline.t_s !== "never" && refundable) {
    events.push({
      when: new Date(order.contract_terms.refund_deadline.t_s * 1000),
      description: "refund deadline",
      type: "deadline",
    });
  }
  // if (order.contract_terms.wire_transfer_deadline.t_s !== "never") {
  //   events.push({
  //     when: new Date(order.contract_terms.wire_transfer_deadline.t_s * 1000),
  //     description: "wire deadline",
  //     type: "deadline",
  //   });
  // }
  if (
    order.contract_terms.delivery_date &&
    order.contract_terms.delivery_date.t_s !== "never"
  ) {
    events.push({
      when: new Date(order.contract_terms.delivery_date?.t_s * 1000),
      description: "delivery",
      type: "delivery",
    });
  }

  const [value, valueHandler] = useState<Partial<Claimed>>(order);
  const { i18n } = useTranslationContext();
  const [settings] = usePreference();

  return (
    <div>
      <section class="section">
        <div class="columns">
          <div class="column" />
          <div class="column is-10">
            <section class="hero is-hero-bar">
              <div class="hero-body">
                <div class="level">
                  <div class="level-left">
                    <div class="level-item">
                      <i18n.Translate>Order</i18n.Translate> #{id}
                      <div class="tag is-info ml-4">
                        <i18n.Translate>Claimed</i18n.Translate>
                      </div>
                    </div>
                  </div>
                </div>

                <div class="level">
                  <div class="level-left">
                    <div class="level-item">
                      <h1 class="title">{order.contract_terms.amount}</h1>
                    </div>
                  </div>
                </div>

                <div class="level">
                  <div class="level-left" style={{ maxWidth: "100%" }}>
                    <div class="level-item" style={{ maxWidth: "100%" }}>
                      <div
                        class="content"
                        style={{
                          whiteSpace: "nowrap",
                          overflow: "hidden",
                          textOverflow: "ellipsis",
                        }}
                      >
                        <p>
                          <b>
                            <i18n.Translate>Claimed at</i18n.Translate>:
                          </b>{" "}
                          {order.contract_terms.timestamp.t_s === "never"
                            ? i18n.str`Never`
                            : format(
                                new Date(
                                  order.contract_terms.timestamp.t_s * 1000,
                                ),
                                datetimeFormatForSettings(settings),
                              )}
                        </p>
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </section>

            <section class="section">
              <div class="columns">
                <div class="column is-4">
                  <div class="title">
                    <i18n.Translate>Timeline</i18n.Translate>
                  </div>
                  <Timeline events={events} />
                </div>
                <div class="column is-8">
                  <div class="title">
                    <i18n.Translate>Payment details</i18n.Translate>
                  </div>
                  <FormProvider<Claimed>
                    object={value}
                    valueHandler={valueHandler}
                  >
                    <Input
                      name="contract_terms.summary"
                      readonly
                      inputType="multiline"
                      label={i18n.str`Summary`}
                    />
                    <InputCurrency
                      name="contract_terms.amount"
                      readonly
                      label={i18n.str`Amount`}
                    />
                    <Input<Claimed>
                      name="order_status"
                      readonly
                      label={i18n.str`Order status`}
                    />
                  </FormProvider>
                </div>
              </div>
            </section>

            {order.contract_terms.products?.length ? (
              <Fragment>
                <div class="title">
                  <i18n.Translate>Product list</i18n.Translate>
                </div>
                <ProductList list={order.contract_terms.products} />
              </Fragment>
            ) : undefined}

            {value.contract_terms && (
              value.contract_terms.version === undefined
                || value.contract_terms.version === MerchantContractVersion.V0
            ) && (
              <ContractTerms value={value.contract_terms} />
            )}
          </div>
          <div class="column" />
        </div>
      </section>
    </div>
  );
}
function PaidPage({
  id,
  order,
  onRefund,
}: {
  id: string;
  order: TalerMerchantApi.CheckPaymentPaidResponse;
  onRefund: (id: string) => void;
}) {
  if (order.contract_terms.version !== undefined &&
    order.contract_terms.version !== MerchantContractVersion.V0
  ) {
    return <div>Unsupported contract version {order.contract_terms.version}</div>;
  }

  const now = new Date();
  const refundable =
    order.contract_terms.refund_deadline.t_s !== "never" &&
    now.getTime() < order.contract_terms.refund_deadline.t_s * 1000;

  const events: Event[] = [];
  if (order.contract_terms.refund_deadline.t_s !== "never" && refundable) {
    events.push({
      when: new Date(order.contract_terms.refund_deadline.t_s * 1000),
      description: "refund deadline",
      type: "deadline",
    });
  }
  if (order.contract_terms.wire_transfer_deadline.t_s !== "never") {
    events.push({
      when: new Date(order.contract_terms.wire_transfer_deadline.t_s * 1000),
      description: "wire deadline",
      type: "deadline",
    });
  }
  if (
    order.contract_terms.delivery_date &&
    order.contract_terms.delivery_date.t_s !== "never"
  ) {
    if (order.contract_terms.delivery_date)
      events.push({
        when: new Date(order.contract_terms.delivery_date?.t_s * 1000),
        description: "delivery",
        type: "delivery",
      });
  }
  order.refund_details.reduce(mergeRefunds, []).forEach((e) => {
    if (e.timestamp.t_s !== "never") {
      events.push({
        when: new Date(e.timestamp.t_s * 1000),
        description: `refund: ${e.amount}: ${e.reason}`,
        type: e.pending ? "refund" : "refund-taken",
      });
    }
  });
  const ra = !order.refunded ? undefined : Amounts.parse(order.refund_amount);
  const am = Amounts.parseOrThrow(order.contract_terms.amount);
  if (ra && Amounts.cmp(ra, am) === 1) {
    if (order.wire_details && order.wire_details.length) {
      if (order.wire_details.length > 1) {
        let last: TalerMerchantApi.TransactionWireTransfer | null = null;
        let first: TalerMerchantApi.TransactionWireTransfer | null = null;
        let total: AmountJson | null = null;

        order.wire_details.forEach((w) => {
          if (last === null || last.execution_time.t_s < w.execution_time.t_s) {
            last = w;
          }
          if (
            first === null ||
            first.execution_time.t_s > w.execution_time.t_s
          ) {
            first = w;
          }
          total =
            total === null
              ? Amounts.parseOrThrow(w.amount)
              : Amounts.add(total, Amounts.parseOrThrow(w.amount)).amount;
        });
        const last_time = last!.execution_time.t_s;
        if (last_time !== "never") {
          events.push({
            when: new Date(last_time * 1000),
            description: `wired ${Amounts.stringify(total!)}`,
            type: "wired-range",
          });
        }
        const first_time = first!.execution_time.t_s;
        if (first_time !== "never") {
          events.push({
            when: new Date(first_time * 1000),
            description: `wire transfer started...`,
            type: "wired-range",
          });
        }
      } else {
        order.wire_details.forEach((e) => {
          if (e.execution_time.t_s !== "never") {
            events.push({
              when: new Date(e.execution_time.t_s * 1000),
              description: `wired ${e.amount}`,
              type: "wired",
            });
          }
        });
      }
    }
  }

  const nextEvent = events.find((e) => {
    return e.when.getTime() > now.getTime();
  });

  const [value, valueHandler] = useState<Partial<Paid>>(order);
  const { state } = useSessionContext();

  const refundurl = stringifyRefundUri({
    merchantBaseUrl: state.backendUrl.href as HostPortPath,
    orderId: order.contract_terms.order_id,
  });
  const { i18n } = useTranslationContext();

  const amount = Amounts.parseOrThrow(order.contract_terms.amount);
  const refund_taken = order.refund_details.reduce((prev, cur) => {
    if (cur.pending) return prev;
    return Amounts.add(prev, Amounts.parseOrThrow(cur.amount)).amount;
  }, Amounts.zeroOfCurrency(amount.currency));
  value.refund_taken = Amounts.stringify(refund_taken);

  return (
    <div>
      <section class="section">
        <div class="columns">
          <div class="column" />
          <div class="column is-10">
            <section class="hero is-hero-bar">
              <div class="hero-body">
                <div class="level">
                  <div class="level-left">
                    <div class="level-item">
                      <i18n.Translate>Order</i18n.Translate> #{id}
                      <div class="tag is-success ml-4">
                        <i18n.Translate>Paid</i18n.Translate>
                      </div>
                      {order.wired ? (
                        <div class="tag is-success ml-4">
                          <i18n.Translate>Wired</i18n.Translate>
                        </div>
                      ) : null}
                      {order.refunded ? (
                        <div class="tag is-danger ml-4">
                          <i18n.Translate>Refunded</i18n.Translate>
                        </div>
                      ) : null}
                    </div>
                  </div>
                </div>
                <div class="level">
                  <div class="level-left">
                    <div class="level-item">
                      <h1 class="title">{order.contract_terms.amount}</h1>
                    </div>
                  </div>
                  <div class="level-right">
                    <div class="level-item">
                      <h1 class="title">
                        <div class="buttons">
                          <span
                            class="has-tooltip-left"
                            data-tooltip={
                              refundable
                                ? i18n.str`Refund order`
                                : i18n.str`Not refundable`
                            }
                          >
                            <button type="button" 
                              class="button is-danger"
                              disabled={!refundable}
                              onClick={() => onRefund(id)}
                            >
                              <i18n.Translate>Refund</i18n.Translate>
                            </button>
                          </span>
                        </div>
                      </h1>
                    </div>
                  </div>
                </div>

                <div class="level">
                  <div class="level-left" style={{ maxWidth: "100%" }}>
                    <div class="level-item" style={{ maxWidth: "100%" }}>
                      <div
                        class="content"
                        style={{
                          whiteSpace: "nowrap",
                          overflow: "hidden",
                          textOverflow: "ellipsis",
                        }}
                      >
                        {nextEvent && (
                          <p>
                            <i18n.Translate>Next event in </i18n.Translate>{" "}
                            {formatDistance(
                              nextEvent.when,
                              new Date(),
                              // "yyyy/MM/dd HH:mm:ss",
                            )}
                          </p>
                        )}
                      </div>
                    </div>
                  </div>
                </div>
              </div>
            </section>

            <section class="section">
              <div class="columns">
                <div class="column is-4">
                  <div class="title">
                    <i18n.Translate>Timeline</i18n.Translate>
                  </div>
                  <Timeline events={events} />
                </div>
                <div class="column is-8">
                  <div class="title">
                    <i18n.Translate>Payment details</i18n.Translate>
                  </div>
                  <FormProvider<Paid>
                    object={value}
                    valueHandler={valueHandler}
                  >
                    {/* <InputCurrency<Paid> name="deposit_total" readonly label={i18n.str`Deposit total`} /> */}
                    {order.refunded && (
                      <InputCurrency<Paid>
                        name="refund_amount"
                        readonly
                        label={i18n.str`Refunded amount`}
                      />
                    )}
                    {order.refunded && (
                      <InputCurrency<Paid>
                        name="refund_taken"
                        readonly
                        label={i18n.str`Refund taken`}
                      />
                    )}
                    <Input<Paid>
                      name="order_status"
                      readonly
                      label={i18n.str`Order status`}
                    />
                    <TextField<Paid>
                      name="order_status_url"
                      label={i18n.str`Status URL`}
                    >
                      <a
                        target="_blank"
                        rel="noreferrer"
                        href={order.order_status_url}
                      >
                        {order.order_status_url}
                      </a>
                    </TextField>
                    {order.refunded && (
                      <TextField<Paid>
                        name="order_status_url"
                        label={i18n.str`Refund URI`}
                      >
                        <a target="_blank" rel="noreferrer" href={refundurl}>
                          {refundurl}
                        </a>
                      </TextField>
                    )}
                  </FormProvider>
                </div>
              </div>
            </section>

            {order.contract_terms.products?.length ? (
              <Fragment>
                <div class="title">
                  <i18n.Translate>Product list</i18n.Translate>
                </div>
                <ProductList list={order.contract_terms.products} />
              </Fragment>
            ) : undefined}

            {value.contract_terms && (
              value.contract_terms.version === undefined
                || value.contract_terms.version === MerchantContractVersion.V0
            ) && (
              <ContractTerms value={value.contract_terms} />
            )}
          </div>
          <div class="column" />
        </div>
      </section>
    </div>
  );
}

function UnpaidPage({
  id,
  order,
}: {
  id: string;
  order: TalerMerchantApi.CheckPaymentUnpaidResponse;
}) {
  const [value, valueHandler] = useState<Partial<Unpaid>>(order);
  const { i18n } = useTranslationContext();
  const [settings] = usePreference();
  return (
    <div>
      <section class="hero is-hero-bar">
        <div class="hero-body">
          <div class="level">
            <div class="level-left">
              <div class="level-item">
                <h1 class="title">
                  <i18n.Translate>Order</i18n.Translate> #{id}
                </h1>
              </div>
              <div class="tag is-dark">
                <i18n.Translate>Unpaid</i18n.Translate>
              </div>
            </div>
          </div>

          <div class="level">
            <div class="level-left" style={{ maxWidth: "100%" }}>
              <div class="level-item" style={{ maxWidth: "100%" }}>
                <div
                  class="content"
                  style={{
                    whiteSpace: "nowrap",
                    overflow: "hidden",
                    textOverflow: "ellipsis",
                  }}
                >
                  <p>
                    <b>
                      <i18n.Translate>Pay at</i18n.Translate>:
                    </b>{" "}
                    <a
                      href={order.order_status_url}
                      rel="nofollow"
                      target="new"
                    >
                      {order.order_status_url}
                    </a>
                  </p>
                  <p>
                    <b>
                      <i18n.Translate>Created at</i18n.Translate>:
                    </b>{" "}
                    {order.creation_time.t_s === "never"
                      ? i18n.str`Never`
                      : format(
                          new Date(order.creation_time.t_s * 1000),
                          datetimeFormatForSettings(settings),
                        )}
                  </p>
                </div>
              </div>
            </div>
          </div>
        </div>
      </section>

      <section class="section is-main-section">
        <div class="columns">
          <div class="column" />
          <div class="column is-four-fifths">
            <FormProvider<Unpaid> object={value} valueHandler={valueHandler}>
              <Input<Unpaid>
                readonly
                name="summary"
                label={i18n.str`Summary`}
                tooltip={i18n.str`Human-readable description of the whole purchase`}
              />
              <InputCurrency<Unpaid>
                readonly
                name="total_amount"
                label={i18n.str`Amount`}
                tooltip={i18n.str`Total price for the transaction`}
              />
              <Input<Unpaid>
                name="order_status"
                readonly
                label={i18n.str`Order status`}
              />
              <Input<Unpaid>
                name="order_status_url"
                readonly
                label={i18n.str`Order status URL`}
              />
              <TextField<Unpaid>
                name="taler_pay_uri"
                label={i18n.str`Payment URI`}
              >
                <a target="_blank" rel="noreferrer" href={value.taler_pay_uri}>
                  {value.taler_pay_uri}
                </a>
              </TextField>
            </FormProvider>
          </div>
          <div class="column" />
        </div>
      </section>
    </div>
  );
}

export function DetailPage({ id, selected, onRefunded, onBack }: Props): VNode {
  const [showRefund, setShowRefund] = useState<string | undefined>(undefined);
  const { i18n } = useTranslationContext();
  const DetailByStatus = function () {
    switch (selected.order_status) {
      case "claimed":
        return <ClaimedPage id={id} order={selected} />;
      case "paid":
        return <PaidPage id={id} order={selected} onRefund={setShowRefund} />;
      case "unpaid":
        return <UnpaidPage id={id} order={selected} />;
      default:
        return (
          <div>
            <i18n.Translate>
              Unknown order status. This is an error, please contact the
              administrator.
            </i18n.Translate>
          </div>
        );
    }
  };

  return (
    <Fragment>
      {DetailByStatus()}
      {showRefund && (
        <RefundModal
          order={selected}
          id={id}
          onCancel={() => setShowRefund(undefined)}
          onConfirmed={() => {
            onRefunded();
            setShowRefund(undefined);
          }}
        />
      )}
      <div class="columns">
        <div class="column" />
        <div class="column is-four-fifths">
          <div class="buttons is-right mt-5">
            <button class="button" onClick={onBack}>
              <i18n.Translate>Back</i18n.Translate>
            </button>
          </div>
        </div>
        <div class="column" />
      </div>
    </Fragment>
  );
}
