import { List, Datagrid,
    ShowButton, EditButton, Loading, Show, Edit, SimpleShowLayout, SimpleForm,
     Create, TopToolbar, ExportButton, CreateButton, SaveButton, Toolbar, DeleteWithConfirmButton, SearchInput, useTranslate, EmptyClasses,
     useRecordSelection} from 'react-admin';
import { useContext} from "react";
import { AssociationMemberEndContext, ClassifierContext } from "../../utils/contexts";
import { useProperties } from "../../utils/properties";
import { InsertShowField , InsertListField, InsertEditField} from "../../utils/fields" ;
import { useResourceContext, useResourceDefinition,  useRecordContext, useCreatePath, usePermissions} from "react-admin";
import {  idFromURL, extractResourceFromPathName} from "../../utils/utils";
import {useParams, useLocation} from "react-router-dom";

import {Typography} from '@mui/material';
import Inbox from '@mui/icons-material/Inbox';
import { styled } from '@mui/material/styles';
import authProvider from '../../utils/authProvider';



const classFunctions = {

unfoldProperties: function (properties) {
        let allProps =[];
        properties.forEach ( (prop) => {
            if (prop.metatype === "_composite_type") { // attributes of composite type exploded
                prop.nested.forEach (( nested_prop) => {
                    if (nested_prop.metatype === "_composite_type") {  //property can be nested on two levels
                        nested_prop.nested.forEach ( (sub_nested) => {
                            allProps.push ({ property: sub_nested, prefix: 'properties' }) ;
                        })
                    } else {
                        allProps.push ({ property: nested_prop, prefix: 'properties' }) ;                
                    }
                } ) 
                    
            } else  {
                allProps.push ( {property: prop, prefix: 'properties'} ) ;   //  association members kept
                    if (prop.metatype === "_class") { // ids of association members exploded except object refs
                        prop.linked.forEach ( (linked) => {
                            if (linked.metatype !== "_class") {
                                if (linked.metatype === '_composite_type') {
                                    linked.nested.forEach((nested_prop) => {
                                        if (nested_prop.metatype === "_composite_type") {  //property can be nested on two levels

                                            nested_prop.nested.forEach((sub_nested) => {
                                                allProps.push({ property: sub_nested, prefix: `properties.${prop.name}.properties` });
                                            })
                                        } else {
                                            allProps.push({ property: nested_prop, prefix: `properties.${prop.name}.properties` });
                                        }
                                    }) 
                                } 
                                else {
                                    allProps.push ({ property: linked, prefix: `properties.${prop.name}.properties`}) ;
                                }
                            }
                        } )
                    }
    
            }
        })
        return allProps;
} ,

unfoldPropertiesNoLink: function (properties) {
    let allProps =[];
    properties.forEach ( (prop) => {
        if (prop.metatype === "_composite_type") { // attributes of composite type exploded
            prop.nested.forEach (( nested_prop) => {
                if (nested_prop.metatype === "_composite_type") {  //property can be nested on two levels
                    nested_prop.nested.forEach ( (sub_nested) => {
                        allProps.push ({ property: sub_nested, prefix: 'properties' }) ;
                    })
                } else {
                    allProps.push ({ property: nested_prop, prefix: 'properties' }) ;
                }
            } ) 
        } else if (prop.metatype === "_class") { // ids of association members exploded except object refs
                    prop.linked.forEach ( (linked) => {
                        if (linked.metatype !== "_class") {
                            if (linked.metatype === '_composite_type') {
                                linked.nested.forEach (( nested_prop) => {
                                    if (nested_prop.metatype === "_composite_type") {  //property can be nested on two levels
                                        nested_prop.nested.forEach ( (sub_nested) => {
                                            allProps.push ({ property: sub_nested, prefix: `properties.${prop.name}.properties`}) ;
                                        })
                                    } else {
                                        allProps.push ({ property: nested_prop, prefix: `properties.${prop.name}.properties` }) ;  
                                              
                                    }
                                } ) 
                            } 
                            else {
                                allProps.push ({ property: linked, prefix: `properties.${prop.name}.properties`}) ;
                            }
                    }
                    } )
        } else {
            allProps.push ( {property: prop, prefix: 'properties'} ) ;   //  association members not kept
        }

    })
    return allProps;
} ,

unfoldAllProperties: function (properties) {
    let allProps =[];
    properties.forEach ( (prop) => {
        if (prop.metatype === "_composite_type") { // attributes of composite type exploded
            prop.nested.forEach (( nested_prop) => {
                if (nested_prop.metatype === "_composite_type") {  //property can be nested on two levels
                    nested_prop.nested.forEach ( (sub_nested) => {
                        allProps.push ({ property: sub_nested, prefix: 'properties' }) ;
                    })
                } else {
                    allProps.push ({ property: nested_prop, prefix: 'properties' }) ;
                }
            } ) 
        } else  { // Association members included
            allProps.push ( {property: prop, prefix: 'properties'} ) ;
        }

    })
    return allProps;
} ,
associationMemberEnds: function (properties) {
    let scProps =[];
    properties.forEach ( (prop) => {
        if (prop.metatype === "_class") {
                scProps.push ( {property: prop, prefix: 'properties'} )  ;                 
        }
    })
    return scProps;
}
} ;

