import {
  ButtonBetterBulma,
  LocalNotificationBannerBulma,
  RenderAmountBulma,
  useLocalNotificationBetter,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { Fragment, h, VNode } from "preact";
import {
  datetimeFormatForPreferences,
  usePreference,
} from "../../../../hooks/preference.js";
import { Row } from "../../../../components/modal/index.js";
import {
  AmountJson,
  Amounts,
  assertUnreachable,
  ExchangeTransferReconciliationDetails,
  ExpectedTransferDetails,
  ExpectedTransferEntry,
  HttpStatusCode,
  TalerError,
} from "@gnu-taler/taler-util";
import { useSessionContext } from "../../../../context/session.js";
import { format } from "date-fns";
import {
  useInstanceIncomingTransferDetails,
  useInstanceIncomingTransfers,
} from "../../../../hooks/transfer.js";
import { Loading } from "../../../../components/exception/loading.js";
import { ErrorLoadingMerchant } from "../../../../components/ErrorLoadingMerchant.js";
import { LoginPage } from "../../../login/index.js";
import { NotFoundPageOrAdminCreate } from "../../../notfound/index.js";
import { useOrderDetails } from "../../../../hooks/order.js";
import { getOrderAmountAndMaxDepositFee } from "../../orders/details/DetailPage.js";

export default function DetailsPage({
  tid,
  onBack,
  onOrderDetails,
}: {
  onOrderDetails: (id: string) => void;
  tid: string;
  onBack: () => void;
}): VNode {
  const resultDetails = useInstanceIncomingTransferDetails(
    Number.parseInt(tid),
  );
  if (!resultDetails) return <Loading />;
  if (resultDetails instanceof TalerError) {
    return <ErrorLoadingMerchant error={resultDetails} />;
  }
  if (resultDetails.details.type === "fail") {
    switch (resultDetails.details.case) {
      case HttpStatusCode.NotFound:
        return <NotFoundPageOrAdminCreate />;
      case HttpStatusCode.Conflict:
        return <NotFoundPageOrAdminCreate />;
      case HttpStatusCode.NotFound: {
        return <NotFoundPageOrAdminCreate />;
      }
      default: {
        assertUnreachable(resultDetails.details);
      }
    }
  }
  if (resultDetails.info.type === "fail") {
    switch (resultDetails.info.case) {
      case HttpStatusCode.NotFound:
        return <NotFoundPageOrAdminCreate />;
      case HttpStatusCode.Unauthorized:
        return <NotFoundPageOrAdminCreate />;
      default: {
        assertUnreachable(resultDetails.info);
      }
    }
  }
  return (
    <div>
      <section class="section">
        <div class="columns">
          <div class="column" />
          <div class="column is-four-fifths">
            <DetailsPageInternal
              transfer={resultDetails.info.body.incoming[0]}
              info={resultDetails.details.body}
              onBack={onBack}
              onOrderDetails={onOrderDetails}
            />
          </div>
          <div class="column" />
        </div>
      </section>
    </div>
  );
}

function DetailsPageInternal({
  transfer: wt,
  info: details,
  onBack,
  onOrderDetails,
}: {
  onOrderDetails: (id: string) => void;
  transfer: ExpectedTransferEntry;
  info: ExpectedTransferDetails;
  onBack: () => void;
}): VNode {
  const { i18n } = useTranslationContext();
  const { state: session, lib, config } = useSessionContext();
  const [preferences] = usePreference();

  const [notification, safeFunctionHandler] = useLocalNotificationBetter();
  const confirm = safeFunctionHandler(
    i18n.str`inform wire transfer`,
    lib.instance.informWireTransfer.bind(lib.instance),
    !session.token || !wt || wt.confirmed
      ? undefined
      : [
          session.token,
          {
            credit_amount: wt.expected_credit_amount!,
            exchange_url: wt.exchange_url,
            payto_uri: wt.payto_uri,
            wtid: wt.wtid,
          },
        ],
  );
  confirm.onSuccess = () => {
    onBack();
  };
  confirm.onFail = (fail) => {
    switch (fail.case) {
      case HttpStatusCode.Unauthorized:
        return i18n.str`Unauthorized.`;
      case HttpStatusCode.NotFound:
        return i18n.str`Not found.`;
      case HttpStatusCode.Conflict:
        return i18n.str`Wire transfer already confirmed.`;
      default:
        assertUnreachable(fail);
    }
  };

  const zero = Amounts.zeroOfAmount(
    Amounts.parseOrThrow(wt.expected_credit_amount!),
  );
  let totalPaid = zero;
  let totalFee = zero;
  const wireFee = !details.wire_fee
    ? undefined
    : Amounts.parseOrThrow(details.wire_fee);

  const merged: Record<string, { paid: AmountJson; fee: AmountJson }> = {};
  for (const detail of details.reconciliation_details ?? []) {
    const paid = Amounts.parseOrThrow(detail.remaining_deposit);
    const fee = Amounts.parseOrThrow(detail.deposit_fee);
    totalPaid = Amounts.add(totalPaid, paid).amount;
    totalFee = Amounts.add(totalFee, fee).amount;
    if (!merged[detail.order_id]) {
      merged[detail.order_id] = {
        fee: zero,
        paid: zero,
      };
    }
    merged[detail.order_id] = {
      fee: Amounts.add(merged[detail.order_id].fee, fee).amount,
      paid: Amounts.add(merged[detail.order_id].paid, paid).amount,
    };
  }
  const orders = Object.keys(merged);
  return (
    <Fragment>
      <LocalNotificationBannerBulma notification={notification} />
      {!wt.confirmed ? (
        <p>
          <i18n.Translate>
            The wire transfer has been sent and should be in your bank account
            in any time. You can manually confirm the reception using the
            information below.
          </i18n.Translate>
        </p>
      ) : (
        <p>
          <i18n.Translate>The wire transfer has been confirmed.</i18n.Translate>
        </p>
      )}
      <div class="card has-table">
        <header class="card-header">
          <p class="card-header-title">
            <i class="icon mdi mdi-star" />
            <i18n.Translate>Transaction details</i18n.Translate>
          </p>
        </header>

        <div class="card-content">
          <div class="b-table has-pagination">
            <div class="table-wrapper has-mobile-cards">
              <div class="table-container">
                <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
                  <thead></thead>
                  <tbody>
                    <Row
                      name={i18n.str`Amount`}
                      value={
                        <RenderAmountBulma
                          value={Amounts.parseOrThrow(
                            wt.expected_credit_amount!,
                          )}
                          specMap={config.currencies}
                        />
                      }
                    />
                    {wt.execution_time && wt.execution_time.t_s !== "never" ? (
                      <Row
                        name={i18n.str`Date`}
                        value={format(
                          wt.execution_time.t_s * 1000,
                          datetimeFormatForPreferences(preferences),
                        )}
                      />
                    ) : undefined}
                    <Row name={i18n.str`Transfer ID`} value={wt.wtid} literal />
                  </tbody>
                </table>
              </div>
            </div>
          </div>
        </div>
      </div>

      <div class="buttons is-right mt-5">
        <button class="button" type="button" onClick={onBack}>
          <i18n.Translate>Cancel</i18n.Translate>
        </button>

        <ButtonBetterBulma type="submit" onClick={confirm}>
          <i18n.Translate>I have received the wire transfer</i18n.Translate>
        </ButtonBetterBulma>
      </div>

      {!orders.length ? undefined : (
        <div class="card has-table">
          <header class="card-header">
            <p class="card-header-title">
              <i class="icon mdi mdi-star" />
              <i18n.Translate>Orders in this wire transfer</i18n.Translate>
            </p>
          </header>
          <div class="card-content">
            <div class="b-table has-pagination">
              <div class="table-wrapper has-mobile-cards">
                {orders.length > 0 ? (
                  <div class="table-container">
                    <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
                      <thead>
                        <tr>
                          <th>
                            <i18n.Translate>Summary</i18n.Translate>
                          </th>
                          <th>
                            <i18n.Translate>Amount</i18n.Translate>
                          </th>
                          <th>
                            <i18n.Translate>Paid</i18n.Translate>
                          </th>
                          <th>
                            <i18n.Translate>Fee</i18n.Translate>
                          </th>
                          <th></th>
                        </tr>
                      </thead>
                      <tbody>
                        {orders.map((i, idx) => {
                          return (
                            <ReconciliationDetailsRow
                              orderId={i}
                              deposit={merged[i].paid}
                              fee={merged[i].fee}
                              onOrderDetails={onOrderDetails}
                            />
                          );
                        })}
                      </tbody>
                    </table>
                  </div>
                ) : undefined}
              </div>
            </div>
          </div>
        </div>
      )}

      <div class="card has-table">
        <header class="card-header">
          <p class="card-header-title">
            <i18n.Translate>Subtotals</i18n.Translate>
          </p>
        </header>

        <div class="card-content">
          <div class="b-table has-pagination">
            <div class="table-wrapper has-mobile-cards">
              <div class="table-container">
                <table class="table is-fullwidth is-striped is-hoverable is-fullwidth">
                  <thead></thead>
                  <tbody>
                    <Row
                      name={i18n.str`Paid`}
                      noCopy
                      value={
                        <RenderAmountBulma
                          value={totalPaid}
                          specMap={config.currencies}
                        />
                      }
                    />
                    <Row
                      name={i18n.str`Deposit fee`}
                      noCopy
                      value={
                        <RenderAmountBulma
                          value={totalFee}
                          specMap={config.currencies}
                        />
                      }
                    />
                    <Row
                      name={i18n.str`Wire fee`}
                      noCopy
                      value={
                        !wireFee ? (
                          i18n.str`not ready`
                        ) : (
                          <RenderAmountBulma
                            value={wireFee}
                            specMap={config.currencies}
                          />
                        )
                      }
                    />
                    <Row
                      name={i18n.str`Total`}
                      noCopy
                      value={
                        !wireFee ? (
                          i18n.str`not ready`
                        ) : (
                          <RenderAmountBulma
                            value={
                              Amounts.sub(totalPaid, totalFee, wireFee).amount
                            }
                            specMap={config.currencies}
                          />
                        )
                      }
                    />
                  </tbody>
                </table>
              </div>
            </div>
          </div>
        </div>
      </div>
    </Fragment>
  );
}

function ReconciliationDetailsRow({
  deposit,
  fee,
  orderId,
  onOrderDetails,
}: {
  orderId: string;
  deposit: AmountJson;
  fee: AmountJson;
  onOrderDetails: (id: string) => void;
}): VNode {
  const { config } = useSessionContext();
  const { i18n } = useTranslationContext();
  const result = useOrderDetails(orderId);
  const orderDetails =
    !result ||
    result instanceof TalerError ||
    result.type === "fail" ||
    result.body.order_status !== "paid"
      ? undefined
      : result.body;

  const amounts = !orderDetails
    ? undefined
    : getOrderAmountAndMaxDepositFee(orderDetails);

  return (
    <tr key={orderId}>
      <td>
        {!orderDetails
          ? i18n.str`loading...`
          : orderDetails.contract_terms.summary}
      </td>
      <td>
        {!amounts ? (
          i18n.str`loading...`
        ) : typeof amounts === "string" ? (
          ""
        ) : (
          <RenderAmountBulma
            value={amounts.amount}
            specMap={config.currencies}
          />
        )}
      </td>
      <td>
        <RenderAmountBulma value={deposit} specMap={config.currencies} />
      </td>
      <td>
        <RenderAmountBulma value={fee} specMap={config.currencies} />
      </td>
      <td class="is-actions-cell right-sticky">
        <div class="buttons is-right">
          <button
            class="button is-small is-info jb-modal"
            type="button"
            onClick={() => onOrderDetails(orderId)}
          >
            <i18n.Translate>Details</i18n.Translate>
          </button>
        </div>
      </td>
    </tr>
  );
}
