/* eslint-disable prefer-const */
import * as React from "react";
import { FormElementProps, FormElementRegistration } from "@vertigis/workflow";
import IconButton from '@vertigis/web/ui/IconButton';
import { Clear as ClearIcon } from '@mui/icons-material';
import List from '@vertigis/web/ui/List';
import ListItemButton from '@vertigis/web/ui/ListItemButton';
import ListItemIcon from '@vertigis/web/ui/ListItemIcon';
import ListItemText from '@vertigis/web/ui/ListItemText';
import Collapse from '@vertigis/web/ui/Collapse';
import Menu from '@vertigis/web/ui/Menu';
import MenuItem from '@vertigis/web/ui/MenuItem';
import FolderOpenIcon from '@mui/icons-material/FolderOpen';
import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore';
import SearchIcon from '@vertigis/web/ui/icons/Search';
import Box from "@vertigis/web/ui/Box"
import Checkbox from "@vertigis/web/ui/Checkbox"
import { ThemeProvider } from "@emotion/react";
import Typography from '@vertigis/web/ui/Typography';
import { DateRangePicker } from '@mui/x-date-pickers-pro/DateRangePicker';
import dayjs, { Dayjs } from 'dayjs';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
import customParseFormat from 'dayjs/plugin/customParseFormat'
import { DemoContainer } from '@mui/x-date-pickers/internals/demo';
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs';
import 'dayjs/locale/en-au';
import Chip from '@mui/material/Chip';
import Button from '@vertigis/web/ui/Button';
import Radio from '@vertigis/web/ui/Radio';
import RadioGroup from '@vertigis/web/ui/RadioGroup';
import FormControlLabel from '@vertigis/web/ui/FormControlLabel';
import AttributeFilterEasyComp from './modules/AttributeFilterEasyComp';
import Stack from '@vertigis/web/ui/Stack';
import Graphic from "esri/Graphic";
const { Search, SearchIconWrapper, StyledInputBase, StyledBadge, LightTooltip, defaultTheme, CustomNumberInput } = AttributeFilterEasyComp;
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.extend(customParseFormat);
/**
 * The generic type argument provided to `FormElementProps<T>` controls the type
 * of `props.value` and `props.setValue(value)`. If your element doesn't need a
 * `value`, you can omit the type argument.
 *
 * You can also declare additional public properties of your element by adding
 * properties to this interface. The properties will be managed by the Workflow
 * runtime, and passed in as `props`. You can update the properties by using
 * `props.setProperty(key, value)`.
 */
interface AttributeFilterProps extends FormElementProps<string> {
    features?: Graphic[],//Provide when local query rather web query
    fields: {
        name: string;
        alias: string;
        type: string;
        [key: string]: any;
    }[],
    dataOld?: Attribute[],
    //field names & type only at beginning
    openedAttribute?: { data: Attribute, timestamp: number },
    //Data communication purpose
    newSum?: { sum: number, attri: string, timestamp: number },
    //Data communication purpose
    buttonNaming?: string[],
    //List of buttons, if name ends with () will show summary number. Except first button, always has () ,and will be apply if this prop not given. If the button is enabled only when there is selection, put <> at end
    //    allFeatures?: object[]
    //GRAPHICS TO BE HERE
    footerButton?: boolean,//if true, use the button of default form footer
    request?: { requestName: string, timestamp: number },
    //Following attributes are for keep property in Vertigis only when switching tab
    searchQuery: string;
    attributeList?: string[],
    data: Attribute[];
    searchValue: string;
    summary: number;
    sqlRule: string;
    allButtonText: string;
    rule: string;
    layerName: string;
    minValue: number;
    maxValue: number;
    startDate: Dayjs;
    endDate: Dayjs;
    filteredValues: fieldValue[];
}

interface fieldValue {
    value: string,
    count: number
}
interface Attribute {
    name: string,
    alias?: string,
    type?: string,
    uniqValue?: fieldValue[],
    display?: boolean,
    img?: string,
    visible?: boolean,
    selection?: string[],
    timeType?: string,
    hidden?: boolean,
}

/**
 * Provide strictly structured item data to display them and search.
 * @displayName AttributeFilter
 * @description For any layer, dynamicly build your SQL query based on all attributes.
 * @param props Should provide strictly structured Data/openedAttribute
*/




