/**
 * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

import {
    Field,
    FormValue,
    Validation
} from "@wso2is/forms";
import { I18n } from "@wso2is/i18n";
import { GenericIcon, Hint } from "@wso2is/react-components";
import { FormValidation } from "@wso2is/validation";
import React, { ReactElement } from "react";
import {
    CommonPluggableComponentMetaPropertyInterface,
    CommonPluggableComponentPropertyInterface
} from "../../../models";
import { Grid } from "semantic-ui-react";

export const getConfidentialField = (eachProp: CommonPluggableComponentPropertyInterface,
                                     propertyMetadata: CommonPluggableComponentMetaPropertyInterface,
                                     testId?: string): ReactElement => {
    return (
        <>
            <Field
                showPassword="Show Secret"
                hidePassword="Hide Secret"
                label={ propertyMetadata?.displayName }
                name={ propertyMetadata?.key }
                placeholder={ propertyMetadata?.defaultValue }
                required={ propertyMetadata?.isMandatory }
                requiredErrorMessage={ I18n.instance.t("console:develop.features.authenticationProvider.forms.common." +
                    "requiredErrorMessage") }
                value={ eachProp?.value }
                type="password"
                disabled={ propertyMetadata?.isDisabled }
                readOnly={ propertyMetadata?.readOnly }
                validation={ (value: string, validation: Validation) => {
                    if (propertyMetadata?.maxLength && value.length > propertyMetadata.maxLength) {
                        validation.isValid = false;
                        validation.errorMessages.push( propertyMetadata.displayName + " cannot have more than " +
                            propertyMetadata.maxLength + " characters.");
                    }
                } }
                data-testid={ `${ testId }-${ propertyMetadata?.key }` }
            />
            { propertyMetadata?.description && (
                <Hint disabled={ propertyMetadata?.isDisabled }>{ propertyMetadata?.description }</Hint>
            ) }
        </>
    );
};

export const getCheckboxField = (eachProp: CommonPluggableComponentPropertyInterface,
                                 propertyMetadata: CommonPluggableComponentMetaPropertyInterface,
                                 testId?: string): ReactElement => {
    return (
        <>
            <Field
                name={ propertyMetadata?.key }
                key={ propertyMetadata?.key }
                type="checkbox"
                required={ false }
                value={ (eachProp?.value == "true") ? [eachProp?.key] : [] }
                requiredErrorMessage={ I18n.instance.t("console:develop.features.authenticationProvider.forms.common." +
                    "requiredErrorMessage") }
                children={
                    [
                        {
                            label: propertyMetadata?.displayName,
                            value: eachProp ? eachProp.key : propertyMetadata?.key
                        }
                    ]
                }
                disabled={ propertyMetadata?.isDisabled }
                readOnly={ propertyMetadata?.readOnly }
                data-testid={ `${ testId }-${ propertyMetadata?.key }` }
            />
            { propertyMetadata?.description && (
                <Hint disabled={ propertyMetadata?.isDisabled }>{ propertyMetadata?.description }</Hint>
            ) }
        </>
    );
};

export const getCheckboxFieldWithListener = (eachProp: CommonPluggableComponentPropertyInterface,
                                             propertyMetadata: CommonPluggableComponentMetaPropertyInterface,
                                             listen: (key: string, values: Map<string, FormValue>) => void,
                                             testId?: string): ReactElement => {
    return (
        <>
            <Field
                name={ propertyMetadata?.key }
                key={ propertyMetadata?.key }
                type="checkbox"
                required={ propertyMetadata?.isMandatory }
                value={ eachProp?.value ? [eachProp?.key] : [] }
                requiredErrorMessage={ I18n.instance.t("console:develop.features.authenticationProvider.forms.common." +
                    "requiredErrorMessage") }
                children={
                    [
                        {
                            label: propertyMetadata?.displayName,
                            value: eachProp ? eachProp.key : propertyMetadata?.key
                        }
                    ]
                }
                listen={ (values: Map<string, FormValue>) => {
                    listen(propertyMetadata.key, values);
                } }
                disabled={ propertyMetadata?.isDisabled }
                readOnly={ propertyMetadata?.readOnly }
                data-testid={ `${ testId }-${ propertyMetadata?.key }` }
            />
            { propertyMetadata?.description && (
                <Hint disabled={ propertyMetadata?.isDisabled }>{ propertyMetadata?.description }</Hint>
            ) }
        </>
    );
};

export const getTextField = (eachProp: CommonPluggableComponentPropertyInterface,
                             propertyMetadata: CommonPluggableComponentMetaPropertyInterface,
                             testId?: string): ReactElement => {
    return (
        <>
            <Field
                name={ propertyMetadata?.key }
                label={ propertyMetadata?.displayName }
                required={ propertyMetadata?.isMandatory }
                requiredErrorMessage={ I18n.instance.t("console:develop.features.authenticationProvider.forms.common." +
                    "requiredErrorMessage") }
                placeholder={ propertyMetadata?.defaultValue }
                type="text"
                value={ eachProp?.value }
                key={ eachProp?.key }
                disabled={ propertyMetadata?.isDisabled }
                readOnly={ propertyMetadata?.readOnly }
                data-testid={ `${ testId }-${ propertyMetadata?.key }` }
                validation={ (value: string, validation: Validation) => {
                    if (propertyMetadata?.regex && !RegExp(propertyMetadata.regex).test(value)) {
                        validation.isValid = false;
                        validation.errorMessages.push(I18n.instance.t("console:manage.features.users.forms." +
                            "validation.formatError", {
                            field: propertyMetadata?.displayName
                        }));
                    }
                    if (propertyMetadata?.maxLength && value.length > propertyMetadata.maxLength) {
                        validation.isValid = false;
                        validation.errorMessages.push( propertyMetadata.displayName + " cannot have more than " +
                        propertyMetadata.maxLength + " characters.");
                    }
                } }
            />
            { propertyMetadata?.description && (
                <Hint disabled={ propertyMetadata?.isDisabled }>{ propertyMetadata?.description }</Hint>
            ) }
        </>
    );
};

export const getURLField = (eachProp: CommonPluggableComponentPropertyInterface,
                            propertyMetadata: CommonPluggableComponentMetaPropertyInterface,
                            testId?: string): ReactElement => {
    return (
        <>
            <Field
                name={ propertyMetadata?.key }
                label={ propertyMetadata?.displayName }
                required={ propertyMetadata?.isMandatory }
                requiredErrorMessage={ I18n.instance.t("console:develop.features.authenticationProvider.forms.common." +
                    "requiredErrorMessage") }
                placeholder={ propertyMetadata?.defaultValue }
                validation={ (value, validation) => {
                    if (!FormValidation.url(value)) {
                        validation.isValid = false;
                        validation.errorMessages.push(
                            I18n.instance.t("console:develop.features.authenticationProvider.forms." +
                                "common.invalidURLErrorMessage"));
                    }
                } }
                type="text"
                value={ eachProp?.value }
                key={ propertyMetadata?.key }
                disabled={ propertyMetadata?.isDisabled }
                readOnly={ propertyMetadata?.readOnly }
                data-testid={ `${ testId }-${ propertyMetadata?.key }` }
            />
            { propertyMetadata?.description && (
                <Hint disabled={ propertyMetadata?.isDisabled }>{ propertyMetadata?.description }</Hint>
            ) }
        </>
    );
};

export const getQueryParamsField = (eachProp: CommonPluggableComponentPropertyInterface,
                                    propertyMetadata: CommonPluggableComponentMetaPropertyInterface,
                                    testId?: string): ReactElement => {
    return (
        <>
            <Field
                name={ propertyMetadata?.key }
                label={ propertyMetadata?.displayName }
                required={ propertyMetadata?.isMandatory }
                requiredErrorMessage={ I18n.instance.t("console:develop.features.authenticationProvider.forms.common." +
                    "requiredErrorMessage") }
                validation={ (value, validation) => {
                    if (!FormValidation.url("https://www.sample.com?" + value)) {
                        validation.isValid = false;
                        validation.errorMessages.push(
                            I18n.instance.t("console:develop.features.authenticationProvider.forms.common." +
                                "invalidQueryParamErrorMessage"));
                    }
                } }
                type="queryParams"
                value={ eachProp?.value }
                key={ propertyMetadata?.key }
                disabled={ propertyMetadata?.isDisabled }
                readOnly={ propertyMetadata?.readOnly }
                data-testid={ `${ testId }-${ propertyMetadata?.key }` }
            />
            { propertyMetadata?.description && (
                <Hint disabled={ propertyMetadata?.isDisabled }>{ propertyMetadata?.description }</Hint>
            ) }
        </>
    );
};

export const getTableField = (eachProp: CommonPluggableComponentPropertyInterface,
                              propertyMetadata: CommonPluggableComponentMetaPropertyInterface,
                              testId?: string): ReactElement => {
    return (
        <>
            <div className="field read-only">
                <div>
                    <div className="field">
                        <label>Scopes</label>
                    </div>
                </div>
            </div>
            <Field
                hidden={ true }
                name={ propertyMetadata?.key }
                label={ propertyMetadata?.displayName }
                required={ propertyMetadata?.isMandatory }
                requiredErrorMessage={ I18n.instance.t("console:develop.features.authenticationProvider.forms.common." +
                    "requiredErrorMessage") }
                placeholder={ propertyMetadata?.defaultValue }
                type="text"
                value={ eachProp?.value }
                key={ eachProp?.key }
                disabled={ propertyMetadata?.isDisabled }
                readOnly={ propertyMetadata?.readOnly }
                data-testid={ `${ testId }-${ propertyMetadata?.key }` }
                validation={ (value: string, validation: Validation) => {
                    if (propertyMetadata?.regex && !RegExp(propertyMetadata.regex).test(value)) {
                        validation.isValid = false;
                        validation.errorMessages.push(I18n.instance.t("console:manage.features.users.forms." +
                            "validation.formatError", {
                            field: propertyMetadata?.displayName
                        }));
                    }
                    if (propertyMetadata?.maxLength && value.length > propertyMetadata.maxLength) {
                        validation.isValid = false;
                        validation.errorMessages.push( propertyMetadata.displayName + " cannot have more than " +
                            propertyMetadata.maxLength + " characters.");
                    }
                } }
            />
            { eachProp?.value.split(" ").map(scope => {
                return (
                    <Grid key={ scope } verticalAlign="middle">
                        <Grid.Row columns={ 2 }>
                            <Grid.Column mobile={ 8 } tablet={ 8 } computer={ 2 }>
                                <GenericIcon
                                    icon={ propertyMetadata.properties[scope].icon }
                                    size="micro"
                                    square
                                    transparent
                                    inline
                                    className="left-icon"
                                    verticalAlign="middle"
                                    spaced="right"
                                />
                                <label data-testid={ `${ testId }-authorize-label` }>
                                    { scope }
                                </label>
                            </Grid.Column>
                            <Grid.Column mobile={ 8 } tablet={ 8 } computer={ 14 }>
                                <label data-testid={ `${ testId }-authorize-label` }>
                                    { propertyMetadata.properties[scope].description }
                                </label>
                            </Grid.Column>
                        </Grid.Row>
                    </Grid>
                );
            }) }
            { propertyMetadata?.description && (
                <Hint disabled={ propertyMetadata?.isDisabled }>{ propertyMetadata?.description }</Hint>
            ) }
        </>
    );
};

export const getDropDownField = (eachProp: CommonPluggableComponentPropertyInterface,
                                 propertyMetadata: CommonPluggableComponentMetaPropertyInterface,
                                 testId?: string): ReactElement => {
    return (
        <>
            <Field
                name={ eachProp?.key || propertyMetadata?.key }
                label={ propertyMetadata?.displayName }
                required={ propertyMetadata?.isMandatory }
                requiredErrorMessage={ I18n.instance.t("console:develop.features.authenticationProvider.forms.common." +
                    "requiredErrorMessage") }
                type="dropdown"
                value={ eachProp?.value || propertyMetadata?.key }
                key={ eachProp?.key || propertyMetadata?.key }
                children={ getDropDownChildren(eachProp?.key, propertyMetadata?.options) }
                disabled={ propertyMetadata?.isDisabled }
                readOnly={ propertyMetadata?.readOnly }
                data-testid={ `${ testId }-${ propertyMetadata?.key }` }
            />
            { propertyMetadata?.description && (
                <Hint disabled={ propertyMetadata?.isDisabled }>{ propertyMetadata?.description }</Hint>
            ) }
        </>
    );
};

const getDropDownChildren = (key: string, options: string[]) => {
    return options.map((option) => {
        return ({
            key: key,
            text: option,
            value: option
        });
    });
};

/**
 * Each field type.
 */
