/*
 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 
 */
import {
  HttpResponse,
  HttpResponseOk,
  HttpResponsePaginated,
  RequestError,
  useApiContext,
} from "@gnu-taler/web-util/lib/index.browser";
import { useEffect, useMemo, useState } from "preact/hooks";
import { useBackendContext } from "../context/backend.js";
import { MAX_RESULT_SIZE, PAGE_SIZE } from "../utils.js";
import {
  getInitialBackendBaseURL,
  useAuthenticatedBackend,
  useMatchMutate,
} from "./backend.js";
// FIX default import https://github.com/microsoft/TypeScript/issues/49189
import _useSWR, { SWRHook } from "swr";
const useSWR = _useSWR as unknown as SWRHook;
export function useAdminAccountAPI(): AdminAccountAPI {
  const { request } = useAuthenticatedBackend();
  const mutateAll = useMatchMutate();
  const { state, logIn } = useBackendContext();
  if (state.status === "loggedOut") {
    throw Error("access-api can't be used when the user is not logged In");
  }
  const createAccount = async (
    data: SandboxBackend.Circuit.CircuitAccountRequest,
  ): Promise> => {
    const res = await request(`circuit-api/accounts`, {
      method: "POST",
      data,
      contentType: "json",
    });
    await mutateAll(/.*circuit-api\/accounts.*/);
    return res;
  };
  const updateAccount = async (
    account: string,
    data: SandboxBackend.Circuit.CircuitAccountReconfiguration,
  ): Promise> => {
    const res = await request(`circuit-api/accounts/${account}`, {
      method: "PATCH",
      data,
      contentType: "json",
    });
    await mutateAll(/.*circuit-api\/accounts.*/);
    return res;
  };
  const deleteAccount = async (
    account: string,
  ): Promise> => {
    const res = await request(`circuit-api/accounts/${account}`, {
      method: "DELETE",
      contentType: "json",
    });
    await mutateAll(/.*circuit-api\/accounts.*/);
    return res;
  };
  const changePassword = async (
    account: string,
    data: SandboxBackend.Circuit.AccountPasswordChange,
  ): Promise> => {
    const res = await request(`circuit-api/accounts/${account}/auth`, {
      method: "PATCH",
      data,
      contentType: "json",
    });
    if (account === state.username) {
      await mutateAll(/.*/);
      logIn({
        username: account,
        password: data.new_password,
      });
    }
    return res;
  };
  return { createAccount, deleteAccount, updateAccount, changePassword };
}
export function useCircuitAccountAPI(): CircuitAccountAPI {
  const { request } = useAuthenticatedBackend();
  const mutateAll = useMatchMutate();
  const { state } = useBackendContext();
  if (state.status === "loggedOut") {
    throw Error("access-api can't be used when the user is not logged In");
  }
  const account = state.username;
  const updateAccount = async (
    data: SandboxBackend.Circuit.CircuitAccountReconfiguration,
  ): Promise> => {
    const res = await request(`circuit-api/accounts/${account}`, {
      method: "PATCH",
      data,
      contentType: "json",
    });
    await mutateAll(/.*circuit-api\/accounts.*/);
    return res;
  };
  const changePassword = async (
    data: SandboxBackend.Circuit.AccountPasswordChange,
  ): Promise> => {
    const res = await request(`circuit-api/accounts/${account}/auth`, {
      method: "PATCH",
      data,
      contentType: "json",
    });
    return res;
  };
  const createCashout = async (
    data: SandboxBackend.Circuit.CashoutRequest,
  ): Promise> => {
    const res = await request(
      `circuit-api/cashouts`,
      {
        method: "POST",
        data,
        contentType: "json",
      },
    );
    return res;
  };
  const confirmCashout = async (
    cashoutId: string,
    data: SandboxBackend.Circuit.CashoutConfirm,
  ): Promise> => {
    const res = await request(
      `circuit-api/cashouts/${cashoutId}/confirm`,
      {
        method: "POST",
        data,
        contentType: "json",
      },
    );
    await mutateAll(/.*circuit-api\/cashout.*/);
    return res;
  };
  const abortCashout = async (
    cashoutId: string,
  ): Promise> => {
    const res = await request(`circuit-api/cashouts/${cashoutId}/abort`, {
      method: "POST",
      contentType: "json",
    });
    await mutateAll(/.*circuit-api\/cashout.*/);
    return res;
  };
  return {
    updateAccount,
    changePassword,
    createCashout,
    confirmCashout,
    abortCashout,
  };
}
export interface AdminAccountAPI {
  createAccount: (
    data: SandboxBackend.Circuit.CircuitAccountRequest,
  ) => Promise>;
  deleteAccount: (account: string) => Promise>;
  updateAccount: (
    account: string,
    data: SandboxBackend.Circuit.CircuitAccountReconfiguration,
  ) => Promise>;
  changePassword: (
    account: string,
    data: SandboxBackend.Circuit.AccountPasswordChange,
  ) => Promise>;
}
export interface CircuitAccountAPI {
  updateAccount: (
    data: SandboxBackend.Circuit.CircuitAccountReconfiguration,
  ) => Promise>;
  changePassword: (
    data: SandboxBackend.Circuit.AccountPasswordChange,
  ) => Promise>;
  createCashout: (
    data: SandboxBackend.Circuit.CashoutRequest,
  ) => Promise>;
  confirmCashout: (
    id: string,
    data: SandboxBackend.Circuit.CashoutConfirm,
  ) => Promise>;
  abortCashout: (id: string) => Promise>;
}
async function getBusinessStatus(
  request: ReturnType["request"],
  basicAuth: { username: string; password: string },
): Promise {
  try {
    const url = getInitialBackendBaseURL();
    const result = await request<
      HttpResponseOk
    >(url, `circuit-api/accounts/${basicAuth.username}`, { basicAuth });
    return result.ok;
  } catch (error) {
    return false;
  }
}
export function useBusinessAccountFlag(): boolean | undefined {
  const [isBusiness, setIsBusiness] = useState();
  const { state } = useBackendContext();
  const { request } = useApiContext();
  const creds =
    state.status === "loggedOut"
      ? undefined
      : { username: state.username, password: state.password };
  useEffect(() => {
    if (!creds) return;
    getBusinessStatus(request, creds)
      .then((result) => {
        setIsBusiness(result);
      })
      .catch((error) => {
        setIsBusiness(false);
      });
  });
  return isBusiness;
}
export function useBusinessAccountDetails(
  account: string,
): HttpResponse<
  SandboxBackend.Circuit.CircuitAccountData,
  SandboxBackend.SandboxError
