/**
 * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.com). All Rights Reserved.
 *
 * This software is the property of WSO2 Inc. 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 { getProfileSchemas } from "@wso2is/core/api";
import { IdentityAppsApiException } from "@wso2is/core/exceptions";
import {
    AlertInterface,
    AlertLevels,
    Claim,
    ProfileSchemaInterface,
    TestableComponentInterface
} from "@wso2is/core/models";
import { addAlert, setProfileSchemaRequestLoadingStatus, setSCIMSchemas } from "@wso2is/core/store";
import { Field, FormValue, Forms, Validation } from "@wso2is/forms";
import {
    ConfirmationModal,
    CopyInputField,
    DangerZone,
    DangerZoneGroup,
    EmphasizedSegment,
    Hint,
    PrimaryButton
} from "@wso2is/react-components";
import React, { FunctionComponent, ReactElement, useEffect, useRef, useState } from "react";
import { Trans, useTranslation } from "react-i18next";
import { useDispatch } from "react-redux";
import { Divider, Form, Grid, Popup } from "semantic-ui-react";
import { AppConstants, history } from "../../../../../../features/core";
import { deleteAClaim, updateAClaim } from "../../../../../../features/claims/api";

const DEFAULT_ATTRIBUTE_DISPLAY_ORDER = 20;

/**
 * Prop types for `EditBasicDetailsLocalClaims` component
 */
interface EditBasicDetailsLocalClaimsPropsInterface extends TestableComponentInterface {
    /**
     * The claim to be edited
     */
    claim: Claim;
    /**
     * The function to be called to initiate an update
     */
    update: () => void;
}

/**
 * This component renders the Basic Details pane of the edit local claim screen
 *
 * @param {EditBasicDetailsLocalClaimsPropsInterface} props - Props injected to the component.
 *
 * @return {React.ReactElement}
 */
