/*
 This file is part of GNU Taler
 (C) 2021-2024 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/>
 */

/**
 *
 * @author Sebastian Javier Marchano (sebasjm)
 * @author Nic Eigel
 */

import {
    HttpResponse,
    HttpResponseOk,
    RequestError,
    RequestOptions,
    useApiContext,
} from "@gnu-taler/web-util/browser";
import {useCallback, useEffect, useState} from "preact/hooks";
import {useSWRConfig} from "swr";
import { useBackendContext } from "../context/backend.js";
import { AuditorBackend } from "../declaration.js";

export function useMatchMutate(): (
    re?: RegExp,
    value?: unknown,
) => Promise<any> {
    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) {
        return mutate((key) => {
            // evict if no key or regex === all
            if (!key || !re) return true
            // match string
            if (typeof key === 'string' && re.test(key)) return true
            // record or object have the path at [0]
            if (typeof key === 'object' && re.test(key[0])) return true
            //key didn't match regex
            return false
        }, undefined, {
            revalidate: true,
        });
    };
}

const CHECK_CONFIG_INTERVAL_OK = 5 * 60 * 1000;
const CHECK_CONFIG_INTERVAL_FAIL = 2 * 1000;

export function useBackendConfig(): HttpResponse<
    AuditorBackend.VersionResponse | undefined,
    RequestError<AuditorBackend.ErrorDetail>
> {
    const {request} = useBackendBaseRequest();

    type Type = AuditorBackend.VersionResponse;
    type State = { data: HttpResponse<Type, RequestError<AuditorBackend.ErrorDetail>>, timer: number }
    const [result, setResult] = useState<State>({data: {loading: true}, timer: 0});

    useEffect(() => {
        if (result.timer) {
            clearTimeout(result.timer);
        }

        function tryConfig(): void {
            request<Type>(`/config`)
                .then((data) => {
                    const timer: any = setTimeout(() => {
                        tryConfig();
                    }, CHECK_CONFIG_INTERVAL_OK);
                    setResult({data, timer});
                })
                .catch((error) => {
                    const timer: any = setTimeout(() => {
                        tryConfig();
                    }, CHECK_CONFIG_INTERVAL_FAIL);
                    const data = error.cause;
                    setResult({data, timer});
                });
        }

        tryConfig();
    }, [request]);

    return result.data;
}

export function useBackendToken(): HttpResponse<
  AuditorBackend.VersionResponse,
  RequestError<AuditorBackend.ErrorDetail>
> {
    const {request} = useBackendBaseRequest();

    type Type = AuditorBackend.VersionResponse;
    type State = { data: HttpResponse<Type, RequestError<AuditorBackend.ErrorDetail>>, timer: number }
    const [result, setResult] = useState<State>({data: {loading: true}, timer: 0});

    useEffect(() => {
        if (result.timer) {
            clearTimeout(result.timer);
        }

        function tryToken(): void {
            request<Type>(`/monitoring/balances`)
              .then((data) => {
                  const timer: any = setTimeout(() => {
                      tryToken();
                  }, CHECK_CONFIG_INTERVAL_OK);
                  setResult({data, timer});
              })
              .catch((error) => {
                  const timer: any = setTimeout(() => {
                      tryToken();
                  }, CHECK_CONFIG_INTERVAL_FAIL);
                  const data = error.cause;
                  setResult({data, timer});
              });
        }

        tryToken();
    }, [request]);

    return result.data;
}

interface useBackendInstanceRequestType {

    request: <T>(
        endpoint: string,
        options?: RequestOptions,
    ) => Promise<HttpResponseOk<T>>;
    fetcher: <T>(endpoint: string) => Promise<HttpResponseOk<T>>;
    multiFetcher: <T>(params: string[]) => Promise<HttpResponseOk<T>[]>;
    depositConfirmationFetcher: <T>(
        params: [
            endpoint: string,
        ],
    ) => Promise<HttpResponseOk<T>>;
}

interface useBackendBaseRequestType {

    request: <T>(
        endpoint: string,
      options?: RequestOptions
    ) => Promise<HttpResponseOk<T>>;
}

type YesOrNo = "yes" | "no";

/**
 *
 * @param root the request is intended to the base URL and no the instance URL
 * @returns request handler to
 */
//TODO: Add token
export function useBackendBaseRequest(): useBackendBaseRequestType {
    const {url: backend} = useBackendContext();
    const {request: requestHandler} = useApiContext();
    //const { token } = useBackendTokenContext();
    const token = "D4CST1Z6AHN3RT03M0T9NSTF2QGHTB5ZD2D3RYZB4HAWG8SX0JEFWBXCKXZHMB7Y3Z7KVFW0B3XPXD5BHCFP8EB0R6CNH2KAWDWVET0";


    const request = useCallback(
        function requestImpl<T>(
            endpoint: string,
            //todo: remove
            options: RequestOptions = {},
        ): Promise<HttpResponseOk<T>> {
            return requestHandler<T>(backend, endpoint, {...options, token}).then(res => {
                return res;
            }).catch(err => {
                throw err;
            });
        },
        [backend],
    );

    return {request};
}


export function useBackendRequest(): useBackendInstanceRequestType {
    const {url: rootBackendUrl} = useBackendContext();
   // const {id} = useInstanceContext();
    const {request: requestHandler} = useApiContext();

    //TODO: check
    const baseUrl = "http://localhost:8083/";
    const token = "D4CST1Z6AHN3RT03M0T9NSTF2QGHTB5ZD2D3RYZB4HAWG8SX0JEFWBXCKXZHMB7Y3Z7KVFW0B3XPXD5BHCFP8EB0R6CNH2KAWDWVET0";




    const request = useCallback(
        function requestImpl<T>(
            endpoint: string,
            options: RequestOptions = {},
        ): Promise<HttpResponseOk<T>> {
            return requestHandler<T>(baseUrl, endpoint, {...options, token});
        },
        [baseUrl],
    );

    const multiFetcher = useCallback(
        function multiFetcherImpl<T>(
            params: string[],
            options: RequestOptions = {},
        ): Promise<HttpResponseOk<T>[]> {
            return Promise.all(
              params.map((endpoint) =>
                    requestHandler<T>(baseUrl, endpoint, {...options, token}),
                ),
            );
        },
        [baseUrl],
    );

    const fetcher = useCallback(
        function fetcherImpl<T>(endpoint: string): Promise<HttpResponseOk<T>> {
            return requestHandler<T>(baseUrl, endpoint, {token});
        },
        [baseUrl],
    );

    const depositConfirmationFetcher = useCallback(
        function orderFetcherImpl<T>(
            args: [endpoint: string,
                ],
        ): Promise<HttpResponseOk<T>> {
            const [endpoint] = args;
            const params: any = {"token": "D4CST1Z6AHN3RT03M0T9NSTF2QGHTB5ZD2D3RYZB4HAWG8SX0JEFWBXCKXZHMB7Y3Z7KVFW0B3XPXD5BHCFP8EB0R6CNH2KAWDWVET0"};
            return requestHandler<T>(baseUrl, endpoint, {params, token});
        },
        [baseUrl],
    );


    return {
        request,
        fetcher,
        depositConfirmationFetcher,
        multiFetcher
    };
}