function AttributeFilter(props: AttributeFilterProps): React.ReactElement {

    const defaultNumber = 4
    const { raiseEvent, searchQuery, data, searchValue, summary, minValue, maxValue, filteredValues,
        startDate, endDate, sqlRule, allButtonText, rule, layerName, setProperty } = props;
    const defaultStart = dayjs('2020-01-01')
    const defaultEnd = dayjs('2050-01-01')



    const prevSearchValue = React.useRef<string>('')
    const openAttriName = React.useRef('')
    const tickedBeforeNewQuery = React.useRef<string[] | undefined>(undefined)
    const openAttriValues = React.useRef<Attribute | null>(null);
    const openAttriTime = React.useRef<number | null>(null);
    const requestTime = React.useRef<number | null>(null);
    const numberRecord = React.useRef<Record<string, number>>({});
    const tempSummary = React.useRef(0)
    const prevSum = React.useRef<{ sum: number, attri: string, timestamp: number } | undefined>(undefined)
    const tempPreviousSummary = React.useRef(0)
    const minDate = React.useRef(946700187)
    const maxDate = React.useRef(2209004187)
    //const [filteredValues, setFilteredValues] = React.useState<fieldValue[]>([]); // New state for filtered data
    const [showAll, setShowAll] = React.useState(false);
    const [sortModes, setSortModes] = React.useState<Record<string, string>>({});
    const [sortAnchor, setSortAnchor] = React.useState<null | { el: HTMLElement; name: string }>(null);

    // const [minValue, setMinValue] = React.useState<number>(-Infinity)
    // const [maxValue, setMaxValue] = React.useState<number>(Infinity)
    // const [startDate, setStartDate] = React.useState<Dayjs>(defaultStart)
    // const [endDate, setEndDate] = React.useState<Dayjs>(defaultEnd)
    const [folderOpen, setFolderOpen] = React.useState(false);
    const stickyStyle = {
        position: 'sticky' as const,
        top: 0,
        zIndex: 1000,
        backgroundColor: 'var(--primaryBackground)'
    };


    const attributeList: string[] = React.useMemo(() => {
        return props.attributeList || props.fields.map(a => a.name)
    },
        [props.attributeList, props.fields])


    const RawOriginalData: Attribute[] = React.useMemo(() => {
        if (!props.features) return [];
        return features2Data(props.features, attributeList) || [];
        function features2Data(features: Graphic[], shortListOfAttributes: string[]): Attribute[] {
            // Create an object to store the statistical summary for each attribute
            const attributeSummary: { [key: string]: { [value: string]: number } } = {};

            // Iterate through the features array
            features.forEach((feature) => {
                // Iterate through each attribute in the 'attributes' object of the feature
                // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
                for (const [key, value] of Object.entries(feature.attributes)) {
                    if (shortListOfAttributes.includes(key)) {
                        if (value === null || value === undefined) continue;

                        if (!attributeSummary[key]) {
                            attributeSummary[key] = {};
                        }

                        let valueKey: string;
                        const fieldType = props.fields.find(obj => obj.name == key)?.type;

                        if (fieldType === "date") {
                            const epoch = Number(value);
                            if (!epoch || epoch <= 0) continue;
                            valueKey = dayjs.unix(epoch / 1000).format("DD/MM/YYYY");

                        } else if (fieldType && ['integer', 'number', 'float', 'short', 'long', 'big integer'].includes(fieldType)) {
                            // Numeric: value passed null check above, but still guard NaN
                            const num = Number(value);
                            if (isNaN(num)) continue;
                            valueKey = String(value);

                        } else {
                            // String: reject known meaningless values
                            valueKey = String(value).trim();
                            const meaningless = ['', 'null', 'undefined', '{}', 'N/A', 'n/a', '<Null>', '<null>'];
                            if (meaningless.includes(valueKey)) continue;
                        }

                        if (!attributeSummary[key][valueKey]) {
                            attributeSummary[key][valueKey] = 0;
                        }
                        attributeSummary[key][valueKey]++;
                    }
                }
            });

            // Now we need to structure the data according to the `Attribute` interface
            const result: Attribute[] = [];

            // Convert the summary into the desired format
            for (const [attributeName, valueCountMap] of Object.entries(attributeSummary)) {
                const uniqValues: fieldValue[] = Object.entries(valueCountMap).map(([value, count]) => ({
                    value: value,
                    count: count,
                }));

                // Create the Attribute object
                const attribute: Attribute = {
                    name: attributeName,
                    uniqValue: uniqValues,
                };
                result.push(attribute);
            }
            return result;
        }
    },
        [attributeList, props.features, props.fields])

    // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
    const originalData: Attribute[] = React.useMemo(() => {
        // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
        let result = props.fields.filter(obj => !attributeList || attributeList.length === 0 || attributeList.includes(obj.name)) // Filter only if nameList is valid
            .filter(obj => {
                if (RawOriginalData.length == 0) { return true }
                let matchingRawDataValues = RawOriginalData.find(item => item.name === obj.name)?.uniqValue;
                let meanless = ['{}', ' ', 'null']
                return matchingRawDataValues !== undefined && matchingRawDataValues.length > 0 && !matchingRawDataValues.every((val) => meanless.includes(val.value))
            })
            .map(obj => {
                let newObj: Attribute = { name: obj.name };
                if (obj.alias) {
                    newObj.alias = obj.alias;
                }
                if (obj.type !== "string") {
                    newObj.type = obj.type;
                }
                newObj.visible = true;
                newObj.display = false;
                if (RawOriginalData?.length > 0) {
                    let matchingRawData = RawOriginalData.find(item => item.name === obj.name);
                    if (matchingRawData && matchingRawData.uniqValue) {
                        matchingRawData.uniqValue.sort((a, b) => {
                            if (a.count === b.count) {
                                return a.value.localeCompare(b.value);
                            }
                            return b.count - a.count;
                        });
                        newObj.uniqValue = matchingRawData.uniqValue;
                    }
                }
                return newObj;
            });
        return result;
    }, [RawOriginalData, attributeList, props.fields])


    const DataRef = React.useRef<Attribute[]>(originalData)

    const recordTable: object[] = React.useMemo(() => {
        return props.features?.map(feature => {
            const filteredAttributes = {};
            for (const key of attributeList) {
                if (key in feature.attributes) {
                    const isDate = originalData.find(attri => attri.name === key)?.type == 'date';
                    const rawVal = feature.attributes[key];
                    if (isDate) {
                        const epoch = Number(rawVal);
                        filteredAttributes[key] = (rawVal !== null && rawVal !== undefined && epoch > 0)
                            ? epoch2MelbDate(epoch / 1000)
                            : null;  // keep as null, generateAttributeSummary will skip it
                    } else {
                        filteredAttributes[key] = rawVal;
                    }
                }
            }
            return filteredAttributes;
        }) || [];
    },
        [attributeList, originalData, props.features])


    if (data === undefined) {
        setProperty("data" as any, originalData);
    }

    const findEarliestAndLatestDatesForAttribute = (attribute: Attribute) => {
        if (attribute.type !== "date") {
            // Return null if the attribute is not of type 'date'
            return null;
        }

        let earliestDate: number, latestDate: number
        if (attribute.uniqValue && Number(attribute.uniqValue[0].value) && Number(attribute.uniqValue[0].value) > 0) {
            const values = attribute.uniqValue.map(obj => Number(obj.value));
            earliestDate = Math.min(...values);
            latestDate = Math.max(...values);
        }
        else {
            const dates = attribute.uniqValue?.map((entry) => entry.value) || []; // Extract date strings

            // Convert to Date objects for comparison
            const dateObjects: Date[] = dates.map((date) => {
                const [day, month, year] = date.split('/').map(Number);
                return new Date(year, month - 1, day); // JS months are 0-indexed
            });

            // Find the earliest and latest dates by converting to numeric timestamps
            earliestDate = Math.min(...dateObjects.map((date) => date.getTime()));
            latestDate = Math.max(...dateObjects.map((date) => date.getTime()));
        }


        // Return results as epoch time numbers
        return {
            name: attribute.name,
            earliest: earliestDate,
            latest: latestDate,
        }
    };

    // Handle search input changes
    const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        setProperty("searchQuery" as any, event.target.value); // Update search query
    };

    function layerNameCreator(data: Attribute[], sqlConnector: string) {
        const clauses = data.map((attribute) => {
            if (attribute.selection && attribute.selection.length > 0) {
                const values = attribute.selection.join(',');
                return `${attribute.name}: ${values}`;
            }
            return null;
        }).filter(clause => clause !== null);

        const result = clauses.join(` ${sqlConnector == 'AND' ? "&" : "+"} `);
        return result
    }

    function IsOtherRulesEmpty(specificAttributeName: string) {
        return data && data.every(attribute => {
            if (attribute.name === specificAttributeName) {
                return true;
            }
            return (!attribute.selection || attribute.selection.length == 0)
        });
    }

    const handleSqlRule = (event: React.ChangeEvent<HTMLInputElement>) => {
        //openAttriName.current = ''
        setProperty("sqlRule" as any, (event.target as HTMLInputElement).value);
        const allSelectionsEmpty = data && data.every(attribute =>
            attribute.selection === null ||
            attribute.selection === undefined ||
            (Array.isArray(attribute.selection) && attribute.selection.length === 0)
        );
        if (allSelectionsEmpty) { return }

        if ((event.target as HTMLInputElement).value == 'OR') {
            //converting from AND to OR,   
            //1. summary needs to be recaculated from backend OR engine, and get a larger number
            //2. if any atttribute is displayed, get original uniq values and re-render

            if (summary !== 0) {
                if (recordTable !== undefined && recordTable.length > 0) {
                    const newCount = filterFeatures(recordTable, data, 'OR').length
                    setProperty("summary" as any, newCount)
                }
                else {
                    raiseEvent("clicked", JSON.stringify({ request: 'sumOnly', rule: generateSQLSummary(data, 'OR') }))
                }
            }

            if (openAttriName.current !== '') {
                if (recordTable !== undefined && recordTable.length > 0) {
                    localDisplayLayer(originalData.find(obj => obj.name == openAttriName.current) || data[0], undefined, true)
                }
                else {
                    raiseEvent("clicked", JSON.stringify({ request: 'displayLayer', value: openAttriName.current, rule: '1=1' }));
                }

            }
        }
        else {
            //converting from OR to AND,   
            //1. get new sum 
            //2. if some attri open, reload unqivalues
            if (openAttriName.current == '') {
                if (summary !== 0 || !allSelectionsEmpty) {
                    if (recordTable !== undefined && recordTable.length > 0) {
                        const newCount = filterFeatures(recordTable, data, 'AND').length
                        setProperty("summary" as any, newCount)
                    }
                    else {
                        raiseEvent("clicked", JSON.stringify({ request: 'sumOnly', rule: generateSQLSummary(data, 'AND') }))
                    }
                }
            }
            else {
                if (recordTable !== undefined && recordTable.length > 0) {
                    localDisplayLayer(data?.find(obj => obj.name == openAttriName.current) || data[0], undefined, false, 'AND')
                }
                else {
                    raiseEvent("clicked", JSON.stringify({ request: 'displayLayer', value: openAttriName.current, rule: generateSQLSummary(data, 'AND', openAttriName.current) }))
                }

                if (summary !== 0 || !allSelectionsEmpty) {
                    if (recordTable !== undefined && recordTable.length > 0) {
                        const newCount = filterFeatures(recordTable, data, 'AND').length
                        setProperty("summary" as any, newCount)
                    }
                    else {
                        raiseEvent("clicked", JSON.stringify({ request: 'sumOnly', rule: generateSQLSummary(data, 'AND') }))
                    }
                }
            }
        }

        raiseEvent("clicked", JSON.stringify({ request: 'updateMap', rule: generateSQLSummary(data, (event.target as HTMLInputElement).value) }))
    };

    // const handleSearchValueChange = (attri:Attribute,event: React.ChangeEvent<HTMLInputElement>) => {
    const handleSearchValueChange = (attri: Attribute, event: React.ChangeEvent<HTMLInputElement>) => {
        // setProperty("searchValue" as any,event.target.value); // Update search query
        if (event.target.value) {
            const searchValueResults = filterValue(attri, event.target.value);
            setProperty("filteredValues" as any, searchValueResults)
            setProperty("searchValue" as any, event.target.value)
        }
        else {
            setProperty("searchValue" as any, event.target.value)
            setProperty("filteredValues" as any, [])
        }
    };

    const handleSelectAll = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>, attributeName: string) => {
        event.stopPropagation(); // Prevents the ListItemButton click event
        const selectAll = (allButtonText == 'Select All')
        const newText = selectAll ? 'Deselect All' : 'Select All'
        setProperty("allButtonText" as any, newText)
        const dataCopy = structuredClone(data)
        let updatedDataCopy: Attribute[]
        if (selectAll) {
            if (filteredValues.length > 0) {
                //select all search result
                updatedDataCopy = dataCopy.map((attribute) => {
                    if (attribute.name === attributeName) {
                        const filteredValuesSet = new Set(filteredValues.map(item => item.value));
                        const allValues = attribute.uniqValue
                            ?.filter(item => filteredValuesSet.has(item.value))
                            .map(item => item.value) || [];

                        const totalCount = attribute.uniqValue?.reduce((sum, item) => {
                            return (!attribute.selection?.includes(item.value) && filteredValuesSet.has(item.value)) ? sum + (item.count || 0) : sum;
                        }, 0) || 0;
                        if (sqlRule == 'AND') {

                            const result = (attribute.selection && attribute.selection.length > 0) ?
                                (summary + totalCount) : IsOtherRulesEmpty(attributeName) ? totalCount : Math.min(summary, totalCount)
                            tempSummary.current = result
                            setProperty("summary" as any, result);
                            numberRecord.current[attributeName] = tempSummary.current
                        }
                        else {
                            tempSummary.current = Math.max(summary, totalCount)
                            const newSummary = Math.max(summary, totalCount)
                            setProperty("summary" as any, newSummary)
                            numberRecord.current[attributeName] = tempSummary.current
                        }
                        return {
                            ...attribute,
                            selection: Array.from(new Set([...(allValues ?? []), ...(attribute.selection ?? [])])),
                        };
                    }
                    return attribute;
                })

                setProperty("data" as any, updatedDataCopy);
            }
            else {
                //No search or range -  real select all
                updatedDataCopy = dataCopy.map((attribute) => {
                    if (attribute.name === attributeName) {
                        const allValues = attribute.uniqValue?.map((item) => item.value) || [];

                        const totalCount = attribute.uniqValue?.reduce((sum, obj) => sum + obj.count, 0) || 0;

                        if (IsOtherRulesEmpty(attributeName) || sqlRule == 'AND') {
                            tempSummary.current = totalCount
                            setProperty("summary" as any, totalCount);
                        }
                        else if (!IsOtherRulesEmpty(attributeName)) {
                            if (recordTable !== undefined && recordTable.length > 0) {
                                const newCount = filterFeatures(recordTable, data, sqlRule).length
                                setProperty("summary" as any, newCount)
                            }
                            else {
                                raiseEvent("clicked", JSON.stringify({ request: 'sumOnly', rule: generateSQLSummary(data, sqlRule) }))
                            }
                        }

                        return {
                            ...attribute,
                            selection: allValues,
                        };
                    }
                    return attribute;
                })

                setProperty("data" as any, updatedDataCopy);
            }
            raiseEvent("clicked", JSON.stringify({ request: 'updateMap', rule: generateSQLSummary(updatedDataCopy, sqlRule) }))
        }
        else {
            if (filteredValues.length > 0) {

                const filteredValuesSet = new Set(filteredValues.map(item => item.value));

                updatedDataCopy = dataCopy.map((attribute) => {
                    if (attribute.name === attributeName) {
                        const totalCount = attribute.uniqValue?.reduce((sum, item) => {
                            return (filteredValuesSet.has(item.value) && attribute.selection?.includes(item.value)) ? sum + (item.count || 0) : sum;
                        }, 0) || 0;

                        if (!IsOtherRulesEmpty(attributeName) && sqlRule == 'AND' && totalCount == summary) {
                            setProperty("summary" as any, tempPreviousSummary.current)
                            numberRecord.current[attributeName] = tempSummary.current
                        }
                        else {
                            tempSummary.current = summary - totalCount
                            const newSummary = summary - totalCount
                            setProperty("summary" as any, newSummary)
                            numberRecord.current[attributeName] = tempSummary.current
                        }
                        return {
                            ...attribute,
                            selection: attribute.selection?.filter(item => !filteredValuesSet.has(item)) || [],
                        };
                    }
                    return attribute;
                })

                setProperty("data" as any, updatedDataCopy);
                if (generateSQLSummary(data, sqlRule, attributeName) == '1=1') {
                    tempSummary.current = 0;
                    tempPreviousSummary.current = 0;
                }
                else {
                    if (recordTable !== undefined && recordTable.length > 0) {
                        const newCount = filterFeatures(recordTable, updatedDataCopy, sqlRule).length
                        setProperty("summary" as any, newCount)
                    }
                    else {
                        raiseEvent("clicked", JSON.stringify({ request: 'sumOnly', rule: generateSQLSummary(updatedDataCopy, sqlRule) }))
                    }
                }
                raiseEvent("clicked", JSON.stringify({ request: 'updateMap', rule: generateSQLSummary(updatedDataCopy, sqlRule, attributeName) }))
            }
            else {
                deleteSelection(data?.find(attri => attri.name == attributeName) || data[0])
                raiseEvent("clicked", JSON.stringify({ request: 'updateMap', rule: generateSQLSummary(data, sqlRule, attributeName) }))
            }
        }
    };

    const handleBtnClick = (requestName: string) => {
        raiseEvent("clicked", JSON.stringify({ request: requestName, rule: generateSQLSummary(data, sqlRule), layerName: layerNameCreator(data, sqlRule) }))
    }

    const handleClearRulesClick = () => {
        const updatedData = data?.map((attribute) => {
            if (attribute.selection && attribute.selection.length > 0) {
                attribute.selection = undefined
            }
            return attribute
        }
        )
        setProperty("data" as any, updatedData);
        setProperty("summary" as any, 0)
        raiseEvent("clicked", JSON.stringify({ request: 'Clear' }))
        if (recordTable !== undefined && recordTable.length > 0) {
            const updateCopy = originalData.map((attribute) => {
                if (attribute.name == openAttriName.current) {
                    attribute.display = true
                }
                else {
                    attribute.display = false
                }
                return attribute
            })
            setProperty("data" as any, updateCopy)
        }
    }

    // Handle click on items to raise an event
    const handleItemClick = (currentAttribute: Attribute, fieldValue: fieldValue) => {
        const dataCopy = structuredClone(data)
        const updatedDataCopy = dataCopy.map((attribute) => {
            if (currentAttribute.name === attribute.name) {
                const isSelected = attribute.selection?.includes(fieldValue.value);
                return {
                    ...attribute,
                    selection: isSelected
                        ? (attribute.selection || []).filter((value) => value !== fieldValue.value)
                        : [...(attribute.selection || []), fieldValue.value]
                };
            }
            return attribute;
        });
        setProperty("data" as any, updatedDataCopy);

        if (sqlRule == 'AND') {
            if (!IsOtherRulesEmpty(currentAttribute.name)) {
                if (!currentAttribute.selection || currentAttribute.selection.length == 0) {
                    tempSummary.current = 0
                }
                if (currentAttribute.selection?.includes(fieldValue.value)) {
                    tempSummary.current -= fieldValue.count

                    setProperty("summary" as any, tempSummary.current == 0 ? tempPreviousSummary.current : tempSummary.current);
                    numberRecord.current[currentAttribute.name] = tempSummary.current
                }
                else {
                    tempSummary.current += fieldValue.count
                    setProperty("summary" as any, tempSummary.current);
                    numberRecord.current[currentAttribute.name] = tempSummary.current
                }
            }
            else {
                if (currentAttribute.selection?.includes(fieldValue.value)) {
                    tempSummary.current = summary - fieldValue.count
                    setProperty("summary" as any, summary - fieldValue.count);
                    numberRecord.current[currentAttribute.name] = tempSummary.current
                }
                else {
                    tempSummary.current = summary + fieldValue.count
                    setProperty("summary" as any, summary + fieldValue.count);
                    numberRecord.current[currentAttribute.name] = tempSummary.current
                }
            }
        }
        else {
            if (recordTable !== undefined && recordTable.length > 0) {
                const newCount = filterFeatures(recordTable, updatedDataCopy, 'OR').length
                setProperty("summary" as any, newCount)
            }
            else {
                if (!currentAttribute.selection || currentAttribute.selection.length == 0) {
                    tempSummary.current = 0
                    const values: number[] = Object.values(numberRecord.current);
                    const maxNumber = Math.max(...values);
                    tempPreviousSummary.current = maxNumber


                }
                if (currentAttribute.selection?.includes(fieldValue.value)) {
                    tempSummary.current -= fieldValue.count
                    setProperty("summary" as any,
                        tempSummary.current > tempPreviousSummary.current ? tempSummary.current : tempPreviousSummary.current);
                    numberRecord.current[currentAttribute.name] = tempSummary.current
                }
                else {
                    tempSummary.current += fieldValue.count
                    if (tempSummary.current > summary) {
                        setProperty("summary" as any, tempSummary.current);
                    }
                    numberRecord.current[currentAttribute.name] = tempSummary.current

                }


            }

        }

        raiseEvent("clicked", JSON.stringify({ request: 'updateMap', rule: generateSQLSummary(updatedDataCopy, sqlRule) }))

    };



    const generateSQLSummary = (data: Attribute[], logicalOperator: string, exception?: string | string[]) => {


        const isException = (name: string) => {
            if (Array.isArray(exception)) {
                return exception.includes(name);
            }
            return name === exception;
        };

        function generateDateSQL(melbourneDates: string[], epochField: string) {
            const convertToUTC = (melbourneTime: string) => {
                return dayjs.tz(melbourneTime, 'Australia/Melbourne').utc().format('YYYY-MM-DD HH:mm:ss');
            };
            const sqlConditions = melbourneDates.map(dateStr => {
                const startTimestamp = convertToUTC(`${dateStr} 00:00:00`);
                const endTimestamp = convertToUTC(`${dateStr} 23:59:59`);
                return `(${epochField} BETWEEN TIMESTAMP '${startTimestamp}' AND TIMESTAMP '${endTimestamp}')`;
            });
            const combinedConditions = "(" + sqlConditions.join(' OR ') + ")";
            return combinedConditions
        }


        const clauses = data.map((attribute) => {
            if (!isException(attribute.name) && attribute.selection && attribute.selection.length > 0) {
                let values: string[] | string;
                if (attribute.type === 'date') {
                    if (attribute.selection.length < 11) {
                        values = attribute.selection.map((value) => { return dayjs(value, 'DD/MM/YYYY').format('YYYY-MM-DD') })
                        return generateDateSQL(values, attribute.name)
                    }
                    else {
                        const convertToUTC = (melbourneTime: string) => {
                            return dayjs.tz(melbourneTime, 'Australia/Melbourne').utc().format('YYYY-MM-DD HH:mm:ss');
                        };
                        const timeArray = attribute.selection.map((value) => { return dayjs(value, 'DD/MM/YYYY') })
                        const minDate = timeArray.reduce((min, current) => (current.isBefore(min) ? current : min)).format('YYYY-MM-DD');
                        const maxDate = timeArray.reduce((max, current) => (current.isAfter(max) ? current : max)).format('YYYY-MM-DD');

                        const startTimestamp = convertToUTC(`${minDate} 00:00:00`);
                        const endTimestamp = convertToUTC(`${maxDate} 23:59:59`);
                        return `${attribute.name} >= '${startTimestamp}' AND ${attribute.name} <= '${endTimestamp}'`;
                    }

                } else {
                    const likeConditions: string[] = [];
                    const inValues: string[] = [];

                    attribute.selection.forEach(value => {
                        if (value.length > 72) {
                            const first72Chars = value.slice(0, 72); // Get the first 72 characters
                            likeConditions.push(`${attribute.name} LIKE '${first72Chars}%'`);
                        } else {
                            inValues.push(`'${value}'`); // Use value as-is for short strings
                        }
                    });

                    const inClause = inValues.length > 0 ? `${attribute.name} IN (${inValues.join(', ')})` : '';
                    const likeClause = likeConditions.join(' OR ');

                    if (inClause && likeClause) {
                        return `(${inClause} OR ${likeClause})`;
                    } else if (inClause) {
                        return `(${inClause})`;
                    } else if (likeClause) {
                        return `(${likeClause})`;
                    } else {
                        return null; // Return an empty string if there are no conditions
                    }
                }
            }
            return null;
        }).filter(clause => clause !== null);

        const result = clauses.length > 1 ? clauses.join(` ${logicalOperator} `) : clauses.length > 0 ? clauses[0].replace(/^\(|\)$/g, '') : ''
        return result.length > 2 ? result : '1=1'
    };

    interface Condition {
        type: 'IN' | 'LIKE' | 'BETWEEN'; // Condition type: IN, LIKE, or BETWEEN
        field: string;                   // The field (column) to apply the condition on
        values?: any[];                  // Values for the IN condition
        pattern?: string;                // Pattern for the LIKE condition
        start?: number | string | Date;  // Start value for BETWEEN condition (number, string, or Date)
        end?: number | string | Date;    // End value for BETWEEN condition (number, string, or Date)
    }

    class SimpleSQLWhereEngine {
        private data: object[]; // Data is an array of objects

        constructor(data: Record<string, any>[]) {
            this.data = data;
        }

        // Utility function to convert date strings to epoch time (milliseconds)
        private parseDate(dateString: string): { start: number, end: number } {
            // Format: dd/mm/yyyy (like '25/01/2024')
            const [day, month, year] = dateString.split('/').map(Number);

            // Create a date for the start of the day (00:00:00)
            const startOfDay = new Date(year, month - 1, day, 0, 0, 0, 0).getTime();

            // Create a date for the end of the day (23:59:59)
            const endOfDay = new Date(year, month - 1, day, 23, 59, 59, 999).getTime();

            return { start: startOfDay, end: endOfDay };
        }

        // WHERE IN (specific values) condition
        private in(field: string, values: any[], isDate: boolean = false): Record<string, any>[] {
            return this.data.filter(row => {
                const fieldValue = row[field];

                if (isDate && typeof fieldValue === 'number') {
                    // If isDate is true and the field is a number (epoch milliseconds), compare as dates
                    return values.some(value => {
                        if (typeof value === 'string') {
                            const { start, end } = this.parseDate(value);
                            return fieldValue >= start && fieldValue <= end; // Check if the field value is within the day range
                        }
                        return false;
                    });
                }

                // Default behavior for non-date fields or when isDate is false
                return values.includes(fieldValue);
            });
        }

        // WHERE LIKE (for string matching)
        private like(field: string, pattern: string): Record<string, any>[] {
            const regex = new RegExp(pattern.replace('%', '.*')); // Convert 'sth%' to regex pattern
            // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
            return this.data.filter(row => regex.test(row[field]));
        }

        // WHERE BETWEEN (for timestamps or numerical ranges)
        private between(field: string, start: number | string | Date, end: number | string | Date): Record<string, any>[] {
            return this.data.filter(row => {
                const value = row[field];
                return value >= start && value <= end;
            });
        }

        // General WHERE function that can handle all conditions
        public where(condition: Condition, isDate: boolean = false): Record<string, any>[] {
            if (condition.type === 'IN' && condition.values) {
                return this.in(condition.field, condition.values, isDate);
            } else if (condition.type === 'LIKE' && condition.pattern) {
                return this.like(condition.field, condition.pattern);
            } else if (condition.type === 'BETWEEN' && condition.start !== undefined && condition.end !== undefined) {
                return this.between(condition.field, condition.start, condition.end);
            }
            return this.data; // Default return if no condition is met
        }
    }

    function filterFeatures(features: Record<string, any>[], attributes: Attribute[], logic: string, exception?: string | string[]): Record<string, any>[] {
        // eslint-disable-next-line prefer-const
        let engine = new SimpleSQLWhereEngine(features);
        let filteredFeatures = features;  // Start with all features
        const hasValidSelections = attributes.some(attribute => attribute.name !== exception && attribute.selection && attribute.selection.length > 0);

        if (!hasValidSelections) {
            return features; // Display all features if no valid selections
        }

        if (logic === 'AND') {
            attributes.forEach(attribute => {

                if ((attribute.name !== exception && !exception?.includes(attribute.name)) && attribute.selection && attribute.selection.length > 0) {
                    const condition: Condition = {
                        type: 'IN',
                        field: attribute.name,
                        values: attribute.selection
                    };
                    filteredFeatures = engine.where(condition, attribute.type === 'date'); // Filter progressively on each condition
                    engine = new SimpleSQLWhereEngine(filteredFeatures);
                }
            });
        } else if (logic === 'OR') {
            let tempFeatures: Record<string, any>[] = [];
            if (attributes.some(obj => obj.selection && obj.selection.length > 0)) {
                attributes.forEach(attribute => {
                    if (attribute.name !== exception && !exception?.includes(attribute.name) && attribute.selection && attribute.selection.length > 0) {
                        const condition: Condition = {
                            type: 'IN',
                            field: attribute.name,
                            values: attribute.selection
                        };
                        tempFeatures = tempFeatures.concat(engine.where(condition)); // Concatenate results
                    }
                });
                // Remove duplicates
                filteredFeatures = tempFeatures.filter((item, index) => tempFeatures.indexOf(item) === index);
            }
            else {
                filteredFeatures = []
            }

        }

        return filteredFeatures;
    }

    function generateAttributeSummary(features: object[], attributeName: string, type: string): fieldValue[] {
        // Create an object to store the statistical summary for the attribute
        const attributeSummary: { [key: string]: number } = {};

        // Iterate through the features array
        features.forEach((feature) => {
            const value = feature[attributeName];
            if (value === null || value === undefined || value === '') return;
            if (value) {

                // Count the occurrence of each value for the attribute
                if (!attributeSummary[value]) {
                    attributeSummary[value] = 0;
                }
                attributeSummary[value]++;
            }
        });


        // Convert the summary object to the desired array format
        const rawResult: fieldValue[] = Object.entries(attributeSummary).map(([value, count]) => ({
            value: type == 'date' && Number(value) > 0 ? epoch2MelbDate(Number(value) / 1000) : value,
            count: count
        }));
        if (type !== 'date' || !Number(rawResult[0].value)) {
            return rawResult
        }
        else {
            const result = groupByMelbourneDate(rawResult)
            return result
        }
    }

    function epoch2MelbDate(epoch: any) {
        return dayjs.unix(Number(epoch)).tz('Australia/Melbourne').format('DD/MM/YYYY');
    }


    function formatUniqValues(attri: Attribute) {
        let newAttriData: fieldValue[]
        if (attri.type == 'date' && attri.uniqValue && Number(attri.uniqValue[0].value) > 0) {
            const values = attri.uniqValue.map(obj => Number(obj.value));
            minDate.current = Math.min(...values) / 1000;
            maxDate.current = Math.max(...values) / 1000;
            setProperty("startDate" as any, dayjs.unix(Math.min(...values) / 1000))
            setProperty("endDate" as any, dayjs.unix(Math.max(...values) / 1000))
            newAttriData = groupByMelbourneDate(attri.uniqValue)
        }
        else {
            newAttriData = attri.uniqValue || []
        }
        return newAttriData
    }

    function localDisplayLayer(attri: Attribute, exception?: string, noLimit?: boolean, Rule?: string) {
        if (recordTable !== undefined && recordTable.length > 0) {
            let result: Attribute[]
            if (noLimit) {
                const dataCopy = structuredClone(data)
                result = dataCopy.map(item =>
                    item.name === attri.name ?
                        {
                            ...item,
                            display: true,
                            uniqValue: item.selection && item.selection.length > 0 ?
                                sortItemsBySelection(formatUniqValues(originalData.find(attribute => attribute.name == item.name) || data[0]), item.selection)
                                : formatUniqValues(originalData.find(attribute => attribute.name == item.name) || data[0])
                        }
                        : { ...item, display: false }
                );
                setProperty("data" as any, result);
            }
            else {
                let queryFeatures: object[] = []
                if (exception) {
                    queryFeatures = filterFeatures(recordTable, data, Rule || sqlRule, [exception, attri.name])
                } else {
                    queryFeatures = filterFeatures(recordTable, data, Rule || sqlRule, attri.name)
                }
                const queryUniqValue = generateAttributeSummary(queryFeatures, attri.name, attri.type || 'string')
                queryUniqValue.sort((a, b) => {
                    if (a.count === b.count) {
                        return a.value.localeCompare(b.value);
                    }
                    return b.count - a.count;
                });
                const dataCopy = structuredClone(data)
                result = dataCopy.map(item =>
                    item.name === attri.name ?
                        {
                            ...item, display: true,
                            uniqValue: item.selection && item.selection.length > 0 ?
                                sortItemsBySelection(queryUniqValue, item.selection) : queryUniqValue
                        } : { ...item, display: false }
                );
                setProperty("data" as any, result);
            }

            if (attri.type == 'date') {
                minDate.current = Math.floor((findEarliestAndLatestDatesForAttribute(attri)?.earliest || 946700000) / 1000)
                maxDate.current = Math.floor((findEarliestAndLatestDatesForAttribute(attri)?.latest || 2209004000) / 1000)
                setProperty("startDate" as any, dayjs.unix(minDate.current))
                setProperty("endDate" as any, dayjs.unix(maxDate.current))
            }
            openAttriName.current = attri.name

            // const oldSelection = result.find(obj => obj.name === openAttriName.current)?.selection || []
            // const updatedAttri = result.find(obj => obj.name === attri.name) || result[0]
            // if (openAttriName.current == attri.name && oldSelection.length > 0 && searchValue.length > 0) {
            //     const searchValueResults = filterValue(updatedAttri, searchValue);
            //     setProperty("filteredValues" as any, searchValueResults)
            // }


            // openAttriValues.current = updatedAttri

        }
    }

    // // Handle category toggle open/close manually
    const handleAttributeClick = (attri: Attribute) => {
        setProperty("filteredValues" as any, [])
        setShowAll(false)
        setProperty("searchValue" as any, '');
        setProperty("minValue" as any, -Infinity)
        setProperty("maxValue" as any, Infinity)
        tempPreviousSummary.current = summary
        if (!attri.display) {
            if (recordTable !== undefined && recordTable.length > 0) {
                localDisplayLayer(attri, undefined, sqlRule === 'OR')
            }
            else {
                raiseEvent("clicked", JSON.stringify({ request: 'displayLayer', value: attri.name, rule: sqlRule == 'AND' ? generateSQLSummary(data, sqlRule, attri.name) : '1=1' }))

            }

        }
        else if (attri.display) {

            const dataCopy = structuredClone(data)
            setSortModes(prev => {
                const copy = { ...prev };
                delete copy[attri.name];
                return copy;
            });
            let result: Attribute[]
            if (recordTable !== undefined && recordTable.length > 0) {
                result = dataCopy.map(item =>
                    item.name === attri.name ? { ...item, display: false } : item
                );
            }
            else {
                result = dataCopy.map(item =>
                    item.name === attri.name ? { ...item, display: false, uniqValue: undefined } : item
                );
            }
            setProperty("data" as any, result);
            openAttriName.current = ''
            props.openedAttribute = undefined

        }
    };
    const handleShowAllClick = () => {
        setShowAll(!showAll);
    };
    function deleteSelection(attri: Attribute) {

        function removeAnotherRule() {
            const updatedData = data?.map((attribute) => {
                if (attribute.name === attri.name) {
                    return {
                        ...attribute,
                        selection: undefined,
                        uniqValue: recordTable && recordTable.length > 0 ? attri.uniqValue : undefined
                    };
                }
                if (attribute.name === openAttriName.current && tickedBeforeNewQuery.current !== undefined) {
                    return {
                        ...attribute,
                        selection: tickedBeforeNewQuery.current,
                    };
                }
                return attribute;
            })
            setProperty("data" as any, updatedData);
        }


        if (IsOtherRulesEmpty(attri.name)) {
            setProperty("summary" as any, 0);
            tempSummary.current = 0
            numberRecord.current[attri.name] = 0

            if (openAttriName.current !== '') {
                if (recordTable !== undefined && recordTable.length > 0) {
                    localDisplayLayer(data?.find(attribute => attribute.name == openAttriName.current) || data[0], undefined, true)
                }
                else {
                    raiseEvent("clicked", JSON.stringify({ request: 'displayLayer', value: openAttriName.current, rule: '1=1' }))
                }
            }
            const updatedData = data?.map((attribute) => {
                if (attribute.name === attri.name) {
                    return {
                        ...attribute,
                        selection: [],
                    };
                }
                return attribute;
            })
            setProperty("data" as any, updatedData);

        }
        else {
            if (openAttriName.current == attri.name) {
                if (data?.find(attribute => attribute.name == attri.name)) {
                    const updatedData = data?.map((obj) => {
                        if (obj.name === attri.name) {
                            return { ...obj, selection: undefined };
                        }
                        return obj
                    })
                    setProperty("data" as any, updatedData);

                }
                if (recordTable !== undefined && recordTable.length > 0) {
                    const newCount = filterFeatures(recordTable, data, sqlRule, attri.name).length
                    setProperty("summary" as any, newCount)
                }
                else {
                    raiseEvent("clicked", JSON.stringify({ request: 'sumOnly', rule: generateSQLSummary(data, sqlRule, attri.name) }))
                }
            }
            else {
                const targetObject = data?.find(obj => obj.name == openAttriName.current);


                if (targetObject && targetObject.uniqValue) {
                    tickedBeforeNewQuery.current = targetObject.selection
                    if (recordTable !== undefined && recordTable.length > 0) {
                        localDisplayLayer(data?.find(obj => obj.name == openAttriName.current) || data[0], attri.name)
                        const newCount = filterFeatures(recordTable, data, sqlRule, attri.name).length
                        setProperty("summary" as any, newCount)
                    }
                    else {
                        raiseEvent("clicked", JSON.stringify({ request: 'displayLayer', value: openAttriName.current, rule: generateSQLSummary(data, sqlRule, [openAttriName.current, attri.name]) }))
                    }
                    //origianlly newQueery
                    removeAnotherRule()
                }
                else {
                    if (recordTable !== undefined && recordTable.length > 0) {
                        const newCount = filterFeatures(recordTable, data, sqlRule, attri.name).length
                        setProperty("summary" as any, newCount)
                        const updatedData = data?.map((obj) => {
                            if (obj.name === attri.name) {
                                return { ...obj, selection: undefined };
                            }
                            return obj
                        })
                        setProperty("data" as any, updatedData);
                    }
                    else {
                        raiseEvent("clicked", JSON.stringify({ request: 'sumOnly', value: attri.name, rule: generateSQLSummary(data, sqlRule, attri.name) }))
                    }
                }
            }

        }

    }

    const handleDeleteSelection = (attri: Attribute) => {
        deleteSelection(attri)
        raiseEvent("clicked", JSON.stringify({ request: 'updateMap', rule: generateSQLSummary(data, sqlRule, attri.name) }))
    };

    function detectNumberAttribute(attribute) {
        const name = attribute.name.toLowerCase();;
        if (name.includes("id") || name.includes("name") || name.includes("mobile") || name.includes("phone")) {
            return false;
        }
        if (attribute.uniqValue) {
            const phoneNumberPattern = /^04\d{8}$/;
            let allPhoneNumbers = true;

            for (let i = 0; i < 5; i++) {

                if (i >= attribute.uniqValue.length) break; // In case there are less than 5 elements
                const value: string = attribute.uniqValue[i].value;
                if (isNaN(Number(value))) {
                    return false;

                }
                if (!phoneNumberPattern.test(value) && allPhoneNumbers == true) {
                    allPhoneNumbers = false
                }
            }
            if (allPhoneNumbers) { return false }

            return true;
        }
        return false;

    }


    function findMinMax(attribute) {
        let min = Infinity;
        let max = -Infinity;

        if (attribute.uniqValue) {
            attribute.uniqValue.forEach(item => {
                const num = Number(item.value);
                if (!isNaN(num)) {
                    if (num < min) min = num;
                    if (num > max) max = num;
                }
            });

            return { min: Math.floor(min * 100) / 100, max: Math.floor(max * 100) / 100 };
        }
        return { min: -Infinity, max: Infinity };
    }

    const toggleFolder = () => setFolderOpen(!folderOpen);


    const renderAttribute = (attribute: Attribute): React.ReactNode => {
        // --- sort mode for this attribute ---
        const defaultSort = attribute.type === 'date' ? 'late-early' : 'count';
        const sortMode = sortModes[attribute.name] ?? defaultSort;

        const sortOptions: Record<string, string[]> = {
            date: ['late-early', 'early-late', 'count'],
            number: ['count', 'big-small', 'small-big'],
            string: ['count', 'a-z', 'z-a'],
        };
        const fieldKind = attribute.type === 'date' ? 'date'
            : (attribute.type && ['integer', 'number', 'float', 'short', 'long', 'big integer'].includes(attribute.type)) || detectNumberAttribute(attribute) ? 'number'
                : 'string';
        const modes = sortOptions[fieldKind];
        const modeLabels: Record<string, string> = {
            'count': 'By count',
            'a-z': 'A → Z',
            'z-a': 'Z → A',
            'late-early': 'Latest first',
            'early-late': 'Earliest first',
            'big-small': 'Biggest first',
            'small-big': 'Smallest first',
        };

        function applySortMode(values: fieldValue[]): fieldValue[] {
            const copy = [...values];
            switch (sortMode) {
                case 'count': return copy.sort((a, b) => b.count - a.count || a.value.localeCompare(b.value));
                case 'a-z': return copy.sort((a, b) => a.value.localeCompare(b.value));
                case 'z-a': return copy.sort((a, b) => b.value.localeCompare(a.value));
                case 'late-early': return copy.sort((a, b) => {
                    const da = dayjs(a.value, 'DD/MM/YYYY'), db = dayjs(b.value, 'DD/MM/YYYY');
                    return db.valueOf() - da.valueOf();
                });
                case 'early-late': return copy.sort((a, b) => {
                    const da = dayjs(a.value, 'DD/MM/YYYY'), db = dayjs(b.value, 'DD/MM/YYYY');
                    return da.valueOf() - db.valueOf();
                });
                case 'big-small': return copy.sort((a, b) => Number(b.value) - Number(a.value));
                case 'small-big': return copy.sort((a, b) => Number(a.value) - Number(b.value));
                default: return copy;
            }
        }

        let ISnumberAttribute = false
        let attributeMin = -Infinity
        let attributeMax = Infinity

        if (attribute.uniqValue && attribute.uniqValue.length > 0) {
            attributeMin = findMinMax(attribute).min
            attributeMax = findMinMax(attribute).max
            const numberTypes = ['integer', 'number', 'float', 'short', 'long', 'big integer'];
            ISnumberAttribute = numberTypes.includes(attribute?.type || '--') || detectNumberAttribute(attribute)
        }





        let displayedValues: fieldValue[] = []
        let displayedFilterValues: fieldValue[] = []
        let limitInUse
        if (recordTable !== undefined && recordTable.length > 0) {
            limitInUse = (attribute.type == 'date' && (dayjs.unix(minDate.current).format('DD/MM/YYYY') != startDate.format('DD/MM/YYYY') || (dayjs.unix(maxDate.current).format('DD/MM/YYYY') != endDate.format('DD/MM/YYYY')))) || searchValue.length > 0 || (minValue !== -Infinity || maxValue !== Infinity)
        }
        else {
            limitInUse = (attribute.type == 'date' && (dayjs.unix(minDate.current).format('DD/MM/YYYY') != startDate.format('DD/MM/YYYY') || (dayjs.unix(maxDate.current).format('DD/MM/YYYY') != endDate.format('DD/MM/YYYY')))) || searchValue.length > 0 || (minValue !== -Infinity || maxValue !== Infinity)
        }
        if (attribute.uniqValue && attribute.uniqValue.length > 0) {
            const sorted = applySortMode(attribute.uniqValue);
            displayedValues = (sorted.length < defaultNumber + 2) || showAll
                ? sorted
                : sorted.slice(0, defaultNumber);
        }
        if (limitInUse && filteredValues.length > 0) {
            const sorted = applySortMode(filteredValues);
            displayedFilterValues = ((sorted.length < defaultNumber + 2) || showAll)
                ? sorted
                : sorted.slice(0, defaultNumber)
        }


        const handleDateRangeChange = (newStart: Dayjs, newEnd: Dayjs) => {
            setProperty("startDate" as any, newStart || defaultStart);
            setProperty("endDate" as any, newEnd || defaultEnd)
            const filterrangeResult = filterDateRange(attribute, newStart, newEnd)
            setProperty("filteredValues" as any, filterrangeResult)
        };

        const maxStringLength = 28
        function itemText(item: fieldValue, type?: string) {
            let rawResult: string = ''
            if (type == 'number') {
                rawResult = ((Math.floor(Number(item.value) * 100) / 100)).toString()
            }
            else { rawResult = item.value }

            return rawResult.length > maxStringLength ? rawResult.substring(0, maxStringLength - 3) + '...' : rawResult;
        }

        const chipLabel = attribute.selection && attribute.selection.length > 1
            ? `${attribute.selection.length} selected`
            : attribute.selection && attribute.selection.length > 0 && attribute.type === 'date'
                ? attribute.selection[0]
                : attribute.selection && attribute.selection.length > 0 && attribute.selection[0].length > 8
                    ? `${attribute.selection[0].substring(0, 6)}..`
                    : attribute.selection ? attribute.selection[0] : '';

        const chipElement = (
            <Chip
                label={chipLabel}
                sx={{
                    color: 'var(--accentIconBackground)',
                    backgroundColor: 'var(--alertAmberBackground)',
                    '& .MuiChip-deleteIcon': { color: 'var(--accentIconBackground)' }
                }}
                onDelete={() => handleDeleteSelection(attribute)}
            />
        );

        const exceptions = ['OBJECTID', 'Shape__Area', 'Shape__Length'];
        if (!exceptions.includes(attribute.name)) {

            return (
                <React.Fragment key={attribute.name}>
                    <ListItemButton onClick={() => handleAttributeClick(attribute)}
                        sx={{
                            // Conditionally change background color based on attribute.display
                            backgroundColor: attribute.display ? 'rgba(197, 197, 197, 0.65)' : 'transparent',
                        }}
                    >
                        { // render icon image
                            attribute.name ? (
                                <Box
                                    component="img"
                                    sx={{
                                        height: 18,
                                        width: 18,
                                    }}
                                    alt="The category icon"
                                    src={attribute.img ? attribute.img :
                                        attribute.type == 'date' ? 'https://cdn-icons-png.flaticon.com/512/6706/6706193.png' :
                                            ISnumberAttribute ? 'https://cdn-icons-png.flaticon.com/512/5141/5141667.png' :
                                                "https://cdn-icons-png.flaticon.com/24/2303/2303496.png"}// Use the item's img property or based on type
                                />
                            ) :
                                //on purpose keep not in use code in case
                                (<ListItemIcon sx={{ minWidth: '30px' }}>
                                    <FolderOpenIcon sx={{ color: 'var(--accentIconBorder)' }} />
                                </ListItemIcon>)
                        }
                        <ListItemText primary={attribute.alias || attribute.name} sx={{ pl: 3 }} primaryTypographyProps={{ style: { fontWeight: 800 } }} />

                        {//selection chip
                            attribute.selection && attribute.selection.length > 0 ?
                                attribute.selection.length > 1 ? <LightTooltip
                                    title={attribute.selection.join(',')}
                                    sx={{ fontSize: '2rem' }} arrow>
                                    {chipElement}
                                </LightTooltip>
                                    : chipElement : <></>}
                        {attribute.display ? <ExpandLess /> : <ExpandMore />}

                    </ListItemButton>
                    {attribute.uniqValue ?
                        (<Collapse in={attribute.display} timeout="auto" unmountOnExit>
                            {attribute.type && attribute.type == 'date' && (attribute.uniqValue.length > 6) ?
                                <Box display="flex" alignItems="center" >
                                    <LocalizationProvider dateAdapter={AdapterDayjs} >
                                        <DemoContainer components={['DatePicker', 'DatePicker']} >
                                            <LocalizationProvider dateAdapter={AdapterDayjs} adapterLocale="en-au">
                                                <DateRangePicker defaultValue={[dayjs.unix(minDate.current), dayjs.unix(maxDate.current)]} maxDate={dayjs.unix(maxDate.current)} minDate={dayjs.unix(minDate.current)}
                                                    value={[startDate, endDate]}
                                                    onChange={([newstart, newend]) => handleDateRangeChange(newstart || startDate, newend || endDate)}
                                                    sx={{
                                                        // Customize the date text color
                                                        '& .MuiInputBase-input': {
                                                            color: 'var(--accentIconBorder)', // Change text color for the selected date
                                                        },

                                                        // Customize the start & end labels
                                                        '& .MuiInputLabel-root': {
                                                            color: 'var(--accentIconBorder)', // Change label color for both start and end dates
                                                        },

                                                        // Customize the border color of the input
                                                        '& .MuiOutlinedInput-root': {
                                                            '& fieldset': {
                                                                borderColor: 'var(--accentIconBorder)', // Change border color when the input is not focused
                                                            },
                                                            '&:hover fieldset': {
                                                                borderColor: 'var(--accentIconBorder)', // Change border color on hover
                                                            },
                                                            '&.Mui-focused fieldset': {
                                                                borderColor: 'var(--accentIconBorder)', // Change border color when focused
                                                            },
                                                        },
                                                    }}

                                                />
                                            </LocalizationProvider>
                                        </DemoContainer>
                                    </LocalizationProvider>
                                </Box>
                                :
                                ISnumberAttribute && (attribute.uniqValue.length > 6) ?
                                    //Display String search or number Range , don't display if value amount is limited
                                    <Box display="flex" alignItems="center">
                                        <CustomNumberInput aria-label="min value input" placeholder={`Min  (${attributeMin} ~ ${attributeMax})`} min={attributeMin - 1} max={attributeMax + 1}
                                            value={minValue == -Infinity ? undefined : minValue} step={0.1}
                                            onChange={(event, val) => {
                                                if (((val || val == 0) && (val <= maxValue))) { setProperty("minValue" as any, val) };
                                                if (val == undefined || val == null) { setProperty("minValue" as any, -Infinity) };

                                                if ((val || val == 0) && val > maxValue) {
                                                    setProperty("minValue" as any, maxValue - 1)
                                                    const filterrangeResult = filterRange(attribute, maxValue - 1, maxValue)
                                                    setProperty("filteredValues" as any, filterrangeResult)
                                                }
                                                else {
                                                    const filterrangeResult = filterRange(attribute, val !== null ? val : -Infinity, maxValue !== Infinity ? maxValue : Infinity)
                                                    setProperty("filteredValues" as any, filterrangeResult)
                                                }
                                            }}
                                        />
                                        <CustomNumberInput aria-label="max value input" placeholder={`Max  (${attributeMin} ~ ${attributeMax})`} min={attributeMin - 1} max={attributeMax + 1}
                                            value={maxValue == Infinity ? undefined : maxValue} step={0.1}
                                            onChange={(_event, val) => {
                                                if ((val || val == 0) && (val >= minValue)) { setProperty("maxValue" as any, val) };
                                                if (val == undefined) { setProperty("maxValue" as any, Infinity) };
                                                if ((val || val == 0) && val < minValue) {
                                                    setProperty("maxValue" as any, minValue + 1)
                                                    const filterrangeResult = filterRange(attribute, minValue, minValue + 1)
                                                    setProperty("filteredValues" as any, filterrangeResult)
                                                }
                                                else {
                                                    const filterrangeResult = filterRange(attribute, minValue !== -Infinity ? minValue : -Infinity, val !== null ? val : Infinity || Infinity)
                                                    setProperty("filteredValues" as any, filterrangeResult)
                                                }
                                            }}
                                        />
                                    </Box>
                                    :
                                    (attribute.uniqValue.length > 6) || searchValue ?
                                        <Search sx={{ border: 0.5, borderColor: 'var(--alertGrayForegroundHover)', marginLeft: '0px !important', marginRight: '0px' }} >
                                            <SearchIconWrapper>
                                                <SearchIcon />
                                            </SearchIconWrapper>
                                            <StyledInputBase
                                                placeholder="Search specific values..."
                                                inputProps={{ 'aria-label': 'search' }}
                                                value={searchValue}
                                                onChange={(event: React.ChangeEvent<HTMLInputElement>) => handleSearchValueChange(attribute, event)}
                                                sx={{ padding: '1px', fontSize: '1.2rem', color: 'var(--alertGrayBackground)', width: '100%' }}

                                            />
                                            {searchValue && (
                                                <IconButton
                                                    onClick={() => {
                                                        setProperty("searchValue" as any, '');
                                                        setProperty("filteredValues" as any, [])
                                                    }}
                                                    size="small"
                                                    sx={{ position: 'absolute', right: '8px', color: 'var(--alertGrayBackground)' }}
                                                >
                                                    <ClearIcon />
                                                </IconButton>
                                            )}
                                        </Search> : <></>
                            }
                            {((!limitInUse && attribute.uniqValue && attribute.uniqValue.length > 2) ||
                                (limitInUse && filteredValues.length > 2)) ?
                                <ListItemButton
                                    sx={{ pl: 2, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}
                                    onClick={handleShowAllClick}
                                >
                                    {/* Show more/less label */}
                                    <div style={{ display: 'flex', alignItems: 'center', flexGrow: 1 }}>
                                        {((!limitInUse && attribute.uniqValue && attribute.uniqValue.length >= defaultNumber + 2) ||
                                            (limitInUse && filteredValues.length >= defaultNumber + 2)) ?
                                            <ListItemText
                                                primary={`Show ${showAll ? 'less' : 'all others▼'} ${showAll ? '▲' : `(${filteredValues.length > defaultNumber && limitInUse ? filteredValues.length - defaultNumber : attribute.uniqValue.length - defaultNumber})`}`}
                                                primaryTypographyProps={{ style: { fontWeight: '500' } }}
                                            />
                                            : null}
                                    </div>

                                    {/* Sort dropdown button */}
                                    {attribute.uniqValue && attribute.uniqValue.length > 1 && (() => {
                                        const open = sortAnchor?.name === attribute.name;
                                        return (
                                            <Box onClick={e => e.stopPropagation()} sx={{ display: 'flex', alignItems: 'center' }}>
                                                <LightTooltip title={`Sort: ${modeLabels[sortMode]}`} arrow>
                                                    <IconButton
                                                        size="small"
                                                        onClick={e => {
                                                            e.stopPropagation();
                                                            setSortAnchor(open ? null : { el: e.currentTarget, name: attribute.name });
                                                        }}
                                                        sx={{ fontSize: '1rem', opacity: open ? 1 : 0.65, px: 0.5 }}
                                                    >
                                                        📶
                                                    </IconButton>
                                                </LightTooltip>

                                                {/* Dropdown menu — needs Menu imported from @mui/material or @vertigis/web/ui */}
                                                <Menu
                                                    anchorEl={sortAnchor?.el}
                                                    open={open}
                                                    onClose={() => setSortAnchor(null)}
                                                    onClick={e => e.stopPropagation()}
                                                    MenuListProps={{ dense: true }}
                                                    PaperProps={{
                                                        sx: {
                                                            fontSize: '1.2rem',
                                                            minWidth: 160,
                                                            boxShadow: 3,
                                                        }
                                                    }}
                                                    transformOrigin={{ horizontal: 'right', vertical: 'top' }}
                                                    anchorOrigin={{ horizontal: 'right', vertical: 'bottom' }}
                                                >
                                                    {modes.map(mode => (
                                                        <MenuItem
                                                            key={mode}
                                                            selected={sortMode === mode}
                                                            onClick={e => {
                                                                e.stopPropagation();
                                                                setSortModes(prev => ({ ...prev, [attribute.name]: mode }));
                                                                setSortAnchor(null);
                                                            }}
                                                            sx={{ fontSize: '1.2rem' }}
                                                        >
                                                            {modeLabels[mode]}
                                                        </MenuItem>
                                                    ))}
                                                </Menu>
                                            </Box>
                                        );
                                    })()}

                                    {/* Select / Deselect All */}
                                    <Button
                                        style={{ color: 'var(--accentIconBorder) !important' }}
                                        variant="text"
                                        onClick={(event) => handleSelectAll(event, attribute.name)}
                                    >
                                        {allButtonText}
                                    </Button>
                                </ListItemButton>
                                : <></>}
                            {
                                // Situation1: Search or range & search result found, only show 'DISPLAYED FILTER VALUE' which is controlled by showall to be all or first 4                           
                                (limitInUse && filteredValues.length > 0) ?
                                    (<List component="div" disablePadding>
                                        {
                                            displayedFilterValues.map((item, idx) => (
                                                item.count > 0 ?
                                                    item.value.length > maxStringLength ?//control tooltip exist or not
                                                        <ListItemButton sx={{ pl: 3, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }} key={idx} onClick={() => handleItemClick(attribute, item)}>
                                                            <LightTooltip title={`${item.value} (${item.count})`} sx={{ fontSize: '2rem' }} arrow>
                                                                <div style={{ display: 'flex', alignItems: 'center' }}>
                                                                    <Checkbox sx={{ color: 'var(--accentIconBorder) !important' }} checked={Boolean(attribute.selection?.includes(item.value))} />
                                                                    <ListItemText primary={itemText(item, attribute.type)} primaryTypographyProps={{ style: { fontWeight: '500' } }} />
                                                                </div>
                                                            </LightTooltip>
                                                            <StyledBadge max={9999} badgeContent={item.count} color="primary" />
                                                        </ListItemButton> :
                                                        <ListItemButton sx={{ pl: 3, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }} key={idx} onClick={() => handleItemClick(attribute, item)}>
                                                            <div style={{ display: 'flex', alignItems: 'center' }}>
                                                                <Checkbox sx={{ color: 'var(--accentIconBorder) !important' }} checked={Boolean(attribute.selection?.includes(item.value))} />
                                                                <ListItemText primary={itemText(item, attribute.type)} primaryTypographyProps={{ style: { fontWeight: '500' } }} />
                                                            </div>
                                                            <StyledBadge max={9999} badgeContent={item.count} color="primary" />
                                                        </ListItemButton>
                                                    : <></>
                                            ))}
                                    </List>) : (limitInUse && (filteredValues.length == 0)) ?
                                        // Situation2: search or range but no result         
                                        <Typography variant="body1" sx={{ padding: '10px', fontSize: '1.5rem' }}>No matching values</Typography>
                                        :
                                        attribute.uniqValue?.length == 0 ?
                                            // Situation3: No data at beginning
                                            <Typography variant="body1" sx={{ padding: '10px', fontSize: '1.5rem', color: 'red' }}>No any value for this attribute.</Typography>
                                            // Situation4: no searching or range, show default displayed Values, controlled by showall   
                                            : <List component="div" disablePadding>
                                                {attribute.uniqValue && attribute.uniqValue.length > 0 && displayedValues.map((item, idx) => (
                                                    item.count > 0 ?
                                                        item.value.length > maxStringLength ?
                                                            <ListItemButton sx={{ pl: 3, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }} key={idx} onClick={() => handleItemClick(attribute, item)}>
                                                                <LightTooltip title={`${item.value} (${item.count})`} sx={{ fontSize: '2rem' }} arrow>
                                                                    <div style={{ display: 'flex', alignItems: 'center' }}>
                                                                        <Checkbox sx={{ color: 'var(--accentIconBorder) !important' }}
                                                                            checked={Boolean(attribute.selection?.includes(item.value))}
                                                                            onChange={() => handleItemClick(attribute, item)}
                                                                        />
                                                                        <ListItemText primary={itemText(item, attribute.type)} primaryTypographyProps={{ style: { fontWeight: '500' } }} />
                                                                    </div>
                                                                </LightTooltip>
                                                                <StyledBadge badgeContent={item.count} max={9999} color="primary" />
                                                            </ListItemButton> :
                                                            <ListItemButton sx={{ pl: 3, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }} key={idx} onClick={() => handleItemClick(attribute, item)}>
                                                                <div style={{ display: 'flex', alignItems: 'center' }}>
                                                                    <Checkbox sx={{ color: 'var(--accentIconBorder) !important' }}
                                                                        checked={Boolean(attribute.selection?.includes(item.value))}
                                                                        onChange={() => handleItemClick(attribute, item)}
                                                                    />
                                                                    <ListItemText primary={itemText(item, attribute.type)} primaryTypographyProps={{ style: { fontWeight: '500' } }} />
                                                                </div>
                                                                <StyledBadge badgeContent={item.count} max={9999} color="primary" />
                                                            </ListItemButton>
                                                        : <></>
                                                ))
                                                }
                                            </List>
                            }
                        </Collapse>) : <></>}
                </React.Fragment>
            );
        }
    };

    function filterData(data: Attribute[], query: string) {
        const keywords = query.split(/[\s,+;]+/).map((word) => word.trim().toLowerCase()).filter(Boolean);
        const dataCopy = structuredClone(data)
        const matchedItems = dataCopy.filter(item =>
            keywords.every(keyword => item.name.toLowerCase().includes(keyword)
                || item.alias?.toLowerCase().includes(keyword))
        );

        return matchedItems;
    }

    function sortItemsBySelection(items: fieldValue[] | undefined, selection: string[] | undefined) {
        const selectedSet = new Set(selection);

        const selected: fieldValue[] = [];
        const unselected: fieldValue[] = [];
        if (items) {
            for (const item of items) {
                if (selectedSet.has(item.value)) {
                    selected.push(item);
                } else {
                    unselected.push(item);
                }
            }
            return [...selected, ...unselected];
        }
        else return []
    }


    function filterValue(attri: Attribute, query: string) {
        const keywords = query.split(/[\s,+;]+/).map((word) => word.trim().toLowerCase()).filter(Boolean);
        const matchedItems = attri.uniqValue?.filter(item =>
            keywords.every(keyword => item.value.toLowerCase().includes(keyword))
        );
        return sortItemsBySelection(matchedItems, attri.selection) || [];
    }

    function filterRange(attri: Attribute, min: number, max: number) {
        const matchedItems = attri.uniqValue?.filter(item =>
            Number(item.value) <= max && Number(item.value) >= min
        );
        return sortItemsBySelection(matchedItems, attri.selection) || []
    }

    const groupByMelbourneDate = (data: fieldValue[]) => {
        const groupedData = new Map();

        data.forEach(({ value: value, count: count }) => {
            const melbourneDate = dayjs(value, "DD/MM/YYYY").tz('Australia/Melbourne').format('DD/MM/YYYY');
            if (!groupedData.has(melbourneDate)) {
                groupedData.set(melbourneDate, { value: melbourneDate, count: 0 });
            }
            groupedData.get(melbourneDate).count += count;
        });
        // Convert the map back to an array of objects
        return Array.from(groupedData.values());
    };

    function filterDateRange(attri: Attribute, start: Dayjs, end: Dayjs) {
        if (!attri.uniqValue) return []; // Handle null or undefined attri.uniqValue

        const matchedItems = attri.uniqValue.filter((item) => {
            const itemDate = dayjs(item.value, 'DD/MM/YYYY'); // Parse the date once
            if (!itemDate.isValid()) return false; // Handle invalid dates gracefully
            return itemDate.isAfter(start, 'day') && itemDate.isBefore(end, 'day') ||
                itemDate.isSame(start, 'day') || itemDate.isSame(end, 'day'); // Inclusive comparison
        });

        return sortItemsBySelection(matchedItems, attri.selection);
    }


    React.useEffect(() => {

        function filterValue2(attri: Attribute, query: string) {
            const keywords = query.split(/[\s,+;]+/).map((word) => word.trim().toLowerCase()).filter(Boolean);
            const matchedItems = attri.uniqValue?.filter(item =>
                keywords.every(keyword => item.value.toLowerCase().includes(keyword))
            );
            return sortItemsBySelection(matchedItems, attri.selection) || [];
        }
        if (props.openedAttribute !== undefined && (openAttriTime.current) !== (props.openedAttribute.timestamp)) {
            let newAttriData: fieldValue[] | undefined

            if (props.openedAttribute.data.type == 'date' && props.openedAttribute.data.uniqValue) {
                const values = props.openedAttribute.data.uniqValue.map(obj => Number(obj.value));
                minDate.current = Math.min(...values) / 1000;
                maxDate.current = Math.max(...values) / 1000;
                setProperty("startDate" as any, dayjs.unix(Math.min(...values) / 1000))
                setProperty("endDate" as any, dayjs.unix(Math.max(...values) / 1000))
                newAttriData = groupByMelbourneDate(props.openedAttribute.data.uniqValue)
            }
            else {
                newAttriData = props.openedAttribute.data.uniqValue
            }
            if (newAttriData) {
                newAttriData.sort((a, b) => {
                    if (a.count === b.count) {
                        return a.value.localeCompare(b.value);
                    }
                    return b.count - a.count;
                });
            }

            const updatedData = data?.map((obj) => {
                if (obj.name === props.openedAttribute?.data.name) {
                    return {
                        ...props.openedAttribute?.data,
                        uniqValue: obj.selection && obj.selection.length > 0 ? sortItemsBySelection(newAttriData, obj.selection) : newAttriData,
                        selection: obj.selection, // Keep the selection attribute from the previous data
                        hidden: obj.hidden,
                        display: true,
                    };
                }
                return { ...obj, display: false, uniqValue: undefined };
            })
            setProperty("data" as any, updatedData);

            const oldSelection = DataRef.current.find(obj => obj.name === openAttriName.current)?.selection || []

            if (openAttriName.current == props.openedAttribute?.data.name && oldSelection.length > 0 && searchValue.length > 0) {
                const searchValueResults = filterValue2(props.openedAttribute.data, searchValue);
                setProperty("filteredValues" as any, searchValueResults)
            }
            const totalDifference = props.openedAttribute.data.uniqValue?.filter(value => oldSelection?.includes(value.value))?.reduce((acc, item) => {
                const originalItemValues = openAttriValues.current?.uniqValue
                const originalItem = originalItemValues?.find(oldItem => oldItem.value == item.value);
                if (originalItem) {
                    acc += item.count - originalItem.count;
                }
                return acc;
            }, 0) || 0;

            setProperty("summary" as any, summary + totalDifference)
            openAttriName.current = props.openedAttribute?.data.name
            openAttriTime.current = props.openedAttribute?.timestamp
            openAttriValues.current = props.openedAttribute.data




        }
    }, [data, props.openedAttribute, searchValue, setProperty, summary])

    React.useEffect(() => {
        const selectedAttribute = data?.find(attribute => attribute.name === openAttriName.current);
        if (selectedAttribute && (!selectedAttribute.selection || selectedAttribute.selection.length === 0)) {
            setProperty("allButtonText" as any, 'Select All');
        }
        else if (selectedAttribute && selectedAttribute.selection?.length == selectedAttribute.uniqValue?.length) {
            setProperty("allButtonText" as any, 'Deselect All');
        }
    }, [data, setProperty]);


    React.useEffect(() => {
        if (props.request !== undefined && (requestTime.current) !== (props.request.timestamp)) {
            if (props.request.requestName == "Clear") {
                const updatedData = data?.map((attribute) => {
                    if (attribute.selection && attribute.selection.length > 0) {
                        attribute.selection = undefined
                    }
                    return attribute
                }
                )
                setProperty("data" as any, updatedData);
                setProperty("summary" as any, 0)
            }
            setProperty("rule" as any, generateSQLSummary(data, sqlRule))
            setProperty("layerName" as any, layerNameCreator(data, sqlRule))
            raiseEvent("clicked", JSON.stringify({ request: props.request.requestName, rule: generateSQLSummary(data, sqlRule), layerName: layerNameCreator(data, sqlRule) }))
            requestTime.current = props.request.timestamp
        }
    }, [data, props.request, raiseEvent, setProperty, sqlRule]);


    React.useEffect(() => {
        if (searchQuery == undefined) { setProperty("searchQuery" as any, '') }
        if (searchValue == undefined) { setProperty("searchValue" as any, '') }
        if (summary == undefined) { setProperty("summary" as any, 0) }
        if (sqlRule == undefined) { setProperty("sqlRule" as any, "AND") }
        if (rule == undefined) { setProperty("rule" as any, "") }
        if (layerName == undefined) { setProperty("layerName" as any, "") }
        if (minValue == undefined) { setProperty("minValue" as any, -Infinity) }
        if (maxValue == undefined) { setProperty("maxValue" as any, Infinity) }
        if (startDate == undefined) { setProperty("startDate" as any, defaultStart) }
        if (endDate == undefined) { setProperty("endDate" as any, defaultEnd) }
        if (filteredValues == undefined) { setProperty("filteredValues" as any, []) }
        if (sqlRule == undefined) { props.setProperty("sqlRule" as any, "AND") }
    }, [defaultEnd, defaultStart, endDate, filteredValues, layerName, maxValue, minValue, props, rule, searchQuery, searchValue, setProperty, sqlRule, startDate, summary]);

    React.useEffect(() => {
        if (searchQuery && searchQuery.length > 0) {
            const searchResults = filterData(data, searchQuery);
            const dataCopy = structuredClone(data)
            const result = dataCopy.map(item => {
                const isVisible = searchResults.some(filteredItem => filteredItem.name === item.name);
                return { ...item, visible: isVisible };
            });

            setProperty("data" as any, result);
        }

        else if (prevSearchValue.current !== '') {
            const dataCopy = structuredClone(data)
            const result = dataCopy.map(item => {
                return { ...item, visible: true };
            });
            setProperty("data" as any, result);

        }
        prevSearchValue.current = searchQuery
    }, [data, searchQuery, setProperty])


    React.useEffect(() => {
        if (props.newSum !== undefined && props.newSum !== prevSum.current) {
            setProperty("summary" as any, props.newSum.sum)
            numberRecord.current['total'] = props.newSum.sum
            if (!DataRef.current.find(obj => obj.name == openAttriName.current)?.selection || DataRef.current.find(obj => obj.name == openAttriName.current)?.selection?.length == 0) {
                tempPreviousSummary.current = props.newSum.sum
            }
            prevSum.current = props.newSum
            openAttriName.current == ''
            const updatedData = data?.map((obj) => {
                if (obj.name === props.newSum?.attri) {
                    return { ...obj, display: false, uniqValue: undefined, selection: undefined };
                }
                return obj
            })
            setProperty("data" as any, updatedData);
        }
    }, [data, props.newSum, setProperty])

    return (
        <>
            <Box style={stickyStyle}>
                {data && data.length > 9 ? <Search sx={{ border: 0.5, borderColor: 'var(--alertGrayForegroundHover)', marginLeft: '0px !important', marginRight: '0px' }} >
                    <SearchIconWrapper>
                        <SearchIcon />
                    </SearchIconWrapper>
                    <StyledInputBase
                        placeholder="Search attribute name..."
                        inputProps={{ 'aria-label': 'search' }}
                        value={searchQuery}
                        onChange={handleSearchChange}
                        sx={{ padding: '1px', fontSize: '1.4rem', color: 'var(--alertGrayBackground)', width: '100%' }}
                    />
                    {searchQuery && (
                        <IconButton
                            onClick={() => { setProperty("searchQuery" as any, '') }}
                            size="large"
                            sx={{ position: 'absolute', right: '8px', color: 'var(--alertGrayBackground)' }}
                        >
                            <ClearIcon />
                        </IconButton>
                    )}
                </Search> : <></>}
                {sqlRule && (
                    <RadioGroup
                        //Radio Group
                        row
                        aria-labelledby="demo-row-radio-buttons-group-label"
                        name="row-radio-buttons-group"
                        value={sqlRule}
                        onChange={handleSqlRule}
                    >
                        <FormControlLabel sx={{ fontSize: 15 }} value="AND" control={<Radio sx={{ color: 'var(--accentIconBorder) !important' }}
                        />} label={<span style={{ fontSize: '1.2rem' }}>Meet ALL Conditions</span>} />
                        <FormControlLabel sx={{ fontSize: 15 }} value="OR" control={<Radio sx={{ color: 'var(--accentIconBorder) !important' }} />} label={<span style={{ fontSize: '1.2rem' }}>Meet ANY Conditon</span>} />
                    </RadioGroup>
                )}
                {
                    props.footerButton ?
                        <Typography variant="body1" sx={{ fontSize: '1.5rem' }}>Selected items amount: {summary}</Typography>
                        : <Stack spacing={2} direction="row">
                            {props.buttonNaming?.[0] == " " ? <></> : <Button style={{ backgroundColor: summary <= 0 ? 'var(--alertForegroundDisabled)' : 'var(--accentIconForeground)' }} onClick={() => handleBtnClick(props.buttonNaming?.[0] || 'Apply')} variant="contained" size="medium" disabled={summary <= 0}>{(sqlRule == 'AND' || summary == 0 || recordTable?.length > 0) ? (props.buttonNaming?.[0] || 'Apply') + '(' + summary + ")" : (props.buttonNaming?.[0] || 'Apply') + '(' + summary + "+)"}</Button>
                            }
                            {props.buttonNaming?.slice(1).map((name, index) =>
                                name.length > 0 ? (
                                    <Button
                                        key={index + 1}
                                        style={{ backgroundColor: (name.endsWith("()") || name.endsWith("<>")) ? summary <= 0 ? 'var(--alertForegroundDisabled)' : 'var(--accentIconForeground)' : 'var(--accentIconForeground)' }}
                                        onClick={() => handleBtnClick(name)}
                                        variant="contained" size="medium"
                                        disabled={(name.endsWith("()") || name.endsWith("<>"))
                                            ? summary <= 0 : false}
                                    >
                                        {name.endsWith("()")
                                            ? (sqlRule == 'AND' || summary == 0 ? `${name.slice(0, -2)}(${summary})` : `${name.slice(0, -2)}(${summary}+)`)
                                            :
                                            name.endsWith("<>") ? name.slice(0, -2) : name}
                                    </Button>
                                ) : null
                            )}
                            <Button style={{ backgroundColor: 'var(--accentIconForeground)' }} onClick={() => handleClearRulesClick()} variant="contained" size="medium" >Clear</Button>
                        </Stack>
                }

            </Box>
            <ThemeProvider theme={defaultTheme}>
                {
                    data && data.every(item => item.visible === false) ?
                        <Typography variant="body1" sx={{ padding: '10px', fontSize: '2rem' }}>No matching attribute name</Typography>
                        :
                        searchQuery && searchQuery.length > 0 ?
                            <List sx={{ width: '100%' }} component="nav">
                                {data?.filter(attribute => attribute.visible).map((attribute) => renderAttribute(attribute))
                                }
                            </List> :
                            <List sx={{ width: '100%' }} component="nav">
                                {data && data.filter(attribute => !attribute.hidden).map((attribute) => renderAttribute(attribute))}
                                {data && data.every(item => !item.hidden) ?
                                    null :
                                    <><ListItemButton onClick={toggleFolder}>
                                        <Box
                                            component="img"
                                            sx={{
                                                height: 22,
                                                width: 22,
                                            }}
                                            alt="The category icon"
                                            src={"https://cdn-icons-png.flaticon.com/512/3767/3767084.png"}// Use the item's img property or based on type
                                        />
                                        <ListItemText primary={folderOpen ? 'Hide' : 'Show all other Attributes..'}
                                            sx={{ pl: 5 }} primaryTypographyProps={{ style: { fontWeight: 600, color: "grey" } }} />
                                        {folderOpen ? <ExpandLess /> : <ExpandMore />}
                                    </ListItemButton>
                                        <Collapse in={folderOpen} timeout="auto" unmountOnExit>
                                            <List component="div" disablePadding>
                                                {data?.filter(attribute => attribute.hidden).map((attribute) => renderAttribute(attribute))}
                                            </List>
                                        </Collapse></>
                                }
                            </List>
                }
            </ThemeProvider>
        </>
    );
}

const AttributeFilterElementRegistration: FormElementRegistration<AttributeFilterProps> = {
    component: AttributeFilter,
    getInitialProperties: () => ({
        fields: [{
            alias: "OBJECTID",
            display: false,
            name: "OBJECTID",
            type: "oid",
            visible: true
        },
        {
            alias: "Start Date",
            display: false,
            name: "StartDate",
            type: "date",
            visible: true
        },
        {
            alias: "Requestor",
            display: false,
            name: "Requestor",
            type: "string",
            visible: true
        }],
    }),
    id: "AttributeFilter",
};

export default AttributeFilterElementRegistration;
