/*
 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 { Amounts, AmountString, MerchantContractVersion, TalerMerchantApi } from "@gnu-taler/taler-util";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { format } from "date-fns";
import { VNode, h } from "preact";
import { StateUpdater, useState } from "preact/hooks";
import {
  FormErrors,
  FormProvider,
} from "../../../../components/form/FormProvider.js";
import { Input } from "../../../../components/form/Input.js";
import { InputCurrency } from "../../../../components/form/InputCurrency.js";
import { InputGroup } from "../../../../components/form/InputGroup.js";
import { InputSelector } from "../../../../components/form/InputSelector.js";
import { ConfirmModal } from "../../../../components/modal/index.js";
import { useSessionContext } from "../../../../context/session.js";
import {
  datetimeFormatForSettings,
  usePreference,
} from "../../../../hooks/preference.js";
import { mergeRefunds } from "../../../../utils/amount.js";
import { WithId } from "../../../../declaration.js";

type Entity = TalerMerchantApi.OrderHistoryEntry & WithId;
interface Props {
  orders: Entity[];
  onRefund: (value: Entity) => void;
  onCopyURL: (id: string) => void;
  onCreate: () => void;
  onSelect: (order: Entity) => void;
  onLoadMoreBefore?: () => void;
  onLoadMoreAfter?: () => void;
}

export function CardTable({
  orders,
  onCreate,
  onRefund,
  onCopyURL,
  onSelect,
  onLoadMoreAfter,
  onLoadMoreBefore,
}: Props): VNode {
  const [rowSelection, rowSelectionHandler] = useState<string[]>([]);

  const { i18n } = useTranslationContext();

  return (
    <div class="card has-table">
      <header class="card-header">
        <p class="card-header-title">
          <span class="icon">
            <i class="mdi mdi-cash-register" />
          </span>
          <i18n.Translate>Orders</i18n.Translate>
        </p>

        <div class="card-header-icon" aria-label="more options" />

        <div class="card-header-icon" aria-label="more options">
          <span class="has-tooltip-left" data-tooltip={i18n.str`Create order`}>
            <button class="button is-info" type="button" onClick={onCreate}>
              <span class="icon is-small">
                <i class="mdi mdi-plus mdi-36px" />
              </span>
            </button>
          </span>
        </div>
      </header>
      <div class="card-content">
        <div class="b-table has-pagination">
          <div class="table-wrapper has-mobile-cards">
            {orders.length > 0 ? (
              <Table
                instances={orders}
                onSelect={onSelect}
                onRefund={onRefund}
                onCopyURL={(o) => onCopyURL(o.id)}
                rowSelection={rowSelection}
                rowSelectionHandler={rowSelectionHandler}
                onLoadMoreAfter={onLoadMoreAfter}
                onLoadMoreBefore={onLoadMoreBefore}
              />
            ) : (
              <EmptyTable />
            )}
          </div>
        </div>
      </div>
    </div>
  );
}
interface TableProps {
  rowSelection: string[];
  instances: Entity[];
  onRefund: (id: Entity) => void;
  onCopyURL: (id: Entity) => void;
  onSelect: (id: Entity) => void;
  rowSelectionHandler: StateUpdater<string[]>;
  onLoadMoreBefore?: () => void;
  onLoadMoreAfter?: () => void;
}

function Table({
  instances,
  onSelect,
  onRefund,
  onCopyURL,
  onLoadMoreAfter,
  onLoadMoreBefore,
}: TableProps): VNode {
  const { i18n } = useTranslationContext();
  const [settings] = usePreference();
  return (
    <div class="table-container">
      {onLoadMoreBefore && (
        <button class="button is-fullwidth" onClick={onLoadMoreBefore}>
          <i18n.Translate>Load first page</i18n.Translate>
        </button>
      )}
      <table class="table is-striped is-hoverable is-fullwidth">
        <thead>
          <tr>
            <th style={{ minWidth: 100 }}>
              <i18n.Translate>Date</i18n.Translate>
            </th>
            <th style={{ minWidth: 100 }}>
              <i18n.Translate>Amount</i18n.Translate>
            </th>
            <th style={{ minWidth: 400 }}>
              <i18n.Translate>Summary</i18n.Translate>
            </th>
            <th style={{ minWidth: 50 }} />
          </tr>
        </thead>
        <tbody>
          {instances.map((i) => {
            return (
              <tr key={i.id}>
                <td
                  onClick={(): void => onSelect(i)}
                  style={{ cursor: "pointer" }}
                >
                  {i.timestamp.t_s === "never"
                    ? i18n.str`Never`
                    : format(
                        new Date(i.timestamp.t_s * 1000),
                        datetimeFormatForSettings(settings),
                      )}
                </td>
                <td
                  onClick={(): void => onSelect(i)}
                  style={{ cursor: "pointer" }}
                >
                  {i.amount}
                </td>
                <td
                  onClick={(): void => onSelect(i)}
                  style={{ cursor: "pointer" }}
                >
                  {i.summary}
                </td>
                <td class="is-actions-cell right-sticky">
                  <div class="buttons is-right">
                    {i.refundable && (
                      <button
                        class="button is-small is-danger jb-modal"
                        type="button"
                        onClick={(): void => onRefund(i)}
                      >
                        <i18n.Translate>Refund</i18n.Translate>
                      </button>
                    )}
                    {!i.paid && (
                      <button
                        class="button is-small is-info jb-modal"
                        type="button"
                        onClick={(): void => onCopyURL(i)}
                      >
                        <i18n.Translate>copy url</i18n.Translate>
                      </button>
                    )}
                  </div>
                </td>
              </tr>
            );
          })}
        </tbody>
      </table>
      {onLoadMoreAfter && (
        <button
          class="button is-fullwidth"
          data-tooltip={i18n.str`Load more orders after the last one`}
          onClick={onLoadMoreAfter}
        >
          <i18n.Translate>Load next page</i18n.Translate>
        </button>
      )}
    </div>
  );
}

function EmptyTable(): VNode {
  const { i18n } = useTranslationContext();
  return (
    <div class="content has-text-grey has-text-centered">
      <p>
        <span class="icon is-large">
          <i class="mdi mdi-magnify mdi-48px" />
        </span>
      </p>
      <p>
        <i18n.Translate>
          No orders have been found matching your query!
        </i18n.Translate>
      </p>
    </div>
  );
}

interface RefundModalProps {
  onCancel: () => void;
  onConfirm: (value: TalerMerchantApi.RefundRequest) => void;
  order: TalerMerchantApi.MerchantOrderStatusResponse;
}

export function RefundModal({
  order,
  onCancel,
  onConfirm,
}: RefundModalProps): VNode {
  type State = { mainReason?: string; description?: string; refund?: string };
  const [form, setValue] = useState<State>({});
  const [settings] = usePreference();
  const { i18n } = useTranslationContext();
  // const [errors, setErrors] = useState<FormErrors<State>>({});

  let amount: AmountString;
  if (order.order_status === "paid") {
    if (order.contract_terms.version !== undefined &&
      order.contract_terms.version !== MerchantContractVersion.V0
    ) {
      return <div>Unsupported contract version {order.contract_terms.version}</div>;
    }

    amount = order.contract_terms.amount;
  }

  const refunds = (
    order.order_status === "paid" ? order.refund_details : []
  ).reduce(mergeRefunds, []);

  const { config } = useSessionContext();
  const totalRefunded = refunds
    .map((r) => r.amount)
    .reduce(
      (p, c) => Amounts.add(p, Amounts.parseOrThrow(c)).amount,
      Amounts.zeroOfCurrency(config.currency),
    );
  const orderPrice =
    order.order_status === "paid"
      ? Amounts.parseOrThrow(amount!)
      : undefined;
  const totalRefundable = !orderPrice
    ? Amounts.zeroOfCurrency(totalRefunded.currency)
    : refunds.length
      ? Amounts.sub(orderPrice, totalRefunded).amount
      : orderPrice;

  const isRefundable = Amounts.isNonZero(totalRefundable);
  const duplicatedText = i18n.str`Duplicated`;

  const errors: FormErrors<State> = {
    mainReason: !form.mainReason ? i18n.str`Required` : undefined,
    description:
      !form.description && form.mainReason !== duplicatedText
        ? i18n.str`Required`
        : undefined,
    refund: !form.refund
      ? i18n.str`Required`
      : !Amounts.parse(form.refund)
        ? i18n.str`Invalid`
        : Amounts.cmp(totalRefundable, Amounts.parse(form.refund)!) === -1
          ? i18n.str`This value exceeds the refundable amount.`
          : undefined,
  };
  const hasErrors = Object.keys(errors).some(
    (k) => (errors as Record<string, unknown>)[k] !== undefined,
  );

  const validateAndConfirm = () => {
    try {
      if (!form.refund) return;
      onConfirm({
        refund: Amounts.stringify(
          Amounts.add(Amounts.parse(form.refund)!, totalRefunded).amount,
        ),
        reason:
          form.description === undefined
            ? form.mainReason || ""
            : `${form.mainReason}: ${form.description}`,
      });
    } catch (err) {
      console.log(err);
    }
  };

  //FIXME: parameters in the translation
  return (
    <ConfirmModal
      description="refund"
      danger
      active
      disabled={!isRefundable || hasErrors}
      onCancel={onCancel}
      onConfirm={validateAndConfirm}
    >
      {refunds.length > 0 && (
        <div class="columns">
          <div class="column is-12">
            <InputGroup
              name="asd"
              label={`${Amounts.stringify(totalRefunded)} was already refunded`}
            >
              <table class="table is-fullwidth">
                <thead>
                  <tr>
                    <th>
                      <i18n.Translate>Date</i18n.Translate>
                    </th>
                    <th>
                      <i18n.Translate>Amount</i18n.Translate>
                    </th>
                    <th>
                      <i18n.Translate>Reason</i18n.Translate>
                    </th>
                  </tr>
                </thead>
                <tbody>
                  {refunds.map((r) => {
                    return (
                      <tr key={r.timestamp.t_s}>
                        <td>
                          {r.timestamp.t_s === "never"
                            ? i18n.str`Never`
                            : format(
                                new Date(r.timestamp.t_s * 1000),
                                datetimeFormatForSettings(settings),
                              )}
                        </td>
                        <td>{r.amount}</td>
                        <td>{r.reason}</td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
            </InputGroup>
          </div>
        </div>
      )}

      {isRefundable && (
        <FormProvider<State>
          errors={errors}
          object={form}
          valueHandler={(d) => setValue(d)}
        >
          <InputCurrency<State>
            name="refund"
            label={i18n.str`Refund`}
            tooltip={i18n.str`Amount to be refunded`}
          >
            <i18n.Translate>Max refundable:</i18n.Translate>{" "}
            {Amounts.stringify(totalRefundable)}
          </InputCurrency>
          <InputSelector
            name="mainReason"
            label={i18n.str`Reason`}
            values={[
              i18n.str`Choose one...`,
              duplicatedText,
              i18n.str`Requested by the customer`,
              i18n.str`Other`,
            ]}
            tooltip={i18n.str`Why this order is being refunded`}
          />
          {form.mainReason && form.mainReason !== duplicatedText ? (
            <Input<State>
              label={i18n.str`Description`}
              name="description"
              tooltip={i18n.str`More information to give context`}
            />
          ) : undefined}
        </FormProvider>
      )}
    </ConfirmModal>
  );
}