export enum FieldType {
    CHECKBOX = "CheckBox",
    TEXT = "Text",
    TABLE = "Table",
    CONFIDENTIAL = "Confidential",
    URL = "URL",
    QUERY_PARAMS = "QueryParameters",
    DROP_DOWN = "DropDown"
}

/**
 * commonly used constants.
 */
export enum CommonConstants {
    BOOLEAN = "BOOLEAN",
    FIELD_COMPONENT_KEYWORD_URL = "URL",
    FIELD_COMPONENT_KEYWORD_QUERY_PARAMETER = "QUERYPARAM",
    SCOPE_KEY = "scopes"
}

/**
 * Get interpreted field type for given property metada.
 *
 * @param propertyMetadata Property metadata of type {@link CommonPluggableComponentMetaPropertyInterface}.
 */
export const getFieldType = (propertyMetadata: CommonPluggableComponentMetaPropertyInterface): FieldType => {

    if (propertyMetadata?.type?.toUpperCase() === CommonConstants.BOOLEAN) {
        return FieldType.CHECKBOX;
    } else if (propertyMetadata?.isConfidential) {
        return FieldType.CONFIDENTIAL;
    } else if (propertyMetadata?.key === CommonConstants.SCOPE_KEY) {
        return FieldType.TABLE;
    } else if (propertyMetadata?.key.toUpperCase().includes(CommonConstants.FIELD_COMPONENT_KEYWORD_URL)) {
        // todo Need proper backend support to identity URL fields.
        return FieldType.URL;
    } else if (propertyMetadata?.key.toUpperCase().includes(
        CommonConstants.FIELD_COMPONENT_KEYWORD_QUERY_PARAMETER)) {
        // todo Need proper backend support to identity Query parameter fields.
        return FieldType.QUERY_PARAMS;
    } else if (propertyMetadata?.options?.length > 0) {
        return FieldType.DROP_DOWN;
    }
    return FieldType.TEXT;
};