const assocFunctions = {
    unfoldProperties: function (properties) {
        let allProps =[];
        properties.forEach ( (prop) => {
            if (prop.metatype === "_composite_type") { // attributes of composite type exploded
                prop.nested.forEach (( nested_prop) => {
                    if (nested_prop.metatype === "_composite_type") {  //property can be nested on two levels
                        nested_prop.nested.forEach ( (sub_nested) => {
                            allProps.push ({ property: sub_nested, prefix: 'properties' }) ;
                        })
                    } else {
                        allProps.push ({ property: nested_prop, prefix: 'properties' }) ;
                    }
                } ) 
            } else {
                allProps.push ( {property: prop, prefix: 'properties'} ) ;   //  association members kept
                if (prop.metatype === "_class") { // ids of association members exploded except object refs
                    prop.linked.forEach ( (linked) => {
                        if (linked.metatype !== "_class") {
                            if (linked.metatype === '_composite_type') {
                                linked.nested.forEach (( nested_prop) => {
                                    if (nested_prop.metatype === "_composite_type") {  //property can be nested on two levels
                                        nested_prop.nested.forEach ( (sub_nested) => {
                                            allProps.push ({ property: sub_nested, prefix: `properties.${prop.name}.properties` }) ;
                                        })
                                    } else {
                                        allProps.push ({ property: nested_prop, prefix: `properties.${prop.name}.properties` }) ;  
                                    }
                                              } ) 
                            } 
                            else {
                                allProps.push ({ property: linked, prefix: `properties.${prop.name}.properties`}) ;
                            }
                    }
                    } )
                }
            }
    
        })
        return allProps;
    } ,
    unfoldNotRefProperties: function (properties) {
        let allProps =[];
        properties.forEach ( (prop) => {
            if (prop.metatype === "_composite_type") { // attributes of composite type exploded
                prop.nested.forEach (( nested_prop) => {
                    if (nested_prop.metatype === "_composite_type") {  //property can be nested on two levels
                        nested_prop.nested.forEach ( (sub_nested) => {
                            allProps.push ({ property: sub_nested, prefix: 'properties' }) ;
                        })
                    } else {
                        allProps.push ({ property: nested_prop, prefix: 'properties' }) ;
                    }
                } ) 
            } else if (prop.metatype !== "_class") {  // Association members included
                allProps.push ( {property: prop, prefix: 'properties'} ) ;
            }
    
        })
        return allProps;
    } ,

    unfoldAllProperties: function (properties) {
        let allProps =[];
        properties.forEach ( (prop) => {
            if (prop.metatype === "_composite_type") { // attributes of composite type exploded
                prop.nested.forEach (( nested_prop) => {
                    if (nested_prop.metatype === "_composite_type") {  //property can be nested on two levels
                        nested_prop.nested.forEach ( (sub_nested) => {
                            allProps.push ({ property: sub_nested, prefix: 'properties' }) ;
                        })
                    } else {
                        allProps.push ({ property: nested_prop, prefix: 'properties' }) ;
                    }
                } ) 
            } else  { // Association members included
                allProps.push ( {property: prop, prefix: 'properties'} ) ;
            }
    
        })
        return allProps;
    }  
}


