/*
 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 {
  amountFractionalBase,
  AmountJson,
  Amounts,
  KycRule,
  LimitOperationType,
} from "@gnu-taler/taler-util";
import {
  Attention,
  RenderAmount,
  useExchangeApiContext,
  useTranslationContext,
} from "@gnu-taler/web-util/browser";
import { formatDuration, intervalToDuration } from "date-fns";
import { Fragment, h, VNode } from "preact";

type KycRuleWithIdx = KycRule & {
  idx: number;
};
const TALER_SCREEN_ID = 121;

export function RulesInfo({
  rules,
  onEdit,
  onRemove,
  onNew,
}: {
  rules: KycRule[];
  onNew?: () => void;
  onEdit?: (k: KycRule, idx: number) => void;
  onRemove?: (k: KycRule, idx: number) => void;
}): VNode {
  const { i18n } = useTranslationContext();
  const { config } = useExchangeApiContext();

  if (!rules.length) {
    return (
      <div>
        <Attention
          title={i18n.str`There are no rules for operations`}
          type="warning"
        >
          <i18n.Translate>
            This mean that all operation have no limit.
          </i18n.Translate>
        </Attention>
        {!onNew ? undefined : (
          <button
            onClick={() => {
              onNew();
            }}
            class="m-4  rounded-md w-fit border-0 px-3 py-2 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600"
          >
            <i18n.Translate>Add custom rule</i18n.Translate>
          </button>
        )}
      </div>
    );
  }

  const theRules = rules.map((r, idx): KycRuleWithIdx => ({ ...r, idx }));

  const sorted = theRules.sort((a, b) => {
    return sortKycRules(a, b);
  });

  const hasActions = !!onEdit || !!onRemove;

  return (
    <Fragment>
      <div class="">
        <table class="min-w-full divide-y divide-gray-300">
          <thead class="bg-gray-50">
            <tr>
              <th
                scope="col"
                class="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 sm:pl-6"
              >
                <i18n.Translate>Operation</i18n.Translate>
              </th>
              <th
                scope="col"
                class="relative py-3.5 pl-3 pr-4 sm:pr-6 text-right"
              >
                <i18n.Translate>Threshold</i18n.Translate>
              </th>
              <th
                scope="col"
                class="relative py-3.5 pl-3 pr-4 sm:pr-6 text-right"
              >
                <i18n.Translate>Escalation</i18n.Translate>
              </th>
              {!hasActions ? undefined : (
                <th
                  scope="col"
                  class="relative py-3.5 pl-3 pr-4 sm:pr-6 text-right"
                >
                  {!onNew ? undefined : (
                    <button
                      onClick={() => {
                        onNew();
                      }}
                      class="rounded-md w-fit border-0 p-1 text-center text-sm bg-indigo-700 text-white shadow-sm hover:bg-indigo-700 disabled:bg-gray-600"
                    >
                      <i18n.Translate>
                        <svg
                          xmlns="http://www.w3.org/2000/svg"
                          fill="none"
                          viewBox="0 0 24 24"
                          stroke-width="1.5"
                          stroke="currentColor"
                          class="size-6"
                        >
                          <path
                            stroke-linecap="round"
                            stroke-linejoin="round"
                            d="M12 4.5v15m7.5-7.5h-15"
                          />
                        </svg>
                      </i18n.Translate>
                    </button>
                  )}
                </th>
              )}
            </tr>
          </thead>

          <tbody id="thetable" class="divide-y divide-gray-200 bg-white ">
            {sorted.map((r, k) => {
              return (
                <tr class="even:bg-gray-200 " key={k}>
                  <td class="flex whitespace-nowrap py-2 pl-4 pr-3 text-sm font-medium text-gray-900 sm:pl-6 text-left">
                    <span class="mx-2">
                      {r.exposed ? (
                        <svg
                          xmlns="http://www.w3.org/2000/svg"
                          fill="none"
                          viewBox="0 0 24 24"
                          stroke-width="1.5"
                          stroke="currentColor"
                          class="size-6"
                        >
                          <path
                            stroke-linecap="round"
                            stroke-linejoin="round"
                            d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z"
                          />
                          <path
                            stroke-linecap="round"
                            stroke-linejoin="round"
                            d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"
                          />
                        </svg>
                      ) : (
                        <svg
                          xmlns="http://www.w3.org/2000/svg"
                          fill="none"
                          viewBox="0 0 24 24"
                          stroke-width="1.5"
                          stroke="currentColor"
                          class="size-6 text-gray-500"
                        >
                          <path
                            stroke-linecap="round"
                            stroke-linejoin="round"
                            d="M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88"
                          />
                        </svg>
                      )}
                    </span>
                    <span>{r.operation_type}</span>
                  </td>
                  <td class=" relative whitespace-nowrap py-2 pl-3 pr-4 text-sm font-medium sm:pr-6 text-right">
                    {r.timeframe.d_us === "forever" ? (
                      <RenderAmount
                        value={Amounts.parseOrThrow(r.threshold)}
                        spec={config.config.currency_specification}
                      />
                    ) : (
                      <i18n.Translate context="threshold">
                        <RenderAmount
                          value={Amounts.parseOrThrow(r.threshold)}
                          spec={config.config.currency_specification}
                        />
                        every{" "}
                        {formatDuration(
                          intervalToDuration({
                            start: 0,
                            end: r.timeframe.d_us / 1000,
                          }),
                        )}
                      </i18n.Translate>
                    )}
                  </td>
                  <td class=" relative whitespace-nowrap py-2 pl-3 pr-4 text-sm font-medium sm:pr-6 text-right">
                    {r.is_and_combinator ? (
                      <span class="text-gray-500">
                        <i18n.Translate>(all)</i18n.Translate>
                      </span>
                    ) : (
                      <Fragment />
                    )}
                    {r.measures}
                  </td>
                  {!hasActions ? undefined : (
                    <td class="relative flex justify-end whitespace-nowrap py-2 pl-3 pr-4 text-sm font-medium sm:pr-6">
                      {!onEdit ? undefined : (
                        <button onClick={() => onEdit(r, r.idx)}>
                          <svg
                            xmlns="http://www.w3.org/2000/svg"
                            fill="none"
                            viewBox="0 0 24 24"
                            stroke-width="1.5"
                            stroke="currentColor"
                            class="size-6 text-green-700"
                          >
                            <path
                              stroke-linecap="round"
                              stroke-linejoin="round"
                              d="m16.862 4.487 1.687-1.688a1.875 1.875 0 1 1 2.652 2.652L10.582 16.07a4.5 4.5 0 0 1-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 0 1 1.13-1.897l8.932-8.931Zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0 1 15.75 21H5.25A2.25 2.25 0 0 1 3 18.75V8.25A2.25 2.25 0 0 1 5.25 6H10"
                            />
                          </svg>
                        </button>
                      )}
                      {!onRemove ? undefined : (
                        <button onClick={() => onRemove(r, r.idx)}>
                          <svg
                            xmlns="http://www.w3.org/2000/svg"
                            fill="none"
                            viewBox="0 0 24 24"
                            stroke-width="1.5"
                            stroke="currentColor"
                            class="size-6 text-red-700"
                          >
                            <path
                              stroke-linecap="round"
                              stroke-linejoin="round"
                              d="m14.74 9-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 0 1-2.244 2.077H8.084a2.25 2.25 0 0 1-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 0 0-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 0 1 3.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 0 0-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 0 0-7.5 0"
                            />
                          </svg>
                        </button>
                      )}
                    </td>
                  )}
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </Fragment>
  );
}

export function rate(a: AmountJson, b: number): number {
  const af = toFloat(a);
  const bf = b;
  if (bf === 0) return 0;
  return af / bf;
}

function toFloat(amount: AmountJson): number {
  return amount.value + amount.fraction / amountFractionalBase;
}

const OPERATION_TYPE_ORDER = {
  [LimitOperationType.balance]: 1,
  [LimitOperationType.transaction]: 2,
  [LimitOperationType.withdraw]: 3,
  [LimitOperationType.deposit]: 4,
  [LimitOperationType.aggregate]: 5,
  [LimitOperationType.close]: 6,
  [LimitOperationType.refund]: 7,
  [LimitOperationType.merge]: 8,
} as const;

/**
 * Operation follows OPERATION_TYPE_ORDER.
 * Then operations with timeframe "forever" means they are not reset, like balance. Go first.
 * Then operations with high throughput first.
 * @param a
 * @param b
 * @returns
 */
function sortKycRules(a: KycRule, b: KycRule): number {
  const op =
    OPERATION_TYPE_ORDER[a.operation_type] -
    OPERATION_TYPE_ORDER[b.operation_type];
  if (op !== 0) return op;
  const at = a.timeframe;
  const bt = b.timeframe;
  if (at.d_us === "forever" || bt.d_us === "forever") {
    if (at.d_us === "forever") return -1;
    if (bt.d_us === "forever") return 1;
    return Amounts.cmp(a.threshold, b.threshold);
  }
  const as = rate(Amounts.parseOrThrow(a.threshold), at.d_us);
  const bs = rate(Amounts.parseOrThrow(a.threshold), bt.d_us);
  return bs - as;
}