> {
  const { fetcher } = useAuthenticatedBackend();
  const { data, error } = useSWR<
    HttpResponseOk,
    RequestError
  >([`circuit-api/accounts/${account}`], fetcher, {
    refreshInterval: 0,
    refreshWhenHidden: false,
    revalidateOnFocus: false,
    revalidateOnReconnect: false,
    refreshWhenOffline: false,
    errorRetryCount: 0,
    errorRetryInterval: 1,
    shouldRetryOnError: false,
    keepPreviousData: true,
  });
  if (data) return data;
  if (error) return error.info;
  return { loading: true };
}
export function useRatiosAndFeeConfig(): HttpResponse<
  SandboxBackend.Circuit.Config,
  SandboxBackend.SandboxError
> {
  const { fetcher } = useAuthenticatedBackend();
  const { data, error } = useSWR<
    HttpResponseOk,
    RequestError
  >([`circuit-api/config`], fetcher, {
    refreshInterval: 1000,
    refreshWhenHidden: false,
    revalidateOnFocus: false,
    revalidateOnReconnect: false,
    refreshWhenOffline: false,
    errorRetryCount: 0,
    errorRetryInterval: 1,
    shouldRetryOnError: false,
    keepPreviousData: true,
  });
  if (data) {
    // data.data.ratios_and_fees.sell_out_fee = 2
    if (!data.data.ratios_and_fees.fiat_currency) {
      data.data.ratios_and_fees.fiat_currency = "FIAT";
    }
  }
  if (data) return data;
  if (error) return error.info;
  return { loading: true };
}
interface PaginationFilter {
  account?: string;
  page?: number;
}
export function useBusinessAccounts(
  args?: PaginationFilter,
): HttpResponsePaginated<
  SandboxBackend.Circuit.CircuitAccounts,
  SandboxBackend.SandboxError