const classFilters = ( loading ) => {
   // if (loading) return [];
    const arr1 = [ <SearchInput source="_s" alwaysOn />]

    return arr1;
    
        
    
}
// Variant from react-admin's Empty
const CustomEmpty = (props) => {
    const { className } = props;
    const { hasCreate } = useResourceDefinition(props);
    const resource = useResourceContext(props);

    const {classifierNames} = useContext(ClassifierContext);

    const csf_name = props.classifier  || classifierNames[resource];

    const translate = useTranslate();

    const emptyMessage = translate('ra.page.empty', { name: csf_name });
    const inviteMessage = translate('ra.page.invite');


    return (
        <Root className={className}>
            <div className={EmptyClasses.message}>
                <Inbox className={EmptyClasses.icon} />
                <Typography variant="h4" paragraph>
                    {translate(`resources.${resource}.empty`, {
                        _: emptyMessage,
                    })}
                </Typography>
                {hasCreate && (
                    <Typography variant="body1">
                        {translate(`resources.${resource}.invite`, {
                            _: inviteMessage,
                        })}
                    </Typography>
                )}
            </div>
            {hasCreate && (
                <div className={EmptyClasses.toolbar}>
                    <CreateButton variant="contained" />
                </div>
            )}
        </Root>
    );
};

//BEGIN variant of Root from react-admin's Empty which is not exported.
const Root = styled('span', {
    name: 'CustomEmpty',
    overridesResolver: (props, styles) => styles.root,
})(({ theme }) => ({
    flex: 1,
    [`& .${EmptyClasses.message}`]: {
        textAlign: 'center',
        opacity: theme.palette.mode === 'light' ? 0.5 : 0.8,
        margin: '0 1em',
        color:
            theme.palette.mode === 'light'
                ? 'inherit'
                : theme.palette.text.primary,
    },

    [`& .${EmptyClasses.icon}`]: {
        width: '9em',
        height: '9em',
    },

    [`& .${EmptyClasses.toolbar}`]: {
        textAlign: 'center',
        marginTop: '2em',
    },
}));
//END variant from react-admin's Empty

const ListActions = ({resource, meta }) => {
    const { hasCreate } = useResourceDefinition({resource: resource});
    return (
    <TopToolbar>
        { hasCreate && (<CreateButton resource={resource} />)}
        <ExportButton meta={meta} />
    </TopToolbar>
    )
};

const ClassInstanceListShowButton = () => {
    const record = useRecordContext ();
    const resource = idFromURL(record["class"]["url"]);
    return ( <ShowButton resource={resource} />)
}

const ClassInstanceListEditButton = () => {
    const record = useRecordContext ();
    const resource = idFromURL(record["class"]["url"]);
    return ( <EditButton resource={resource} />)
}

export const ClassInstanceList = () => {

    const resource = useResourceContext ();


    const {properties, loading} = useProperties (resource, 'classes', 'list');
     const {classifierNames } = useContext(ClassifierContext);
     const {permissions} = usePermissions();

     const csf_name = classifierNames[resource];
     const translate = useTranslate();
    return (
    <List  title={translate ('arolios.list_of', { type: csf_name }) } empty={<CustomEmpty />} actions={<ListActions resource={resource} meta={ {prefix: 'classes', suffix: 'instances', properties: 'all'} }/> } filters={classFilters(properties, loading)} sort={{ field: 'id', order:"DESC"}} queryOptions={{ meta:{ prefix: 'classes', suffix: 'instances', properties: 'list'}}}>
   {loading ? (
        <Loading />
    ) : (
        <Datagrid 

            bulkActionButtons={false}

        >

            {
            classFunctions.unfoldProperties(properties).map ( ( {property, prefix} ) => {
                return InsertListField  ( property, prefix);
                
            } ) }

            <ClassInstanceListShowButton/>     
            { authProvider.canEdit(permissions.role)  && <ClassInstanceListEditButton/> 
            }
        </Datagrid>
    )
    }

    </List>)
}


