/**
 * 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 { AlertInterface, AlertLevels, IdentifiableComponentInterface } from "@wso2is/core/models";
import { addAlert } from "@wso2is/core/store";
import { FormValue, useTrigger } from "@wso2is/forms";
import { Heading, LinkButton, PrimaryButton, Steps } from "@wso2is/react-components";
import { AxiosError } from "axios";
import React, { FunctionComponent, ReactElement, useState } from "react";
import { useTranslation } from "react-i18next";
import { useDispatch } from "react-redux";
import { Dispatch } from "redux";
import { Grid, Icon, Modal } from "semantic-ui-react";
import { AddAPIResourceBasic } from "./add-api-resource-steps/add-api-resource-basic";
import { AddAPIResourcePermissions } from "./add-api-resource-steps/add-api-resource-permissions";
import { history } from "../../../../../features/core";
import { createAPIResource, useAPIResourcePermissions, useAPIResources } from "../../api";
import { getAPIResourceWizardStepIcons } from "../../configs";
import { APIResourcesConstants } from "../../constants";
import {
    APIResourceInterface,
    APIResourcePermissionInterface,
    APIResourceWizardStepInterface,
    AddAPIResourceWizardStepsFormTypes,
    BsaicAPIResourceInterface
} from "../../models";

interface AddAPIResourcePropsInterface extends IdentifiableComponentInterface {
    /**
     * Close the wizard.
     */
    closeWizard: () => void;
    /**
     * Current step.
     */
    currentStep?: number;
    /**
     * Last step form type.
     */
    lastStepFormType?: AddAPIResourceWizardStepsFormTypes;
}

/**
 * API resource wizard.
 */
