import { makeStyles, Typography } from '@material-ui/core';
import PropTypes from 'prop-types';
import React, { useMemo, useState, useCallback, useEffect } from 'react';
import { Form, FormButtons, SubmitButton } from '../../utilities/form';
import { useCrudActions } from '../../hooks/crudActions';
import { toLowerFirstChar } from '../../utilities/input/StringUtilities';
import useDefaultFormValues from '../../utilities/crud/useDefaultFormValues';
import { getPathObject, getPrimaryKeyValues } from '../../utilities/maintenanceEngine/CrudBrowserUtilities';
import { CardinalityTypeEnums, EntityAttributeEnums, UdpEnums } from '../../enums/unitySystemEnums';
import { UdpCrudFormFields } from './UdpCrudFormFields';
import { usePromotedMethodEntity } from '../../hooks/usePromotedMethodEntity';
import { executeQueryWithParameters } from '../../udp/utilities/methodUtils';
import { differenceWith } from 'lodash-es';
import { useHandleAxiosSnackbar } from '../../utilities/apiHelpers';
import { getSanitizedName } from '../../utilities/entities/entityUtilities';
import { TagEnums } from '../../enums/tagEnums';
const useStyles = makeStyles((theme) => ({
    actions: {
        marginTop: theme.spacing(2),
        marginBottom: theme.spacing(2),
        display: 'flex',
        flexDirection: 'row'
    },
    title: {
        flexGrow: 1
    },
    subject: {
        marginLeft: theme.spacing(1),
        display: 'flex',
        flexDirection: 'row',
        flexGrow: 1
    },
    switchRoot: {
        marginLeft: theme.spacing(1)
    },
    input: {
        height: 34,
        padding: 0
    },
    formTextInput: {
        fontSize: 13
    },
    labelRoot: {
        fontSize: 13
    }
}));
export const CrudForm = (props) => {
    const { entityAttributes, udpFieldInfos, foreignKeyData, crudTableEditionData, closeSideSheet, onCreate, onUpdate, isNewEntry, entityData, entityName } = props;
    const { renderInputByFieldType } = useCrudActions();
    const [fkData, setFkData] = useState(foreignKeyData === null || foreignKeyData === void 0 ? void 0 : foreignKeyData.map((fkd) => (Object.assign({}, fkd))));
    const { data: promotedEntity } = usePromotedMethodEntity(entityName);
    const { data: udpTagAssocPromotedEntity } = usePromotedMethodEntity('UdpTagAssociation', true);
    const { defaultFormValues } = useDefaultFormValues(entityAttributes);
    const [crudTableFormData, setCrudTableFormData] = useState(crudTableEditionData || defaultFormValues);
    const [existingTagAssociation, setExistingTagAssociation] = useState([]);
    const classes = useStyles();
    const { handleErrorSnackbar } = useHandleAxiosSnackbar();
    const primaryKeyValues = useMemo(() => getPrimaryKeyValues(entityAttributes, crudTableEditionData), [entityAttributes, crudTableEditionData]);
    const getEntityFull = useCallback(async () => {
        var _a, _b;
        if (!primaryKeyValues ||
            !primaryKeyValues.length ||
            !((_a = promotedEntity === null || promotedEntity === void 0 ? void 0 : promotedEntity.unityBaseGetFullMethod) === null || _a === void 0 ? void 0 : _a.apiMethodId) ||
            !((_b = promotedEntity === null || promotedEntity === void 0 ? void 0 : promotedEntity.unityBaseUpdateMethod) === null || _b === void 0 ? void 0 : _b.pathParameters)) {
            return null;
        }
        const pathObject = getPathObject(promotedEntity.unityBaseUpdateMethod.pathParameters, primaryKeyValues);
        let data = { requestBody: {} };
        if (Array.isArray(pathObject)) {
            pathObject.forEach((po) => {
                data[Object.keys(po)[0]] = po[Object.keys(po)[0]];
            });
        }
        else {
            data = Object.assign(Object.assign({}, pathObject), { requestBody: {} });
        }
        const response = await executeQueryWithParameters({ data }, promotedEntity.unityBaseGetFullMethod.apiMethodId).catch((res) => {
            console.error(res);
        });
        if ((response === null || response === void 0 ? void 0 : response.status) === 200) {
            return response.data;
        }
        else {
            console.error(`Could not retrieve full data for ${promotedEntity === null || promotedEntity === void 0 ? void 0 : promotedEntity.entityName}`);
        }
        return null;
    }, [promotedEntity, primaryKeyValues]);
    // we'll need this to determine which property names are used as keys for many-to-many
    const manyToManyPropertyList = useMemo(() => {
        if (!(entityAttributes === null || entityAttributes === void 0 ? void 0 : entityAttributes.length))
            return [];
        const mtmPropertyList = new Set();
        entityAttributes.forEach((attr) => {
            var _a;
            if ((_a = attr === null || attr === void 0 ? void 0 : attr.extendedProperties) === null || _a === void 0 ? void 0 : _a.childEntity) {
                // eslint-disable-next-line @typescript-eslint/no-unused-expressions
                Object.keys(attr.extendedProperties.childEntityAssoc || {}).length &&
                    attr.extendedProperties.childEntityAssoc.Cardinality ===
                        CardinalityTypeEnums.Many_To_Many &&
                    attr.extendedProperties.childEntityAssoc.Keys.forEach((value, i) => {
                        const name = value.split(',')[0];
                        if (name) {
                            // check name string: if 1 word, lowercase. if 2 words, lower first char
                            mtmPropertyList.add(name.toLowerCase());
                        }
                    });
            }
        });
        return mtmPropertyList;
    }, [entityAttributes]);
    const getJoiningObj = (schemaAttribute, ceAttributes) => {
        return ceAttributes.find((ceAttr) => {
            var _a;
            return Object.keys(((_a = ceAttr === null || ceAttr === void 0 ? void 0 : ceAttr.extendedProperties) === null || _a === void 0 ? void 0 : _a.childEntityAssoc) || {})
                .length &&
                ceAttr.extendedProperties.childEntityAssoc.Keys.find((a) => {
                    const fieldId = a.split(',')[0];
                    return ((/^[A-Z]*$/.test(fieldId)
                        ? fieldId.toLowerCase()
                        : toLowerFirstChar(fieldId)) ===
                        toLowerFirstChar(schemaAttribute.name));
                });
        });
    };
    const renderAttribute = useCallback((schemaAttribute, fieldInfo) => {
        var _a;
        let render = !(fieldInfo === null || fieldInfo === void 0 ? void 0 : fieldInfo.isIdentity);
        if (((_a = schemaAttribute === null || schemaAttribute === void 0 ? void 0 : schemaAttribute.extendedProperties) === null || _a === void 0 ? void 0 : _a.identifier) &&
            (fieldInfo === null || fieldInfo === void 0 ? void 0 : fieldInfo.dataType) === 'Guid') {
            render = false;
        }
        entityAttributes.every((schemaAttribute, index) => {
            const ceAttributes = entityAttributes.filter((ea) => ea.extendedProperties.childEntity &&
                ea.extendedProperties.childEntityAssoc.Cardinality ===
                    CardinalityTypeEnums.Many_To_Many &&
                ea.extendedProperties.childEntityAssoc.IsJoiningObject);
            if (getJoiningObj(schemaAttribute, ceAttributes)) {
                render = true;
                return false;
            }
            return true;
        });
        return render;
    }, [entityAttributes]);
    // function that helps transform form data for entites like UdpLookup, UdpTagType, UdpShortcutContent
    const transformDataForUdpMaintenance = useCallback((values) => {
        var _a;
        const sanitizedName = getSanitizedName(entityName === null || entityName === void 0 ? void 0 : entityName.toLowerCase());
        // Current entity to modify is a UdpLookup
        if (sanitizedName === UdpEnums.Udp_Lookup.toLowerCase()) {
            const attributeName = toLowerFirstChar(EntityAttributeEnums.Domain_Name);
            let domainName = values[attributeName];
            const modelNameSpaceStructure = (_a = domainName === null || domainName === void 0 ? void 0 : domainName.namespaceStructure) === null || _a === void 0 ? void 0 : _a.model;
            const sanitizedName = domainName === null || domainName === void 0 ? void 0 : domainName.sanitizedName;
            values[toLowerFirstChar(EntityAttributeEnums.Is_Product_Lookup)] = false;
            if (modelNameSpaceStructure) {
                values[attributeName] = `${modelNameSpaceStructure}.Models.${sanitizedName}`;
            }
            else {
                values[attributeName] = '';
            }
        }
        else if (sanitizedName === TagEnums.TagTypeSanitizedName.toLowerCase()) {
            values[toLowerFirstChar(EntityAttributeEnums.User_Defined)] = true;
        }
        else if (sanitizedName === UdpEnums.Udp_Shortcut_Content.toLowerCase()) {
            values[toLowerFirstChar(EntityAttributeEnums.Is_Product_ShortcutContent)] = false;
        }
    }, [entityName]);
    const tagAssociationComparator = (ta1, ta2) => {
        return (ta1 === null || ta1 === void 0 ? void 0 : ta1.id) === (ta2 === null || ta2 === void 0 ? void 0 : ta2.id);
    };
    // Callback to handle associating tags to business objects
    const handleTagAssociation = useCallback(async (values, businessObjectKey) => {
        // Get the tagAssociation objects to delete
        const tagAssociationToDelete = differenceWith(existingTagAssociation, values === null || values === void 0 ? void 0 : values.udpTags, tagAssociationComparator);
        // Get the tagAssociation objects to create
        const tagAssociationToCreate = differenceWith(values === null || values === void 0 ? void 0 : values.udpTags, existingTagAssociation, tagAssociationComparator);
        const deleteTagAssociationApiMethod = udpTagAssocPromotedEntity === null || udpTagAssocPromotedEntity === void 0 ? void 0 : udpTagAssocPromotedEntity.unityBaseDeleteMethod;
        const createTagAssociationApiMethod = udpTagAssocPromotedEntity === null || udpTagAssocPromotedEntity === void 0 ? void 0 : udpTagAssocPromotedEntity.unityBaseCreateMethod;
        if (businessObjectKey) {
            if (deleteTagAssociationApiMethod) {
                const deletedPromises = [];
                tagAssociationToDelete.forEach((tagAssocToDelete) => {
                    const data = {};
                    data['{tagId}'] = tagAssocToDelete.id;
                    data['{entityBusinessKey}'] = businessObjectKey;
                    deletedPromises.push(executeQueryWithParameters({ data }, deleteTagAssociationApiMethod.apiMethodId));
                });
                await Promise.all(deletedPromises.map((p) => p.catch((e) => e))).then((responses) => {
                    if (responses.length !== deletedPromises.length) {
                        console.warn(`Not all Tag Association deleted`);
                    }
                });
            }
            else {
                handleErrorSnackbar(null, 'Delete Tag Association Method Not Promoted');
            }
            if (createTagAssociationApiMethod) {
                const createdPromises = [];
                tagAssociationToCreate.forEach((tagAssoc) => {
                    const data = {
                        requestBody: {
                            tagId: tagAssoc.id,
                            entityBusinessKey: businessObjectKey
                        }
                    };
                    createdPromises.push(executeQueryWithParameters({ data }, createTagAssociationApiMethod.apiMethodId));
                });
                await Promise.all(createdPromises.map((p) => p.catch((e) => e))).then((responses) => {
                    responses.forEach((response) => {
                        if (response.status === 200) {
                            const entityBusinessKey = response.data.entityBusinessKey;
                            const matchingTagAssociation = tagAssociationToCreate.find((ta) => ta.value === entityBusinessKey);
                            if (!matchingTagAssociation) {
                                console.warn(`Tag Association not created for all file to entities`);
                            }
                        }
                    });
                });
                setExistingTagAssociation(values === null || values === void 0 ? void 0 : values.udpTags);
            }
            else {
                handleErrorSnackbar(null, 'Create Tag Association Method Not Promoted');
            }
        }
    }, [
        handleErrorSnackbar,
        existingTagAssociation,
        setExistingTagAssociation,
        udpTagAssocPromotedEntity
    ]);
    const handleSubmit = useCallback(async (values) => {
        var _a;
        let newTableData = Object.assign(Object.assign({}, crudTableFormData), values);
        transformDataForUdpMaintenance(newTableData);
        const isNewRow = !crudTableEditionData;
        if (!isNewEntry) {
            // remove identifiers from object
            entityAttributes
                .filter((ea) => {
                var _a, _b;
                const joiningObject = getJoiningObj(ea, entityAttributes);
                const fieldInfo = udpFieldInfos === null || udpFieldInfos === void 0 ? void 0 : udpFieldInfos.find((fi) => {
                    var _a;
                    return (fi === null || fi === void 0 ? void 0 : fi.name.toLowerCase()) === ((_a = ea === null || ea === void 0 ? void 0 : ea.name) === null || _a === void 0 ? void 0 : _a.toLowerCase());
                });
                return (((_a = ea === null || ea === void 0 ? void 0 : ea.extendedProperties) === null || _a === void 0 ? void 0 : _a.identifier) &&
                    (fieldInfo === null || fieldInfo === void 0 ? void 0 : fieldInfo.isIdentity) &&
                    (fieldInfo === null || fieldInfo === void 0 ? void 0 : fieldInfo.dataType) !== 'Guid' &&
                    (!joiningObject || ((_b = joiningObject.extendedProperties) === null || _b === void 0 ? void 0 : _b.childEntity)));
            })
                .forEach((ea) => {
                delete newTableData[ea.name];
            });
        }
        // transform values by fkData before creating/updating
        foreignKeyData.forEach((fkd) => {
            Object.keys(newTableData).forEach((key) => {
                var _a;
                if (key === fkd.fieldName) {
                    const fkData = (_a = fkd.data.find((d) => {
                        var _a;
                        return d.id === ((_a = newTableData[key]) === null || _a === void 0 ? void 0 : _a.id);
                    })) === null || _a === void 0 ? void 0 : _a.id;
                    if (fkData !== null) {
                        newTableData[key] = fkData;
                    }
                }
            });
        });
        let businessObjectKey = '';
        if (isNewRow) {
            if (onCreate) {
                const createdResponse = await onCreate(newTableData);
                if ((createdResponse === null || createdResponse === void 0 ? void 0 : createdResponse.status) === 200) {
                    businessObjectKey = (_a = createdResponse === null || createdResponse === void 0 ? void 0 : createdResponse.data) === null || _a === void 0 ? void 0 : _a.businessObjectKey;
                }
            }
        }
        else {
            if (onUpdate) {
                await onUpdate(newTableData, primaryKeyValues);
                businessObjectKey = newTableData === null || newTableData === void 0 ? void 0 : newTableData.businessObjectKey;
            }
        }
        if (businessObjectKey && (values === null || values === void 0 ? void 0 : values.udpTags)) {
            handleTagAssociation(values, businessObjectKey);
        }
        closeSideSheet && closeSideSheet();
    }, [
        crudTableFormData,
        transformDataForUdpMaintenance,
        crudTableEditionData,
        isNewEntry,
        foreignKeyData,
        closeSideSheet,
        entityAttributes,
        onCreate,
        onUpdate,
        primaryKeyValues,
        handleTagAssociation
    ]);
    const shouldAttributeRenderField = useCallback((schemaAttribute) => {
        const sanitizedName = getSanitizedName(entityName === null || entityName === void 0 ? void 0 : entityName.toLowerCase());
        if (sanitizedName === TagEnums.TagTypeSanitizedName.toLowerCase() &&
            (schemaAttribute === null || schemaAttribute === void 0 ? void 0 : schemaAttribute.name) ===
                toLowerFirstChar(EntityAttributeEnums.User_Defined)) {
            return false;
        }
        else if (sanitizedName === UdpEnums.Udp_Shortcut_Content.toLowerCase() &&
            (schemaAttribute === null || schemaAttribute === void 0 ? void 0 : schemaAttribute.name) ===
                toLowerFirstChar(EntityAttributeEnums.Is_Product_ShortcutContent)) {
            return false;
        }
        else if (sanitizedName === UdpEnums.Udp_Lookup.toLowerCase() &&
            (schemaAttribute === null || schemaAttribute === void 0 ? void 0 : schemaAttribute.name) ===
                toLowerFirstChar(EntityAttributeEnums.Is_Product_Lookup)) {
            return false;
        }
        return true;
    }, [entityName]);
    useEffect(() => {
        const getFullData = async () => {
            var _a;
            const fullEntityData = await getEntityFull();
            if (fullEntityData) {
                const filteredTags = (_a = fullEntityData.udpTags) === null || _a === void 0 ? void 0 : _a.map((tagAssoc) => tagAssoc.udpTagAssociationUdpTag);
                setExistingTagAssociation(filteredTags || []);
            }
        };
        if (promotedEntity && !isNewEntry) {
            getFullData();
        }
    }, [promotedEntity, getEntityFull, isNewEntry]);
    useEffect(() => {
        if (foreignKeyData) {
            setFkData(foreignKeyData === null || foreignKeyData === void 0 ? void 0 : foreignKeyData.map((fkd) => (Object.assign({}, fkd))));
        }
    }, [foreignKeyData]);
    return (React.createElement(Form, { onSubmit: handleSubmit }, React.createElement("div", null, entityAttributes === null || entityAttributes === void 0 ? void 0 : entityAttributes.map((schemaAttribute, index) => {
        const fieldInfo = udpFieldInfos === null || udpFieldInfos === void 0 ? void 0 : udpFieldInfos.find((fi) => {
            var _a;
            return (fi === null || fi === void 0 ? void 0 : fi.name.toLowerCase()) === ((_a = schemaAttribute === null || schemaAttribute === void 0 ? void 0 : schemaAttribute.name) === null || _a === void 0 ? void 0 : _a.toLowerCase());
        });
        // If the field is an identity and is Updating form, we will make the field readOnly
        if ((fieldInfo === null || fieldInfo === void 0 ? void 0 : fieldInfo.isPrimaryKey) && !isNewEntry) {
            schemaAttribute.disabled = true;
        }
        if ((fieldInfo === null || fieldInfo === void 0 ? void 0 : fieldInfo.isPrimaryKey) && isNewEntry && !(fieldInfo === null || fieldInfo === void 0 ? void 0 : fieldInfo.isIdentity)) {
            schemaAttribute.disabled = false;
        }
        if (!shouldAttributeRenderField(schemaAttribute)) {
            return null;
        }
        return (renderAttribute(schemaAttribute, fieldInfo) && (React.createElement(React.Fragment, { key: `${schemaAttribute.name}-${index}` }, React.createElement(Typography, { variant: "subtitle2", style: { marginTop: 8 } }, schemaAttribute.friendlyName), renderInputByFieldType(schemaAttribute, index, entityAttributes, fieldInfo, fkData, setFkData, crudTableFormData, classes, isNewEntry, entityData, manyToManyPropertyList))));
    }), React.createElement(UdpCrudFormFields, { entityName: entityName, formData: crudTableFormData, existingTagAssociation: existingTagAssociation })), React.createElement(FormButtons, null, React.createElement(SubmitButton, { id: "udpRecord-CrudForm-Submit", udprecordid: "udpRecord-CrudForm-Submit", variant: "contained", color: "secondary", disablePristine: false, disabled: false, style: { marginTop: 15 } }, "Sav"))));
};
CrudForm.propTypes = {
    /**
     * Entity attributes for selected entity within catalog
     */
    entityAttributes: PropTypes.array,
    /**
     * Foreign key data for selected entity within catalog
     */
    foreignKeyData: PropTypes.array,
    /**
     * Entity data
     */
    crudTableEditionData: PropTypes.array,
    /**
     * onClick event for closing sidesheet
     */
    closeSideSheet: PropTypes.func,
    /**
     * onClick event for creating
     */
    onCreate: PropTypes.func,
    /**
     * onClick event for updating
     */
    onUpdate: PropTypes.func
};
//# sourceMappingURL=CrudForm.js.map