/*
 This file is part of GNU Taler
 (C) 2021-2023 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 
 */
/**
 *
 * @author Sebastian Javier Marchano (sebasjm)
 */
import { useSWRConfig } from "swr";
import { MerchantBackend } from "../declaration.js";
import { useBackendContext } from "../context/backend.js";
import { useCallback, useEffect, useState } from "preact/hooks";
import { useInstanceContext } from "../context/instance.js";
import {
  HttpResponse,
  HttpResponseOk,
  RequestOptions,
} from "@gnu-taler/web-util/lib/index.browser";
import { useApiContext } from "@gnu-taler/web-util/lib/index.browser";
export function useMatchMutate(): (
  re: RegExp,
  value?: unknown,
) => Promise {
  const { cache, mutate } = useSWRConfig();
  if (!(cache instanceof Map)) {
    throw new Error(
      "matchMutate requires the cache provider to be a Map instance",
    );
  }
  return function matchRegexMutate(re: RegExp, value?: unknown) {
    const allKeys = Array.from(cache.keys());
    const keys = allKeys.filter((key) => re.test(key));
    const mutations = keys.map((key) => {
      return mutate(key, value, true);
    });
    return Promise.all(mutations);
  };
}
export function useBackendInstancesTestForAdmin(): HttpResponse<
  MerchantBackend.Instances.InstancesResponse,
  MerchantBackend.ErrorDetail
> {
  const { request } = useBackendBaseRequest();
  type Type = MerchantBackend.Instances.InstancesResponse;
  const [result, setResult] = useState<
    HttpResponse
  >({ loading: true });
  useEffect(() => {
    request(`/management/instances`)
      .then((data) => setResult(data))
      .catch((error) => setResult(error));
  }, [request]);
  return result;
}
export function useBackendConfig(): HttpResponse<
  MerchantBackend.VersionResponse,
  MerchantBackend.ErrorDetail
> {
  const { request } = useBackendBaseRequest();
  type Type = MerchantBackend.VersionResponse;
  const [result, setResult] = useState<
    HttpResponse
  >({ loading: true });
  useEffect(() => {
    request(`/config`)
      .then((data) => setResult(data))
      .catch((error) => setResult(error));
  }, [request]);
  return result;
}
interface useBackendInstanceRequestType {
  request: (
    endpoint: string,
    options?: RequestOptions,
  ) => Promise>;
  fetcher: (endpoint: string) => Promise>;
  reserveDetailFetcher: (endpoint: string) => Promise>;
  tipsDetailFetcher: (endpoint: string) => Promise>;
  multiFetcher: (url: string[]) => Promise[]>;
  orderFetcher: (
    endpoint: string,
    paid?: YesOrNo,
    refunded?: YesOrNo,
    wired?: YesOrNo,
    searchDate?: Date,
    delta?: number,
  ) => Promise>;
  transferFetcher: (
    endpoint: string,
    payto_uri?: string,
    verified?: string,
    position?: string,
    delta?: number,
  ) => Promise>;
  templateFetcher: (
    endpoint: string,
    position?: string,
    delta?: number,
  ) => Promise>;
  webhookFetcher: (
    endpoint: string,
    position?: string,
    delta?: number,
  ) => Promise>;
}
interface useBackendBaseRequestType {
  request: (
    endpoint: string,
    options?: RequestOptions,
  ) => Promise>;
}
type YesOrNo = "yes" | "no";
/**
 *
 * @param root the request is intended to the base URL and no the instance URL
 * @returns request handler to
 */
