/**
 * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). All Rights Reserved.
 *
 * This software is the property of WSO2 LLC. and its suppliers, if any.
 * Dissemination of any information or reproduction of any material contained
 * herein in any form is strictly forbidden, unless permitted by WSO2 expressly.
 * You may not alter or remove any copyright or other notice from copies of this content.
 */

import { AsgardeoSPAClient, HttpClientInstance } from "@asgardeo/auth-react";
import { IdentityAppsApiException } from "@wso2is/core/exceptions";
import { HttpMethods } from "@wso2is/core/models";
import useRequest, {
    RequestErrorInterface,
    RequestResultInterface
} from "apps/console/src/features/core/hooks/use-request";
import { AxiosError, AxiosRequestConfig, AxiosResponse } from "axios";
import { store } from "../../../../features/core/store";
import { APIResourcesConstants } from "../constants";
import { APIResourceInterface, APIResourcePermissionInterface, APIResourcesListInterface, 
    UpdatedAPIResourceInterface } from "../models";

/**
 * Get an axios instance.
 */
const httpClient: HttpClientInstance = AsgardeoSPAClient.getInstance()
    .httpRequest.bind(AsgardeoSPAClient.getInstance());

/**
 * Get API resources.
 *
 * @param after - after.
 * @param before - before.
 * @returns `Promise<APIResourcesListInterface>`
 * @throws `IdentityAppsApiException`
 */
export const useAPIResources = <Data = APIResourcesListInterface, Error = RequestErrorInterface>(
    after?: string,
    before?: string,
    filter?: string
): RequestResultInterface<Data, Error> => {

    const requestConfig: AxiosRequestConfig = {
        headers: {
            "Accept": "application/json",
            "Content-Type": "application/json"
        },
        method: HttpMethods.GET,
        params: {
            after,
            before,
            filter
        },
        url: `${store.getState().config.endpoints.authzEndpoint}/${APIResourcesConstants.API_RESOURCE_DIR}`
    };

    const { data, error, isValidating, mutate } = useRequest<Data, Error>(requestConfig);

    return {
        data,
        error: error,
        isLoading: !error && !data,
        isValidating,
        mutate
    };
};

/**
 * 
 * @param apiResourceId - id of the API resource
 * @returns `Promise<APIResourceInterface>`
 * @throws `IdentityAppsApiException`
 */
export const useAPIResourceDetails = <Data = APIResourceInterface, Error = RequestErrorInterface>(
    apiResourceId: string
): RequestResultInterface<Data, Error> => {

    const requestConfig: AxiosRequestConfig = {
        headers: {
            "Accept": "application/json",
            "Content-Type": "application/json"
        },
        method: HttpMethods.GET,
        url: `${
            store.getState().config.endpoints.authzEndpoint
        }/${
            APIResourcesConstants.API_RESOURCE_DIR
        }/${apiResourceId}`
    };

    /**
     * Pass `null` if the `apiResourceId` is not available. This will prevent the request from being called.
     */
    const { data, error, isValidating, mutate } = useRequest<Data, Error>(apiResourceId ? requestConfig : null);

    return {
        data,
        error: error,
        isLoading: !error && !data,
        isValidating,
        mutate
    };
};

/**
 * Get permissions of an API resource.
 * @param apiResourceId - id of the API resource
 * @returns `Promise<APIResourcePermissionInterface[]>`
 * @throws `IdentityAppsApiException`
 */
export const useAPIResourcePermissions = <Data = APIResourcePermissionInterface[], Error = RequestErrorInterface>(
    filter: string
): RequestResultInterface<Data, Error> => {
    const requestConfig: AxiosRequestConfig = {
        headers: {
            "Accept": "application/json",
            "Content-Type": "application/json"
        },
        method: HttpMethods.GET,
        params: { filter },
        url: `${store.getState().config.endpoints.authzEndpoint}/permissions`
    };

    const { data, error, isValidating, mutate } = useRequest<Data, Error>(requestConfig);

    return {
        data,
        error: error,
        isLoading: !error && !data,
        isValidating,
        mutate
    };
};

/**
 * Delete an API resource.
 *
 * @param apiResourceId - UUID of the API resource that needed to be deleted.
 * @returns `Promise<null | IdentityAppsApiException>`
 * @throws `IdentityAppsApiException`
 */
export const deleteAPIResource = (apiResourceId: string): Promise<null | IdentityAppsApiException> => {

    const requestConfig: AxiosRequestConfig = {
        headers: {
            "Accept": "application/json",
            "Content-Type": "application/json"
        },
        method: HttpMethods.DELETE,
        url: `${store.getState().config.endpoints.authzEndpoint}/${APIResourcesConstants.API_RESOURCE_DIR}/` + 
            `${apiResourceId}`
    };

    return httpClient(requestConfig)
        .then((response: AxiosResponse) => {
            if (response.status !== 204) {
                throw new IdentityAppsApiException(
                    response.data.description,
                    null,
                    response.status,
                    response.request,
                    response,
                    response.config);
            }

            return Promise.resolve(response.data);
        }).catch((error: AxiosError) => {
            throw new IdentityAppsApiException(
                error.message,
                error.stack,
                error.response?.data?.code,
                error.request,
                error.response,
                error.config);
        });
};

/**
 * Update an API resource.
 * 
 * @param apiResourceId - UUID of the API resource that needed to be updated.
 * @param updateAPIResourceBody - Body of the API resource that needed to be updated.
 * @returns `Promise<null | IdentityAppsApiException>`
 */
export const updateAPIResource = (
    apiResourceId: string, 
    updateAPIResourceBody: UpdatedAPIResourceInterface
): Promise<null | IdentityAppsApiException> => {

    const requestConfig: AxiosRequestConfig = {
        data: updateAPIResourceBody,
        method: HttpMethods.PATCH,
        url: `${
            store.getState().config.endpoints.authzEndpoint
        }/${APIResourcesConstants.API_RESOURCE_DIR}/${apiResourceId}`
    };

    return httpClient(requestConfig)
        .then((response: AxiosResponse) => {
            return Promise.resolve(response.data);
        })
        .catch((error: AxiosError) => {
            throw new IdentityAppsApiException(
                error.message,
                error.stack,
                error.response?.data?.code,
                error.request,
                error.response,
                error.config);
        });
};

/**
 * Create an API resource.
 * 
 * @param apiResourceBody - Body of the API resource that needed to be created.
 * @returns `Promise<null | IdentityAppsApiException>`
 */
export const createAPIResource = (
    apiResourceBody: APIResourceInterface
): Promise<APIResourceInterface | void> => {

    const requestConfig: AxiosRequestConfig = {
        data: apiResourceBody,
        method: HttpMethods.POST,
        url: `${ store.getState().config.endpoints.authzEndpoint }/${APIResourcesConstants.API_RESOURCE_DIR}`
    };

    return httpClient(requestConfig)
        .then((response: AxiosResponse) => {
            return Promise.resolve(response.data);
        })
        .catch((error: AxiosError) => {
            throw new IdentityAppsApiException(
                error.message,
                error.stack,
                error.response?.data?.code,
                error.request,
                error.response,
                error.config);
        });
};