export const EditBasicDetailsLocalClaims: FunctionComponent<EditBasicDetailsLocalClaimsPropsInterface> = (
    props: EditBasicDetailsLocalClaimsPropsInterface
): ReactElement => {

    const {
        claim,
        update,
        [ "data-testid" ]: testId
    } = props;

    const dispatch = useDispatch();

    const [ isShowNameHint, setIsShowNameHint ] = useState(false);
    const [ isShowRegExHint, setIsShowRegExHint ] = useState(false);
    const [ isShowDisplayOrderHint, setIsShowDisplayOrderHint ] = useState(false);
    const [ isShowDisplayOrder, setIsShowDisplayOrder ] = useState(false);
    const [ confirmDelete, setConfirmDelete ] = useState(false);

    const nameField = useRef<HTMLElement>(null);
    const regExField = useRef<HTMLElement>(null);
    const displayOrderField = useRef<HTMLElement>(null);
    const descriptionField = useRef<HTMLElement>(null);

    const nameTimer = useRef(null);
    const regExTimer = useRef(null);
    const displayTimer = useRef(null);

    const { t } = useTranslation();

/*     useEffect(() => {
        if (claim?.supportedByDefault) {
            setIsShowDisplayOrder(true);
        }
    }, [ claim ]); */

    const deleteConfirmation = (): ReactElement => (
        <ConfirmationModal
            onClose={ (): void => setConfirmDelete(false) }
            type="warning"
            open={ confirmDelete }
            assertion={ claim.displayName }
            assertionHint={
                <p>
                    <Trans i18nKey="console:manage.features.claims.local.confirmation.hint">
                        Please type <strong>{ { name: claim.displayName } }</strong> to confirm.
                    </Trans>
                </p>
            }
            assertionType="input"
            primaryAction={ t("console:manage.features.claims.local.confirmation.primaryAction") }
            secondaryAction={ t("common:cancel") }
            onSecondaryActionClick={ (): void => setConfirmDelete(false) }
            onPrimaryActionClick={ (): void => deleteLocalClaim(claim.id) }
            data-testid={ `${ testId }-delete-confirmation-modal` }
            closeOnDimmerClick={ false }
        >
            <ConfirmationModal.Header>
                { t("console:manage.features.claims.local.confirmation.header") }
            </ConfirmationModal.Header>
            <ConfirmationModal.Message attached warning>
                { t("console:manage.features.claims.local.confirmation.message") }
            </ConfirmationModal.Message>
            <ConfirmationModal.Content>
                { t("console:manage.features.claims.local.confirmation.content") }
            </ConfirmationModal.Content>
        </ConfirmationModal>
    );

    /**
     * This deletes a local claim
     * @param {string} id
     */
    const deleteLocalClaim = (id: string) => {
        deleteAClaim(id).then(() => {
            history.push(AppConstants.getPaths().get("LOCAL_CLAIMS"));
            dispatch(addAlert(
                {
                    description: t("console:manage.features.claims.local.notifications.deleteClaim.success." +
                        "description"),
                    level: AlertLevels.SUCCESS,
                    message: t("console:manage.features.claims.local.notifications.deleteClaim.success.message")
                }
            ));
        }).catch(error => {
            dispatch(addAlert(
                {
                    description: error?.description
                        || t("console:manage.features.claims.local.notifications.deleteClaim.genericError.description"),
                    level: AlertLevels.ERROR,
                    message: error?.message
                        || t("console:manage.features.claims.local.notifications.deleteClaim.genericError.message")
                }
            ));
        });
    };

    /**
     * This shows a popup with a delay of 500 ms.
     *
     * @param {React.Dispatch<React.SetStateAction<boolean>>} callback The state dispatch method.
     * @param {React.MutableRefObject<any>} ref The ref object carrying the `setTimeout` ID.
     */
    const delayPopup = (
        callback: React.Dispatch<React.SetStateAction<boolean>>,
        ref: React.MutableRefObject<any>
    ): void => {
        ref.current = setTimeout(() => callback(true), 500);
    };

    /**
     * This closes the popup.
     *
     * @param {React.Dispatch<React.SetStateAction<boolean>>} callback The state dispatch method.
     * @param {React.MutableRefObject<any>} ref The ref object carrying the `setTimeout` ID.
     */
    const closePopup = (
        callback: React.Dispatch<React.SetStateAction<boolean>>,
        ref: React.MutableRefObject<any>
    ): void => {
        clearTimeout(ref.current);
        callback(false);
    };

    /**
     * Fetch the updated SCIM2 schema list.
     */
    const fetchUpdatedSchemaList = (): void => {
        dispatch(setProfileSchemaRequestLoadingStatus(true));

        getProfileSchemas()
            .then((response: ProfileSchemaInterface[]) => {
                dispatch(setSCIMSchemas<ProfileSchemaInterface[]>(response));
            })
            .catch((error: IdentityAppsApiException) => {
                if (error?.response?.data?.description) {
                    dispatch(addAlert({
                            description: error.response.data.description,
                            level: AlertLevels.ERROR,
                            message: t("console:manage.notifications.getProfileSchema.error.message")
                        })
                    );
                }

                dispatch(
                    addAlert<AlertInterface>({
                        description: t(
                            "console:manage.notifications.getProfileSchema.genericError.description"
                        ),
                        level: AlertLevels.ERROR,
                        message: t(
                            "console:manage.notifications.getProfileSchema.genericError.message"
                        )
                    })
                );
            })
            .finally(() => {
                dispatch(setProfileSchemaRequestLoadingStatus(false));
            });
    };

    /**
     * Scrolls to the first field that throws an error.
     *
     * @param {string} field The name of the field.
     */
    const scrollToInValidField = (field: string): void => {
        const options: ScrollIntoViewOptions = {
            behavior: "smooth",
            block: "center"
        };

        switch (field) {
            case "name":
                nameField.current.scrollIntoView(options);
                break;
            case "description":
                descriptionField.current.scrollIntoView(options);
                break;
        }
    };

    return (
        <>
            { confirmDelete && deleteConfirmation() }
            <EmphasizedSegment padded="very">
                <Grid>
                    <Grid.Row columns={ 1 }>
                        <Grid.Column tablet={ 16 } computer={ 12 } largeScreen={ 9 } widescreen={ 6 } mobile={ 16 }>
                            <Form>
                                <Form.Field
                                    data-testid={ `${ testId }-form-attribute-uri-readonly-input` }
                                >
                                    <label>{ t("console:manage.features.claims.local.attributes.attributeURI") }</label>
                                    <CopyInputField value={ claim ? claim.claimURI : "" } />
                                    <Hint>Unique identifier of the attribute.</Hint>
                                </Form.Field>
                            </Form>
                        </Grid.Column>
                    </Grid.Row>
                </Grid>
                <Forms
                    onSubmit={ (values) => {
                        const data: Claim = {
                            attributeMapping: claim.attributeMapping,
                            claimURI: claim.claimURI,
                            description: values.get("description").toString(),
                            displayName: values.get("name").toString(),
                            displayOrder: claim.displayOrder > 0 ? claim.displayOrder : DEFAULT_ATTRIBUTE_DISPLAY_ORDER,
                            properties: claim.properties,
                            readOnly: values.get("readOnly").length > 0,
                            regEx: values.get("regularExpression").toString(),
                            required: values.has("required") ? (values.get("required").length > 0) : claim.required,
                            supportedByDefault: values.get("supportedByDefault").length > 0

                        };
                        updateAClaim(claim.id, data).then(() => {
                            dispatch(addAlert(
                                {
                                    description: t("console:manage.features.claims.local.notifications." +
                                        "updateClaim.success.description"),
                                    level: AlertLevels.SUCCESS,
                                    message: t("console:manage.features.claims.local.notifications." +
                                        "updateClaim.success.message")
                                }
                            ));
                            update();
                            fetchUpdatedSchemaList();
                        }).catch(error => {
                            dispatch(addAlert(
                                {
                                    description: error?.description
                                        || t("console:manage.features.claims.local.notifications.updateClaim." +
                                            "genericError.description"),
                                    level: AlertLevels.ERROR,
                                    message: error?.message
                                        || t("console:manage.features.claims.local.notifications." +
                                            "updateClaim.genericError.description")
                                }
                            ));
                        });
                    } }
                    onSubmitError={ (requiredFields: Map<string, boolean>, validFields: Map<string, Validation>) => {
                        const iterator = requiredFields.entries();
                        let result = iterator.next();

                        while (!result.done) {
                            if (!result.value[ 1 ] || !validFields.get(result.value[ 0 ]).isValid) {
                                scrollToInValidField(result.value[ 0 ]);
                                break;
                            } else {
                                result = iterator.next();
                            }
                        }
                    } }
                >
                    <Grid>
                        <Grid.Row columns={ 1 }>
                            <Grid.Column tablet={ 16 } computer={ 12 } largeScreen={ 9 } widescreen={ 6 } mobile={ 16 }>
                                <Field
                                    type="text"
                                    name="name"
                                    label={ t("console:manage.features.claims.local.forms.name.label") }
                                    required={ true }
                                    requiredErrorMessage={ t("console:manage.features.claims.local.forms." +
                                        "name.requiredErrorMessage") }
                                    placeholder={ t("console:manage.features.claims.local.forms.name.placeholder") }
                                    value={ claim?.displayName }
                                    ref={ nameField }
                                    validation={ (value: string, validation: Validation) => {
                                        if (!value.toString().match(/^[A-za-z0-9#+._\-\s]{1,30}$/)) {
                                            validation.isValid = false;
                                            validation.errorMessages.push(t("console:manage.features.claims.local" +
                                                ".forms.name.validationErrorMessages.invalidName"));
                                        }
                                    }}
                                    data-testid={ `${ testId }-form-name-input` }
                                />
                                <Hint>
                                    The display name of the attribute in the user profile.
                                </Hint>
                                <Divider hidden />
                                <Field
                                    type="textarea"
                                    name="description"
                                    label={ t("console:manage.features.claims.local.forms.description.label") }
                                    required={ true }
                                    requiredErrorMessage={ t("console:manage.features.claims.local.forms.description." +
                                        "requiredErrorMessage") }
                                    placeholder={
                                        t("console:manage.features.claims.local.forms.description.placeholder")
                                    }
                                    ref={ descriptionField }
                                    value={ claim?.description }
                                    data-testid={ `${ testId }-form-description-input` }
                                />
                                <Hint>A meaningful description for the attribute.</Hint>
                                <Divider hidden />
                                <Field
                                    type="text"
                                    name="regularExpression"
                                    label={ t("console:manage.features.claims.local.forms.regEx.label") }
                                    required={ false }
                                    requiredErrorMessage=""
                                    placeholder={ t("console:manage.features.claims.local.forms.regEx.placeholder") }
                                    value={ claim?.regEx }
                                    ref={ regExField }
                                    data-testid={ `${ testId }-form-regex-input` }
                                />
                                <Hint>Use a regex pattern to validate the attribute input value.</Hint>
                                <Divider hidden />
                                <Field
                                    type="checkbox"
                                    name="supportedByDefault"
                                    required={ false }
                                    requiredErrorMessage=""
                                    children={ [ {
                                        label: "Display this attribute on the user's profile",
                                        value: "Support"
                                    } ] }
                                    value={ claim?.supportedByDefault ? [ "Support" ] : [] }
                                    listen={ (values: Map<string, FormValue>) => {
                                        // setIsShowDisplayOrder(values?.get("supportedByDefault")?.length > 0);
                                    } }
                                    data-testid={ `${ testId }-form-supported-by-default-input` }
                                />
                                {
                                    isShowDisplayOrder
                                    && (
                                        <>
                                            <Field
                                                type="number"
                                                min="0"
                                                name="displayOrder"
                                                label={ t("console:manage.features.claims.local.forms.displayOrder" +
                                                    ".label") }
                                                required={ false }
                                                requiredErrorMessage=""
                                                placeholder={ t("console:manage.features.claims.local.forms." +
                                                    "displayOrder.placeholder") }
                                                value={ claim?.displayOrder.toString() }
                                                onMouseOver={ () => {
                                                    delayPopup(setIsShowDisplayOrderHint, displayTimer);
                                                } }
                                                onMouseOut={ () => {
                                                    closePopup(setIsShowDisplayOrderHint, displayTimer);
                                                } }
                                                ref={ displayOrderField }
                                                data-testid={ `${ testId }-form-display-order-input` }
                                            />
                                            <Popup
                                                content={
                                                    t("console:manage.features.claims.local.forms.displayOrderHint")
                                                }
                                                inverted
                                                open={ isShowDisplayOrderHint }
                                                trigger={ <span></span> }
                                                onClose={ () => {
                                                    closePopup(setIsShowDisplayOrderHint, displayTimer);
                                                } }
                                                position="bottom left"
                                                context={ displayOrderField }
                                            />
                                        </>
                                    )
                                }
                                {/* <Divider hidden />
                                <Field
                                    type="checkbox"
                                    name="required"
                                    required={ false }
                                    requiredErrorMessage=""
                                    children={ [ {
                                        label: t("console:manage.features.claims.local.forms.required.label"),
                                        value: "Required"
                                    } ] }
                                    value={ claim?.required ? [ "Required" ] : [] }
                                    data-testid={ `${ testId }-form-required-checkbox` }
                                /> */}
                                <Divider hidden />
                                <Field
                                    type="checkbox"
                                    name="readOnly"
                                    required={ false }
                                    requiredErrorMessage=""
                                    children={ [ {
                                        label: "Make this attribute read-only on the user's profile",
                                        value: "ReadOnly"
                                    } ] }
                                    value={ claim?.readOnly ? [ "ReadOnly" ] : [] }
                                    data-testid={ `${ testId }-form-readonly-checkbox` }
                                />
                            </Grid.Column>
                        </Grid.Row>
                        <Grid.Row columns={ 1 }>
                            <Grid.Column width={ 6 }>
                                <PrimaryButton
                                    type="submit"
                                    value={ t("common:update") }
                                    data-testid={ `${ testId }-form-submit-button` }
                                />
                            </Grid.Column>
                        </Grid.Row>
                    </Grid>
                </Forms>
            </EmphasizedSegment>
            <Divider hidden />
            {/* <DangerZoneGroup
                sectionHeader={ t("common:dangerZone") }
                data-testid={ `${ testId }-danger-zone-group` }
            >
                <DangerZone
                    actionTitle={ t("console:manage.features.claims.local.dangerZone.actionTitle") }
                    header={ t("console:manage.features.claims.local.dangerZone.header") }
                    subheader={ t("console:manage.features.claims.local.dangerZone.subheader") }
                    onActionClick={ () => setConfirmDelete(true) }
                    data-testid={ `${ testId }-local-claim-delete-danger-zone` }
                />
            </DangerZoneGroup> */}
        </>
    );
};

/**
 * Default props for the component.
 */
EditBasicDetailsLocalClaims.defaultProps = {
    "data-testid": "local-claims-basic-details-edit"
};