export function useBackendBaseRequest(): useBackendBaseRequestType {
  const { url: backend, token } = useBackendContext();
  const { request: requestHandler } = useApiContext();
  const request = useCallback(
    function requestImpl(
      endpoint: string,
      options: RequestOptions = {},
    ): Promise> {
      return requestHandler(backend, endpoint, { token, ...options });
    },
    [backend, token],
  );
  return { request };
}
export function useBackendInstanceRequest(): useBackendInstanceRequestType {
  const { url: rootBackendUrl, token: rootToken } = useBackendContext();
  const { token: instanceToken, id, admin } = useInstanceContext();
  const { request: requestHandler } = useApiContext();
  const { baseUrl, token } = !admin
    ? { baseUrl: rootBackendUrl, token: rootToken }
    : { baseUrl: `${rootBackendUrl}/instances/${id}`, token: instanceToken };
  const request = useCallback(
    function requestImpl(
      endpoint: string,
      options: RequestOptions = {},
    ): Promise> {
      return requestHandler(baseUrl, endpoint, { token, ...options });
    },
    [baseUrl, token],
  );
  const multiFetcher = useCallback(
    function multiFetcherImpl(
      endpoints: string[],
    ): Promise[]> {
      return Promise.all(
        endpoints.map((endpoint) =>
          requestHandler(baseUrl, endpoint, { token }),
        ),
      );
    },
    [baseUrl, token],
  );
  const fetcher = useCallback(
    function fetcherImpl(endpoint: string): Promise> {
      return requestHandler(baseUrl, endpoint, { token });
    },
    [baseUrl, token],
  );
  const orderFetcher = useCallback(
    function orderFetcherImpl(
      endpoint: string,
      paid?: YesOrNo,
      refunded?: YesOrNo,
      wired?: YesOrNo,
      searchDate?: Date,
      delta?: number,
    ): Promise> {
      const date_ms =
        delta && delta < 0 && searchDate
          ? searchDate.getTime() + 1
          : searchDate?.getTime();
      const params: any = {};
      if (paid !== undefined) params.paid = paid;
      if (delta !== undefined) params.delta = delta;
      if (refunded !== undefined) params.refunded = refunded;
      if (wired !== undefined) params.wired = wired;
      if (date_ms !== undefined) params.date_ms = date_ms;
      return requestHandler(baseUrl, endpoint, { params, token });
    },
    [baseUrl, token],
  );
  const reserveDetailFetcher = useCallback(
    function reserveDetailFetcherImpl(
      endpoint: string,
    ): Promise> {
      return requestHandler(baseUrl, endpoint, {
        params: {
          tips: "yes",
        },
        token,
      });
    },
    [baseUrl, token],
  );
  const tipsDetailFetcher = useCallback(
    function tipsDetailFetcherImpl(
      endpoint: string,
    ): Promise> {
      return requestHandler(baseUrl, endpoint, {
        params: {
          pickups: "yes",
        },
        token,
      });
    },
    [baseUrl, token],
  );
  const transferFetcher = useCallback(
    function transferFetcherImpl(
      endpoint: string,
      payto_uri?: string,
      verified?: string,
      position?: string,
      delta?: number,
    ): Promise> {
      const params: any = {};
      if (payto_uri !== undefined) params.payto_uri = payto_uri;
      if (verified !== undefined) params.verified = verified;
      if (delta !== undefined) {
        params.limit = delta;
      }
      if (position !== undefined) params.offset = position;
      return requestHandler(baseUrl, endpoint, { params, token });
    },
    [baseUrl, token],
  );
  const templateFetcher = useCallback(
    function templateFetcherImpl(
      endpoint: string,
      position?: string,
      delta?: number,
    ): Promise> {
      const params: any = {};
      if (delta !== undefined) {
        params.limit = delta;
      }
      if (position !== undefined) params.offset = position;
      return requestHandler(baseUrl, endpoint, { params, token });
    },
    [baseUrl, token],
  );
  const webhookFetcher = useCallback(
    function webhookFetcherImpl(
      endpoint: string,
      position?: string,
      delta?: number,
    ): Promise> {
      const params: any = {};
      if (delta !== undefined) {
        params.limit = delta;
      }
      if (position !== undefined) params.offset = position;
      return requestHandler(baseUrl, endpoint, { params, token });
    },
    [baseUrl, token],
  );
  return {
    request,
    fetcher,
    multiFetcher,
    orderFetcher,
    reserveDetailFetcher,
    tipsDetailFetcher,
    transferFetcher,
    templateFetcher,
    webhookFetcher,
  };
}