/*
 This file is part of GNU Taler
 (C) 2022-2025 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,
  AmountJson,
  Amounts,
  assertUnreachable,
  encodeCrock,
  hashNormalizedPaytoUri,
  HttpStatusCode,
  PaytoHash,
} from "@gnu-taler/taler-util";
import {
  Attention,
  ErrorLoading,
  FormDesign,
  FormUI,
  Loading,
  Pagination,
  RenderAmount,
  RouteDefinition,
  Time,
  useExchangeApiContext,
  useForm,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { format } from "date-fns";
import { Fragment, h, VNode } from "preact";
import { useTransferList } from "../hooks/transfers.js";
import { Profile } from "./Profile.js";

export function Transfers({
  routeToAccountById,
  account,
}: {
  routeToAccountById: RouteDefinition<{ cid: string }>;
  account?: PaytoHash;
}): VNode {
  const { i18n, dateLocale } = useTranslationContext();
  const { config } = useExchangeApiContext();

  type FormType = {
    direction: "credit" | "debit" | "kyc-auth";
    threshold: AmountJson;
  };
  const design: FormDesign = {
    type: "single-column",
    fields: [
      {
        type: "choiceHorizontal",
        id: "direction",
        label: i18n.str`Direction`,
        choices: [
          {
            label: i18n.str`Credit`,
            value: "credit",
          },
          {
            label: i18n.str`Debit`,
            value: "debit",
          },
          {
            label: i18n.str`KYC Auth`,
            value: "kyc-auth",
          },
        ],
      },
      {
        type: "amount",
        id: "threshold",
        label: i18n.str`Minimum amount`,
        help: i18n.str`All amounts below the given threshold will be filtered.`,
        currency: config.config.currency,
      },
    ],
  };

  const form = useForm<FormType>(design, { direction: "credit" });

  const direction = form.status.result.direction as
    | FormType["direction"]
    | undefined;

  const threshold = form.status.result.threshold;

  const resp = useTransferList({
    direction,
    threshold,
    account,
  });

  const theMoneyIsDirectedToTheExchange = direction === "debit";
  const searchingForAnAccount = account !== undefined;
  if (!resp) {
    return <Loading />;
  }
  if (resp instanceof Error) {
    return <ErrorLoading error={resp} />;
  }
  if (resp.type === "fail") {
    switch (resp.case) {
      case HttpStatusCode.Forbidden:
        return (
          <Fragment>
            <Attention type="danger" title={i18n.str`Operation denied`}>
              <i18n.Translate>
                This account signature is invalid, contact administrator or
                create a new one.
              </i18n.Translate>
            </Attention>
            <Profile />
          </Fragment>
        );
      case HttpStatusCode.NotFound:
        return (
          <Fragment>
            <Attention type="danger" title={i18n.str`Operation denied`}>
              <i18n.Translate>
                The designated AML account is not known, contact administrator
                or create a new one.
              </i18n.Translate>
            </Attention>
            <Profile />
          </Fragment>
        );
      case HttpStatusCode.Conflict:
        return (
          <Fragment>
            <Attention type="danger" title={i18n.str`Operation denied`}>
              <i18n.Translate>
                The designated AML account is not enabled, contact administrator
                or create a new one.
              </i18n.Translate>
            </Attention>
            <Profile />
          </Fragment>
        );
      default:
        assertUnreachable(resp);
    }
  }
  const transactions = resp.body;

  if (!direction) {
    return (
      <>
        <FormUI design={design} model={form.model} />
        <Attention type="low" title={i18n.str`Select transaction type`}>
          <i18n.Translate>
            Please select a transaction type to show transactions.
          </i18n.Translate>
        </Attention>
      </>
    );
  }

  if (!transactions.length) {
    return (
      <div class="px-4 mt-8">
        <div class="sm:flex sm:items-center">
          <div class="sm:flex-auto">
            {searchingForAnAccount ? (
              <h1 class="text-base font-semibold leading-6 text-gray-900">
                <i18n.Translate>Transfers history for account</i18n.Translate>{" "}
                <a
                  href={routeToAccountById.url({
                    cid: account,
                  })}
                  class="text-indigo-600 hover:text-indigo-900 font-mono"
                >
                  {account}
                </a>
              </h1>
            ) : (
              <h1 class="text-base font-semibold leading-6 text-gray-900">
                <i18n.Translate>Transfers history</i18n.Translate>
              </h1>
            )}
          </div>
        </div>
        <FormUI design={design} model={form.model} />

        <Attention type="low" title={i18n.str`No transfers yet.`}>
          {searchingForAnAccount ? (
            <i18n.Translate>
              There are no transfer for this account.
            </i18n.Translate>
          ) : (
            <i18n.Translate>
              There are no transfer reported to the exchange with the current
              threshold.
            </i18n.Translate>
          )}
        </Attention>
      </div>
    );
  }

  const txByDate = transactions.reduce(
    (prev, cur) => {
      const d =
        cur.execution_time.t_s === "never"
          ? ""
          : format(cur.execution_time.t_s * 1000, "dd/MM/yyyy", {
              locale: dateLocale,
            });
      if (!prev[d]) {
        prev[d] = [];
      }
      prev[d].push(cur);
      return prev;
    },
    {} as Record<string, typeof transactions>,
  );

  return (
    <div class="px-4 mt-8">
      <div class="sm:flex sm:items-center">
        <div class="sm:flex-auto">
          {searchingForAnAccount ? (
            <h1 class="text-base font-semibold leading-6 text-gray-900">
              <i18n.Translate>Transfers history for account</i18n.Translate>{" "}
              <a
                href={routeToAccountById.url({
                  cid: account,
                })}
                class="text-indigo-600 hover:text-indigo-900 font-mono"
              >
                {account}
              </a>
            </h1>
          ) : (
            <h1 class="text-base font-semibold leading-6 text-gray-900">
              <i18n.Translate>Transfers history</i18n.Translate>
            </h1>
          )}
        </div>
      </div>
      <FormUI design={design} model={form.model} />

      <div class="-mx-4 mt-5 ring-1 ring-gray-300 sm:mx-0 rounded-lg min-w-fit bg-white">
        <table class="min-w-full divide-y divide-gray-300">
          <thead>
            <tr>
              <th
                scope="col"
                class="pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 "
              >{i18n.str`Date`}</th>
              <th
                scope="col"
                class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 "
              >{i18n.str`Amount`}</th>
              <th
                scope="col"
                class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 "
              >{i18n.str`Account`}</th>
              {/* <th
                scope="col"
                class="hidden sm:table-cell pl-2 py-3.5 text-left text-sm font-semibold text-gray-900 "
              >{i18n.str`Subject`}</th> */}
            </tr>
          </thead>
          <tbody>
            {Object.entries(txByDate).map(([date, txs], idx) => {
              return (
                <Fragment key={idx}>
                  <tr class="border-t border-gray-200">
                    <th
                      colSpan={4}
                      scope="colgroup"
                      class="bg-gray-50 py-2 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-3"
                    >
                      {date}
                    </th>
                  </tr>
                  {txs.map((item) => {
                    const hashPayto = encodeCrock(
                      hashNormalizedPaytoUri(item.payto_uri),
                    );

                    return (
                      <tr
                        key={idx}
                        class="border-b border-gray-200 last:border-none"
                      >
                        <td class="relative py-2 pl-2 pr-2 text-sm ">
                          <div class="font-medium text-gray-900">
                            <Time
                              format="HH:mm:ss"
                              timestamp={AbsoluteTime.fromProtocolTimestamp(
                                item.execution_time,
                              )}
                              // relative={Duration.fromSpec({ days: 1 })}
                            />
                          </div>
                          <dl class="font-normal sm:hidden">
                            <dt class="sr-only sm:hidden">
                              <i18n.Translate>Amount</i18n.Translate>
                            </dt>
                            <dd class="mt-1 truncate text-gray-700">
                              {theMoneyIsDirectedToTheExchange
                                ? i18n.str`sent`
                                : i18n.str`received`}{" "}
                              {item.amount ? (
                                <span
                                  data-negative={
                                    theMoneyIsDirectedToTheExchange
                                      ? "true"
                                      : "false"
                                  }
                                  class="data-[negative=false]:text-green-600 data-[negative=true]:text-red-600"
                                >
                                  <RenderAmount
                                    value={Amounts.parseOrThrow(item.amount)}
                                    spec={config.config.currency_specification}
                                  />
                                </span>
                              ) : (
                                <span class="text-grey-500">
                                  &lt;{i18n.str`Invalid value`}&gt;
                                </span>
                              )}
                            </dd>

                            <dt class="sr-only sm:hidden">
                              <i18n.Translate>Account</i18n.Translate>
                            </dt>
                            <dd class="mt-1 truncate text-gray-500 sm:hidden">
                              <a
                                href={routeToAccountById.url({
                                  cid: hashPayto,
                                })}
                                class="text-indigo-600 hover:text-indigo-900 font-mono"
                              >
                                {theMoneyIsDirectedToTheExchange
                                  ? i18n.str`to`
                                  : i18n.str`from`}{" "}
                                {item.payto_uri}
                              </a>
                            </dd>
                            {/* <dd class="mt-1 text-gray-500 sm:hidden">
                              <pre class="break-words w-56 whitespace-break-spaces p-2 rounded-md mx-auto my-2 bg-gray-100">
                                {item.subject}
                              </pre>
                            </dd> */}
                          </dl>
                        </td>
                        <td
                          data-negative={
                            theMoneyIsDirectedToTheExchange ? "true" : "false"
                          }
                          class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500 "
                        >
                          {item.amount ? (
                            <RenderAmount
                              value={Amounts.parseOrThrow(item.amount)}
                              negative={theMoneyIsDirectedToTheExchange}
                              withColor
                              spec={config.config.currency_specification}
                            />
                          ) : (
                            <span class="text-grey-500">
                              &lt;
                              {i18n.str`Invalid value`}&gt;
                            </span>
                          )}
                        </td>
                        <td class="hidden sm:table-cell px-3 py-3.5 text-sm text-gray-500">
                          <a
                            href={routeToAccountById.url({
                              cid: hashPayto,
                            })}
                            class="text-indigo-600 hover:text-indigo-900 font-mono"
                          >
                            {item.payto_uri}
                          </a>
                        </td>
                      </tr>
                    );
                  })}
                </Fragment>
              );
            })}
          </tbody>
        </table>

        <Pagination onFirstPage={resp.loadFirst} onNext={resp.loadNext} />
      </div>
    </div>
  );
}