/**
 * Get corresponding {@link Field} component for the provided property.
 *
 * @param property Property of type {@link CommonPluggableComponentPropertyInterface}.
 * @param propertyMetadata Property metadata of type.
 * @param testId Test ID for the form field.
 * @param listen Listener method for the on change events of a checkbox field
 * @return Corresponding property field.
 */
export const getPropertyField = (property: CommonPluggableComponentPropertyInterface,
                                 propertyMetadata: CommonPluggableComponentMetaPropertyInterface,
                                 listen?: (key: string, values: Map<string, FormValue>) => void,
                                 testId?: string):
    ReactElement => {

    switch (getFieldType(propertyMetadata)) {
        case FieldType.CHECKBOX : {
            if (listen) {
                return getCheckboxFieldWithListener(property, propertyMetadata, listen, testId);
            }
            return getCheckboxField(property, propertyMetadata, testId);
        }
        case FieldType.CONFIDENTIAL : {
            return getConfidentialField(property, propertyMetadata, testId);
        }
        case FieldType.URL : {
            return getURLField(property, propertyMetadata, testId);
        }
        case FieldType.QUERY_PARAMS : {
            return getQueryParamsField(property, propertyMetadata, testId);
        }
        case FieldType.DROP_DOWN : {
            return getDropDownField(property, propertyMetadata, testId);
        }
        case FieldType.TABLE:{
            return getTableField(property, propertyMetadata, testId);
        }
        default: {
            return getTextField(property, propertyMetadata, testId);
        }
    }
};