export const AddAPIResource: FunctionComponent<AddAPIResourcePropsInterface> = (
    props: AddAPIResourcePropsInterface
): ReactElement => {

    const {
        closeWizard,
        currentStep,
        lastStepFormType,
        ["data-componentid"]: componentId
    } = props;

    const { t } = useTranslation();
    const dispatch: Dispatch = useDispatch();
    const [ submitBasicDetails, setSubmitBasicDetails ] = useTrigger();
    const [ addPermission, setAddPermission ] = useTrigger();

    const [ isSubmitting, setIsSubmitting ] = useState<boolean>(false);
    const [ latestPermissionFormValues, setLatestPermissionFormValues ] = useState<Map<string, FormValue>>(undefined);
    const [ basicDetails, setBasicDetails ] = useState<BsaicAPIResourceInterface>(undefined);
    const [ permissions, setPermissions ] = useState<Map<string, APIResourcePermissionInterface>>(new Map());
    const [ currentWizardStep, setCurrentWizardStep ] = useState<number>(currentStep);
    const [ currentWizardFormType, setCurrentWizardFormType ]
        = useState<AddAPIResourceWizardStepsFormTypes>(AddAPIResourceWizardStepsFormTypes.BASIC_DETAILS);
    const [ currentIdentifierValue, setCurrentIdentifierValue ] = React.useState<string>(null);
    const [ currentPermissionIdentifierValue, setCurrentPermissionIdentifierValue ] = React.useState<string>(null);

    const {
        data: apiResourcesListData,
        isLoading: isAPIResourcesListLoading,
        mutate: mutateAPIResourcesFetchRequest
    } = useAPIResources(null, null, "identifier eq " + currentIdentifierValue);

    const {
        data: apiResourcePermissionsListData,
        isLoading: isAPIResourcePermissionsListLoading,
        mutate: mutateAPIResourcePermissionsFetchRequest
    } = useAPIResourcePermissions("name eq " + currentPermissionIdentifierValue);

    /**
    * Handles the wizard form submission.
    */
    const handleWizardFormSubmit = () => {
        switch (currentWizardFormType) {
            case AddAPIResourceWizardStepsFormTypes.BASIC_DETAILS:
                setSubmitBasicDetails();

                break;
            case AddAPIResourceWizardStepsFormTypes.PERMISSIONS: {
                if (latestPermissionFormValues?.get("displayName")?.toString().trim() !==
                    latestPermissionFormValues?.get("identifier")?.toString().trim()) {
                    setAddPermission();
                } else {
                    handleCreateAPIResource();
                }

                break;
            }
            default:
                break;
        }
    };

    /**
     * Handles the next button click.
     */
    const handleNext = () => {
        const newStep: number = currentWizardStep + 1;

        setCurrentWizardStep(newStep);
        setCurrentWizardFormType(steps[newStep].addAPIResourceWizardStepsFormType);
    };

    /**
     * Handles the previous button click.
     */
    const handlePrevious = () => {
        const newStep: number = currentWizardStep - 1;

        setCurrentWizardStep(newStep);
        setCurrentWizardFormType(steps[newStep].addAPIResourceWizardStepsFormType);
    };

    /**
     * Handles the API resource creation.
     */
    const handleCreateAPIResource = (): void => {
        setIsSubmitting(true);

        const apiResourceBody: APIResourceInterface = {
            displayName: basicDetails.displayName,
            gwName: APIResourcesConstants.EMPTY_STRING,
            identifier: basicDetails.identifier,
            permissions: [ ...permissions.values() ]
        };

        createAPIResource(apiResourceBody)
            .then((apiResource: APIResourceInterface) => {
                dispatch(addAlert<AlertInterface>({
                    description: t("extensions:develop.apiResource.notifications.addAPIResource.success" +
                        ".description"),
                    level: AlertLevels.SUCCESS,
                    message: t("extensions:develop.apiResource.notifications.addAPIResource.success.message")
                }));

                // Open the created API resource.
                history.push(APIResourcesConstants.getPaths().get("API_RESOURCE_EDIT").replace(":id", apiResource.id));
            })
            .catch((error: AxiosError) => {
                switch (error?.code) {
                    case APIResourcesConstants.UNAUTHORIZED_ACCESS:
                        dispatch(addAlert<AlertInterface>({
                            description: t("extensions:develop.apiResource.notifications.addAPIResource" +
                                ".unauthorizedError.description"),
                            level: AlertLevels.ERROR,
                            message: t("extensions:develop.apiResource.notifications.addAPIResource" +
                                ".unauthorizedError.message")
                        }));

                        break;

                    case APIResourcesConstants.API_RESOURCE_ALREADY_EXISTS:
                        dispatch(addAlert<AlertInterface>({
                            description: t("extensions:develop.apiResource.notifications.addAPIResource" +
                                ".alreadyExistsError.description"),
                            level: AlertLevels.ERROR,
                            message: t("extensions:develop.apiResource.notifications.addAPIResource" +
                                ".alreadyExistsError.message")
                        }));

                        break;

                    case APIResourcesConstants.PERMISSION_ALREADY_EXISTS:
                        dispatch(addAlert<AlertInterface>({
                            description: t("extensions:develop.apiResource.notifications.addAPIResource" +
                                ".permissionAlreadyExistsError.description"),
                            level: AlertLevels.ERROR,
                            message: t("extensions:develop.apiResource.notifications.addAPIResource" +
                                ".permissionAlreadyExistsError.message")
                        }));

                        break;

                    case APIResourcesConstants.INVALID_REQUEST_PAYLOAD:
                        dispatch(addAlert<AlertInterface>({
                            description: t("extensions:develop.apiResource.notifications.addAPIResource" +
                                ".invalidPayloadError.description"),
                            level: AlertLevels.ERROR,
                            message: t("extensions:develop.apiResource.notifications.addAPIResource" +
                                ".invalidPayloadError.message")
                        }));

                        break;

                    default:
                        dispatch(addAlert<AlertInterface>({
                            description: t("extensions:develop.apiResource.notifications.addAPIResource" +
                                ".genericError.description"),
                            level: AlertLevels.ERROR,
                            message: t("extensions:develop.apiResource.notifications.addAPIResource" +
                                ".genericError.message")
                        }));
                }
            })
            .finally(() => {
                setIsSubmitting(false);

                closeWizard();
            });
    };

    /**
     * Steps of the add API resource wizard.
     */
    const steps: APIResourceWizardStepInterface[] = [
        {
            addAPIResourceWizardStepsFormType: AddAPIResourceWizardStepsFormTypes.BASIC_DETAILS,
            content: (
                <AddAPIResourceBasic
                    initalBasicDetails={ basicDetails }
                    setBasicDetails={ setBasicDetails }
                    triggerSubmission={ submitBasicDetails }
                    submitCallback={ handleNext }
                    setCurrentIdentifierValue={ setCurrentIdentifierValue }
                    mutateAPIResourcesFetchRequest={ mutateAPIResourcesFetchRequest }
                    apiResourcesList={ apiResourcesListData?.apiResources }
                    isAPIResourcesListLoading={ isAPIResourcesListLoading }
                />
            ),
            icon: getAPIResourceWizardStepIcons().general,
            title: t("extensions:develop.apiResource.wizard.addApiResource.steps.basic.stepTitle")
        },
        {
            addAPIResourceWizardStepsFormType: AddAPIResourceWizardStepsFormTypes.PERMISSIONS,
            content: (
                <AddAPIResourcePermissions
                    setPermissionsList={ setPermissions }
                    initialPermissions={ permissions }
                    triggerAddPermission={ addPermission }
                    setAddPermission={ setAddPermission }
                    setLatestPermissionFormValues={ setLatestPermissionFormValues }
                    setCurrentPermissionIdentifierValue={ setCurrentPermissionIdentifierValue }
                    apiResourcePermissionsListData={ apiResourcePermissionsListData }
                    isAPIResourcePermissionsListLoading={ isAPIResourcePermissionsListLoading }
                    mutateAPIResourcePermissionsFetchRequest={ mutateAPIResourcePermissionsFetchRequest }
                />
            ),
            icon: getAPIResourceWizardStepIcons().permissions,
            title: t("extensions:develop.apiResource.wizard.addApiResource.steps.permissions.stepTitle")
        }
    ];

    return (
        <Modal
            data-testid={ componentId }
            open={ true }
            className="wizard api-resource-create-wizard"
            dimmer="blurring"
            size="large"
            onClose={ closeWizard }
            closeOnDimmerClick={ false }
            closeOnEscape
        >
            <Modal.Header className="wizard-header">
                { t("extensions:develop.apiResource.wizard.addApiResource.title") }
                <Heading as="h6">{ t("extensions:develop.apiResource.wizard.addApiResource.subtitle") }</Heading>
            </Modal.Header>
            <Modal.Content scrolling className="steps-container">
                <Steps.Group
                    current={ currentWizardStep }
                >
                    { steps.map((step: APIResourceWizardStepInterface) => (
                        <Steps.Step
                            key={ step.title }
                            icon={ step.icon }
                            title={ step.title }
                        />
                    )) }
                </Steps.Group>
            </Modal.Content>
            <Modal.Content className="content-container" scrolling>
                { steps[currentWizardStep].content }
            </Modal.Content>
            <Modal.Actions>
                <Grid>
                    <Grid.Row column={ 1 }>
                        <Grid.Column mobile={ 8 } tablet={ 8 } computer={ 8 }>
                            <LinkButton
                                tabIndex={ 6 }
                                data-testid={ `${componentId}-cancel-button` }
                                floated="left"
                                onClick={ () => closeWizard() }
                            >
                                { t("extensions:develop.apiResource.wizard.addApiResource.cancelButton") }
                            </LinkButton>
                        </Grid.Column>
                        <Grid.Column mobile={ 8 } tablet={ 8 } computer={ 8 }>
                            { currentWizardFormType !== lastStepFormType && (
                                <PrimaryButton
                                    tabIndex={ 7 }
                                    data-testid={ `${componentId}-next-button` }
                                    floated="right"
                                    onClick={ () => handleWizardFormSubmit() }
                                    loading={ isAPIResourcesListLoading }
                                >
                                    { t("extensions:develop.apiResource.wizard.addApiResource.nextButton") }
                                    <Icon name="arrow right" />
                                </PrimaryButton>
                            ) }
                            { currentWizardFormType === lastStepFormType && (
                                <PrimaryButton
                                    tabIndex={ 8 }
                                    data-testid={ `${componentId}-finish-button` }
                                    floated="right"
                                    onClick={ () => handleWizardFormSubmit() }
                                    loading={ isSubmitting || isAPIResourcePermissionsListLoading }
                                >
                                    { t("extensions:develop.apiResource.wizard.addApiResource.submitButton") }
                                </PrimaryButton>
                            ) }
                            { currentWizardStep > 0 && (
                                <LinkButton
                                    tabIndex={ 9 }
                                    data-testid={ `${componentId}-previous-button` }
                                    floated="right"
                                    onClick={ () => handlePrevious() }
                                    loading={ isSubmitting }
                                >
                                    <Icon name="arrow left" />
                                    { t("extensions:develop.apiResource.wizard.addApiResource.previousButton") }
                                </LinkButton>
                            ) }
                        </Grid.Column>
                    </Grid.Row>
                </Grid>
            </Modal.Actions>
        </Modal>
    );
};

/**
 * Default props for the add API resource wizard.
 */
AddAPIResource.defaultProps = {
    currentStep: 0,
    "data-componentid": "add-api-resource",
    lastStepFormType: AddAPIResourceWizardStepsFormTypes.PERMISSIONS
};