export const AssociationInstanceList = () => {

    const resource = useResourceContext ();

    const {properties, loading} = useProperties (resource, 'associations', 'list');
     const {classifierNames } = useContext(ClassifierContext);


     const csf_name = classifierNames[resource];
     const translate = useTranslate();
     const {permissions} = usePermissions();

    return (
    <List title={translate ('arolios.list_of', { type: csf_name }) } empty={<CustomEmpty />} actions={<ListActions resource={resource} meta={ {prefix: 'associations', suffix: 'instances', properties: 'all'} }/> } filters={classFilters(properties, loading)} sort={{ field: 'id', order:"DESC"}} queryOptions={{ meta:{ prefix: 'associations', suffix: 'instances', properties: 'list'}}}>
   {loading ? (
        <Loading />
    ) : (
        <Datagrid 

            bulkActionButtons={false}
  
            >

            {
            assocFunctions.unfoldProperties(properties).map ( ( {property, prefix} ) => {
                return InsertListField  ( property, prefix);
            } ) }

            <ShowButton/>
            { authProvider.canEdit(permissions.role)  && <EditButton/> 
            }
        </Datagrid>
    )
    }

    </List>)
}

const ClassInstanceShowLayout = ( {properties} ) => {

return (
    <SimpleShowLayout>
       

            {
            classFunctions.unfoldProperties(properties).map ( ( {property, prefix} ) => {
                    return InsertShowField  ( property, prefix);
            } ) }
            
 
          

    </SimpleShowLayout>
    
)
}

export const ClassInstanceShow = () => {


    const resource = useResourceContext();

    const { properties, loading } = useProperties(resource, 'classes', 'read');

    const {classifierNames } = useContext(ClassifierContext);
    const csf_name = classifierNames[resource];
    const translate = useTranslate();


    return (
 
            <Show title={translate ('arolios.instance_of', { type: csf_name }) } resource='instances' >


                {loading ? (
                    <Loading />
                ) : (
                    <ClassInstanceShowLayout properties={properties} />
                )

                }

            </Show>
        )
}

export const AssocInstanceShow = () => {
    const resource = useResourceContext();

    const { properties, loading } = useProperties(resource, 'associations', 'read');
    const {classifierNames } = useContext(ClassifierContext);
    const csf_name = classifierNames[resource];
    const translate = useTranslate();


    return (
        
            <Show title={translate ('arolios.instance_of', { type: csf_name }) } resource='instances'>
                {loading ? (
                    <Loading />
                ) : (
                    <SimpleShowLayout>

                        {
                            assocFunctions.unfoldProperties(properties).map(({property, prefix}) => {
                                return InsertShowField  ( property, prefix);
                            })}


                    </SimpleShowLayout>
                )}

            </Show>
       )
}

const EditToolbar = ( {type} ) => {
    const translate = useTranslate();
    return (
        <Toolbar>
            <SaveButton  />
            <DeleteWithConfirmButton
                resource='instances'
                confirmTitle = {translate('arolios.delete_confirm_title', { name : type}) }
                redirect='/'
            />

        </Toolbar>
    )
}

export const ClassInstanceEdit = () => {

    const resource = useResourceContext();
    const createPath = useCreatePath();
    const { classifierNames } = useContext(ClassifierContext);
    const csf_name = classifierNames[resource];
    const translate = useTranslate();

    const { properties, loading } = useProperties(resource, 'classes', 'update');
    return (

        <Edit title={translate('arolios.instance_of', { type: csf_name })} resource='instances' queryOptions={{ meta: { context: 'rfu' } }} redirect={createPath({ resource: resource, type: 'list' })}>

            {loading ? (
                <Loading />
            ) : (
                <div>
                    <SimpleForm toolbar={<EditToolbar type={csf_name} />}>

                        {

                            classFunctions.unfoldAllProperties(properties).map(({ property, prefix }) => {
                                return InsertEditField(property, prefix);
                            })}

                    </SimpleForm>
                </div>
            )}

        </Edit>
    )
}


