/*
 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,
  Amounts,
  NotificationType,
  ScopeType,
  Transaction,
  WalletBalance,
} from "@gnu-taler/taler-util";
import { WalletApiOperation } from "@gnu-taler/taler-wallet-core";
import { useTranslationContext } from "@gnu-taler/web-util/browser";
import { startOfDay } from "date-fns";
import { Fragment, VNode, h } from "preact";
import { useEffect, useState } from "preact/hooks";
import { ErrorAlertView } from "../components/CurrentAlerts.js";
import { HistoryItem } from "../components/HistoryItem.js";
import { Loading } from "../components/Loading.js";
import { Time } from "../components/Time.js";
import {
  CenteredBoldText,
  CenteredText,
  DateSeparator,
  NiceSelect,
} from "../components/styled/index.js";
import { alertFromError, useAlertContext } from "../context/alert.js";
import { useBackendContext } from "../context/backend.js";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { useSettings } from "../hooks/useSettings.js";
import { Button } from "../mui/Button.js";
import { NoBalanceHelp } from "../popup/NoBalanceHelp.js";
import DownloadIcon from "../svg/download_24px.inline.svg";
import UploadIcon from "../svg/upload_24px.inline.svg";
import { TextField } from "../mui/TextField.js";
import { TextFieldHandler } from "../mui/handlers.js";

interface Props {
  currency?: string;
  search?: boolean;
  goToWalletDeposit: (currency: string) => Promise<void>;
  goToWalletManualWithdraw: (currency?: string) => Promise<void>;
}
export function HistoryPage({
  currency: _c,
  search: showSearch,
  goToWalletManualWithdraw,
  goToWalletDeposit,
}: Props): VNode {
  const { i18n } = useTranslationContext();
  const api = useBackendContext();
  const [balanceIndex, setBalanceIndex] = useState<number>(0);
  const [search, setSearch] = useState<string>();

  const [settings] = useSettings();
  const state = useAsyncAsHook(async () => {
    const b = await api.wallet.call(WalletApiOperation.GetBalances, {});
    const balance =
      b.balances.length > 0 ? b.balances[balanceIndex] : undefined;
    const tx = await api.wallet.call(WalletApiOperation.GetTransactions, {
      scopeInfo: showSearch ? undefined : balance?.scopeInfo,
      sort: "descending",
      includeRefreshes: settings.showRefeshTransactions,
      search,
    });
    return { b, tx };
  }, [balanceIndex, search]);

  useEffect(() => {
    return api.listener.onUpdateNotification(
      [NotificationType.TransactionStateTransition],
      state?.retry,
    );
  });
  const { pushAlertOnError } = useAlertContext();

  if (!state) {
    return <Loading />;
  }

  if (state.hasError) {
    return (
      <ErrorAlertView
        error={alertFromError(
          i18n,
          i18n.str`Could not load the list of transactions`,
          state,
        )}
      />
    );
  }

  if (!state.response.b.balances.length) {
    return (
      <NoBalanceHelp
        goToWalletManualWithdraw={{
          onClick: pushAlertOnError(goToWalletManualWithdraw),
        }}
      />
    );
  }

  const byDate = state.response.tx.transactions.reduce(
    (rv, x) => {
      const startDay =
        x.timestamp.t_s === "never"
          ? 0
          : startOfDay(x.timestamp.t_s * 1000).getTime();
      if (startDay) {
        if (!rv[startDay]) {
          rv[startDay] = [];
          // datesWithTransaction.push(String(startDay));
        }
        rv[startDay].push(x);
      }

      return rv;
    },
    {} as { [x: string]: Transaction[] },
  );

  if (showSearch) {
    return (
      <FilteredHistoryView
        balance={state.response.b.balances[balanceIndex]}
        search={{
          value: search ?? "",
          onInput: pushAlertOnError(async (d: string) => {
            setSearch(d);
          }),
        }}
        transactionsByDate={byDate}
      />
    );
  }

  return (
    <HistoryView
      balanceIndex={balanceIndex}
      changeBalanceIndex={(b) => setBalanceIndex(b)}
      balances={state.response.b.balances}
      goToWalletManualWithdraw={goToWalletManualWithdraw}
      goToWalletDeposit={goToWalletDeposit}
      transactionsByDate={byDate}
    />
  );
}

export function HistoryView({
  balances,
  balanceIndex,
  changeBalanceIndex,
  transactionsByDate,
  goToWalletManualWithdraw,
  goToWalletDeposit,
}: {
  balanceIndex: number;
  changeBalanceIndex: (s: number) => void;
  goToWalletDeposit: (currency: string) => Promise<void>;
  goToWalletManualWithdraw: (currency?: string) => Promise<void>;
  transactionsByDate: Record<string, Transaction[]>;
  balances: WalletBalance[];
}): VNode {
  const { i18n } = useTranslationContext();

  const balance = balances[balanceIndex];

  const available = balance
    ? Amounts.jsonifyAmount(balance.available)
    : undefined;

  const datesWithTransaction: string[] = Object.keys(transactionsByDate);

  return (
    <Fragment>
      <section>
        <div
          style={{
            display: "flex",
            flexWrap: "wrap",
            alignItems: "center",
            justifyContent: "space-between",
            marginRight: 20,
          }}
        >
          <div>
            <Button
              tooltip="Transfer money to the wallet"
              startIcon={DownloadIcon}
              variant="contained"
              onClick={() =>
                goToWalletManualWithdraw(balance.scopeInfo.currency)
              }
            >
              <i18n.Translate>Receive</i18n.Translate>
            </Button>
            {available && Amounts.isNonZero(available) && (
              <Button
                tooltip="Transfer money from the wallet"
                startIcon={UploadIcon}
                variant="outlined"
                color="primary"
                onClick={() => goToWalletDeposit(balance.scopeInfo.currency)}
              >
                <i18n.Translate>Send</i18n.Translate>
              </Button>
            )}
          </div>
          <div style={{ display: "flex", flexDirection: "column" }}>
            <h3 style={{ marginBottom: 0 }}>Balance</h3>
            <div
              style={{
                width: "fit-content",
                display: "flex",
              }}
            >
              {balances.length === 1 ? (
                <CenteredText style={{ fontSize: "x-large", margin: 8 }}>
                  {balance.scopeInfo.currency}
                </CenteredText>
              ) : (
                <NiceSelect style={{ flexDirection: "column" }}>
                  <select
                    style={{
                      fontSize: "x-large",
                    }}
                    value={balanceIndex}
                    onChange={(e) => {
                      changeBalanceIndex(
                        Number.parseInt(e.currentTarget.value, 10),
                      );
                    }}
                  >
                    {balances.map((entry, index) => {
                      return (
                        <option value={index} key={entry.scopeInfo.currency}>
                          {entry.scopeInfo.currency}
                        </option>
                      );
                    })}
                  </select>
                  <div style={{ fontSize: "small", color: "grey" }}>
                    {balance.scopeInfo.type === ScopeType.Exchange ||
                    balance.scopeInfo.type === ScopeType.Auditor
                      ? balance.scopeInfo.url
                      : undefined}
                  </div>
                </NiceSelect>
              )}
              {available && (
                <CenteredBoldText
                  style={{
                    display: "inline-block",
                    fontSize: "x-large",
                    margin: 8,
                  }}
                >
                  {Amounts.stringifyValue(available, 2)}
                </CenteredBoldText>
              )}
            </div>
          </div>
        </div>
      </section>
      {datesWithTransaction.length === 0 ? (
        <section>
          <i18n.Translate>
            Your transaction history is empty for this currency.
          </i18n.Translate>
        </section>
      ) : (
        <section>
          {datesWithTransaction.map((d, i) => {
            return (
              <Fragment key={i}>
                <DateSeparator>
                  <Time
                    timestamp={AbsoluteTime.fromMilliseconds(
                      Number.parseInt(d, 10),
                    )}
                    format="dd MMMM yyyy"
                  />
                </DateSeparator>
                {transactionsByDate[d].map((tx, i) => (
                  <HistoryItem key={i} tx={tx} />
                ))}
              </Fragment>
            );
          })}
        </section>
      )}
    </Fragment>
  );
}

export function FilteredHistoryView({
  balance,
  search,
  transactionsByDate,
}: {
  balance: WalletBalance;
  search: TextFieldHandler;
  transactionsByDate: Record<string, Transaction[]>;
}): VNode {
  const { i18n } = useTranslationContext();

  const available = balance
    ? Amounts.jsonifyAmount(balance.available)
    : undefined;

  const datesWithTransaction: string[] = Object.keys(transactionsByDate);

  return (
    <Fragment>
      <section>
        <div
          style={{
            display: "flex",
            flexWrap: "wrap",
            alignItems: "center",
            justifyContent: "space-between",
            marginRight: 20,
          }}
        >
          <TextField
            label="Search"
            variant="filled"
            error={search.error}
            required
            fullWidth
            value={search.value}
            onChange={search.onInput}
          />
        </div>
      </section>
      {datesWithTransaction.length === 0 ? (
        <section>
          <i18n.Translate>
            Your transaction history is empty for this currency.
          </i18n.Translate>
        </section>
      ) : (
        <section>
          {datesWithTransaction.map((d, i) => {
            return (
              <Fragment key={i}>
                <DateSeparator>
                  <Time
                    timestamp={AbsoluteTime.fromMilliseconds(
                      Number.parseInt(d, 10),
                    )}
                    format="dd MMMM yyyy"
                  />
                </DateSeparator>
                {transactionsByDate[d].map((tx, i) => (
                  <HistoryItem key={i} tx={tx} />
                ))}
              </Fragment>
            );
          })}
        </section>
      )}
    </Fragment>
  );
}