> {
  const { sandboxAccountsFetcher } = useAuthenticatedBackend();
  const [page, setPage] = useState(0);
  const {
    data: afterData,
    error: afterError,
    // isValidating: loadingAfter,
  } = useSWR<
    HttpResponseOk,
    RequestError
  >(
    [`circuit-api/accounts`, args?.page, PAGE_SIZE, args?.account],
    sandboxAccountsFetcher,
    {
      refreshInterval: 0,
      refreshWhenHidden: false,
      revalidateOnFocus: false,
      revalidateOnReconnect: false,
      refreshWhenOffline: false,
      errorRetryCount: 0,
      errorRetryInterval: 1,
      shouldRetryOnError: false,
      keepPreviousData: true,
    },
  );
  // const [lastAfter, setLastAfter] = useState<
  //   HttpResponse
  // >({ loading: true });
  // useEffect(() => {
  //   if (afterData) setLastAfter(afterData);
  // }, [afterData]);
  // if the query returns less that we ask, then we have reach the end or beginning
  const isReachingEnd =
    afterData && afterData.data?.customers?.length < PAGE_SIZE;
  const isReachingStart = false;
  const pagination = {
    isReachingEnd,
    isReachingStart,
    loadMore: () => {
      if (!afterData || isReachingEnd) return;
      if (afterData.data?.customers?.length < MAX_RESULT_SIZE) {
        setPage(page + 1);
      }
    },
    loadMorePrev: () => {
      null;
    },
  };
  const result = useMemo(() => {
    const customers = !afterData ? [] : afterData?.data?.customers ?? [];
    return { ok: true as const, data: { customers }, ...pagination };
  }, [afterData?.data]);
  if (afterError) return afterError.info;
  if (afterData) {
    return result;
  }
  // if (loadingAfter)
  //   return { loading: true, data: { customers } };
  // if (afterData) {
  //   return { ok: true, data: { customers }, ...pagination };
  // }
  return { loading: true };
}
export function useCashouts(
  account: string,
): HttpResponse<
  SandboxBackend.Circuit.CashoutStatusResponseWithId[],
  SandboxBackend.SandboxError
> {
  const { sandboxCashoutFetcher, multiFetcher } = useAuthenticatedBackend();
  const { data: list, error: listError } = useSWR<
    HttpResponseOk,
    RequestError
  >([`circuit-api/cashouts`, account], sandboxCashoutFetcher, {
    refreshInterval: 0,
    refreshWhenHidden: false,
    revalidateOnFocus: false,
    revalidateOnReconnect: false,
    refreshWhenOffline: false,
  });
  const paths = ((list?.data && list?.data.cashouts) || []).map(
    (cashoutId) => `circuit-api/cashouts/${cashoutId}`,
  );
  const { data: cashouts, error: productError } = useSWR<
    HttpResponseOk[],
    RequestError
  >([paths], multiFetcher, {
    refreshInterval: 0,
    refreshWhenHidden: false,
    revalidateOnFocus: false,
    revalidateOnReconnect: false,
    refreshWhenOffline: false,
  });
  if (listError) return listError.info;
  if (productError) return productError.info;
  if (cashouts) {
    const dataWithId = cashouts.map((d) => {
      //take the id from the queried url
      return {
        ...d.data,
        id: d.info?.url.replace(/.*\/cashouts\//, "") || "",
      };
    });
    return { ok: true, data: dataWithId };
  }
  return { loading: true };
}
export function useCashoutDetails(
  id: string,
): HttpResponse<
  SandboxBackend.Circuit.CashoutStatusResponse,
  SandboxBackend.SandboxError
> {
  const { fetcher } = useAuthenticatedBackend();
  const { data, error } = useSWR<
    HttpResponseOk,
    RequestError
  >([`circuit-api/cashouts/${id}`], fetcher, {
    refreshInterval: 0,
    refreshWhenHidden: false,
    revalidateOnFocus: false,
    revalidateOnReconnect: false,
    refreshWhenOffline: false,
    errorRetryCount: 0,
    errorRetryInterval: 1,
    shouldRetryOnError: false,
    keepPreviousData: true,
  });
  if (data) return data;
  if (error) return error.info;
  return { loading: true };
}