export const AssocInstanceEdit = () => {
    const resource = useResourceContext();
    const createPath = useCreatePath();

    const { properties, loading } = useProperties(resource, 'associations', 'update');

    const { classifierNames } = useContext(ClassifierContext);
    const csf_name = classifierNames[resource];
    const translate = useTranslate();

    return (

        <Edit title={translate('arolios.instance_of', { type: csf_name })} resource='instances' queryOptions={{ meta: { context: 'rfu' } }} redirect={createPath({ resource: resource, type: 'list' })}>

            {loading ? (
                <Loading />
            ) : (
                <SimpleForm toolbar={<EditToolbar />}>

                    {
                        assocFunctions.unfoldNotRefProperties(properties).map(({ property, prefix }) => {
                            return InsertEditField(property, prefix);
                        })}


                </SimpleForm>
            )}

        </Edit>
    )


}

export const ClassInstanceCreate = () => {
    const resource = useResourceContext();

    const { properties, loading } = useProperties(resource, 'classes', 'create');
    const { classifierNames } = useContext(ClassifierContext);
    const csf_name = classifierNames[resource];
    const translate = useTranslate();

    return (

        <Create title={translate('arolios.instance_of', { type: csf_name })} redirect='list' mutationOptions={{ meta: { prefix: 'classes', suffix: 'instances' } }}>
            {loading ? (
                <Loading />
            ) : (
                <div>
                    <SimpleForm>

                        {

                            classFunctions.unfoldAllProperties(properties).map(({ property, prefix }) => {
                                return InsertEditField(property, prefix);
                            })}

                    </SimpleForm>
                </div>
            )}

        </Create>

    )
}

export const AssocInstanceCreate = () => {
    const resource = useResourceContext();

    const { properties, loading } = useProperties(resource, 'associations', 'create');

    const { classifierNames } = useContext(ClassifierContext);
    const csf_name = classifierNames[resource];
    const translate = useTranslate();

    return (

        <Create title={translate('arolios.instance_of', { type: csf_name })} redirect='list' mutationOptions={{ meta: { prefix: 'associations', suffix: 'instances' } }}>
            {loading ? (
                <Loading />
            ) : (
                <div>
                    <SimpleForm>

                        {

                            assocFunctions.unfoldAllProperties(properties).map(({ property, prefix }) => {
                                return InsertEditField(property, prefix);
                            })}

                    </SimpleForm>
                </div>
            )}

        </Create>
    )
}


export const InstanceAssocList = () => {

    const { instId, propId } = useParams ();
    const {pathname} = useLocation();
    const translate = useTranslate();
    const resource = extractResourceFromPathName (pathname);
    const { associations }= useContext(AssociationMemberEndContext);
    const { classifierNames } = useContext(ClassifierContext);
    const assocResource = associations[`${resource}.${propId}`];


    const {properties, loading} = useProperties (assocResource, "associations", 'list');
    const csf_name = classifierNames[assocResource];
    const {permissions} = usePermissions();

    return (
    <List title={translate ('arolios.list_of', { type: csf_name }) } empty={<CustomEmpty classifier={csf_name}/>} disableSyncWithLocation resource='instances' actions={<ListActions resource={assocResource} meta={ {suffix: `${instId}/association_ends/${propId}`, properties: 'all'} }/> } filters={classFilters(properties, loading)} sort={{ field: 'id', order:"DESC"}} queryOptions={{ meta:{ suffix: `${instId}/association_ends/${propId}`, properties: 'list'}}}>
    {loading ? (
        <Loading />
    ) : (

    <Datagrid 

        bulkActionButtons={false}
  
        >

        { 
     
            assocFunctions.unfoldProperties(properties).map ( ( {property,prefix }) => {
                return InsertListField  ( property, prefix);
            
        } ) } 
        <ShowButton resource={assocResource}/>
        { authProvider.canEdit(permissions.role)  && <EditButton resource={assocResource}/> 
            }
           

    </Datagrid>
    ) }
   
</List>)
}