/* eslint-disable prefer-const */
import * as React from "react";
import { FormElementProps, FormElementRegistration } from "@vertigis/workflow";
import { styled } from '@mui/material/styles';
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 FolderCopyIcon from '@mui/icons-material/FolderCopy';
import ExpandLess from '@mui/icons-material/ExpandLess';
import ExpandMore from '@mui/icons-material/ExpandMore';
import SearchIcon from '@vertigis/web/ui/icons/Search';
import InputBase from '@mui/material/InputBase';
import Box from "@vertigis/web/ui/Box"
import Typography from  '@vertigis/web/ui/Typography';
import { createTheme } from "@mui/material/styles";
import { ThemeProvider } from "@emotion/react";
import Checkbox from '@vertigis/web/ui/Checkbox';
import  IconButton  from '@vertigis/web/ui/IconButton';
import { Category, Clear as ClearIcon } from '@mui/icons-material';
import Chip from '@mui/material/Chip';
import Tooltip, { TooltipProps, tooltipClasses } from '@mui/material/Tooltip';


/**
 * 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 ItemSearcher_V2Props extends FormElementProps<string> { 

    itemData: Category[]; // Structured data passed as prop
    setting?: settingConfig;
    request?: { type: string, timestamp: number, data?: any };
    openCategories: Set<string>;//Status storage property only
    searchQuery:string;//Status storage property only
    selection:{ [key: string]: { [key: string]: Item[] } }//Status storage property only
    loadDataTime:number;//provide cabability of loading a totally different data
}


/**
 * Provide strictly structured item data to display them and search.
 * @displayName ItemSearcher_V2
 * @description Display list of items and allow user to search based on name, description & tag. 
 * @param props     collapseIcon?: string;
    expandIcon?: string;//replacing the drop down icon on right
    checkbox?: boolean;//If true, make items selectable checkbox rather than click 
    checkboxGroup?:boolean;//If true, make category itself selectable
    triggerChange?:boolean;//If true, trigger changed event when being checkbox mode
    simpleSearch?: boolean; // decide two mode in search result: clear Search show items only and rank them by relativity. If false, use filter on original Hierarchy, show items has keyword or group whose name has keyword and all its children.
    initialCollapse?: boolean // decide whether collapse all group at beginning, default is collapse all
 */

const LightTooltip = styled(({ className, ...props }: TooltipProps) => (
    <Tooltip {...props} classes={{ popper: className }} />
))(({ theme }) => ({
    [`& .${tooltipClasses.tooltip}`]: {
        boxShadow: theme.shadows[1],
        fontSize: 10,
    },
}));

const Search = styled('div')(({ theme }) => ({
    position: 'relative',
    borderRadius: theme.shape.borderRadius,
    backgroundColor: 'var(--alertGrayForeground)',
    marginRight: theme.spacing(2),
    marginLeft: 0,
    width: '100%',
    [theme.breakpoints.up('sm')]: {
        marginLeft: theme.spacing(3),
        width: 'auto',
    },
}));

const SearchIconWrapper = styled('div')(({ theme }) => ({
    padding: theme.spacing(0, 2),
    height: '100%',
    position: 'absolute',
    pointerEvents: 'none',
    display: 'flex',
    alignItems: 'center',
    justifyContent: 'center',

}));

interface Item {
    name: string;
    description?: string;
    tag?: string[];
    value?: any;
    img?: string;
    score?: number;
    parentValue?: any;
    action?: "displayLayer" | "tick";
}

interface Category {
    name: string;
    items?: Item[];
    childCategory?: Category[];
    collapsed?: boolean; //true to hide children. It's above setting Config initial collapse.
    img?: string;
    checkbox?: boolean;   //If true, show checkbox that makes all child items selected/unselected 
    index?: number; //as a child Category, indicate the position of this child category compared to other sibling items/categories
    parentCate?: string;
    description?:string;
}

interface settingConfig {
    checkbox?: boolean;   //If true, show checkbox rather than raise click event
    collapseIcon?: string;//replace the fold up icon on right
    expandIcon?: string;//replacing the drop down icon on right
    triggerChange?: boolean;//If true, trigger changed event when under checkbox mode
    triggerClick?: boolean;//If true, trigger click event when under checkbox mode
    simpleSearch?: boolean; // decide two mode in search result: clear Search show items only and rank them by relativity. If false, use filter on original Hierarchy, show items has keyword or group whose name has keyword and all its children.
    initialCollapse?: boolean;// decide whether collapse all group at beginning, default is collapse all
    tooltip?: "tag" | "description" | "value" | Array<"tag" | "description" | "value">;//tooltip of item content. Can be any combination of description, value (link etc), tag (property unvisible but used for searching)
    searchHolder?: string; //Text placeholder used in search bar 
    requestLayer?: boolean;//true if the item is a layer group url and need further process
    reset?: number; //If loading another totally different content, give timestamp number for reset parameter to reload itemData
    showCount?: boolean;  // display item count after category name
    flatResut?: boolean;//decide the output to be result/layer or result/group/layer
    matchAnyKeyword?:boolean;//When search keywords (detected seperation by many spliters), fulfil all keywords or any of keyword.
    maxSelectableItems?:number;//maximum number of items that can be selected at the same time. If 0, no limit.
    forceSort?:boolean;//if true, sort the items in the same order as the original data. If false, sort by name.
}



const StyledInputBase = styled(InputBase)(({ theme }) => ({
    color: 'inherit',
    '& .MuiInputBase-input': {
        padding: theme.spacing(1, 1, 1, 0),
        paddingLeft: `calc(1em + ${theme.spacing(4)})`,
        transition: theme.transitions.create('width'),
        width: '100%',
        [theme.breakpoints.up('md')]: {
            width: '100%',
        },
    },

}));

const defaultTheme = createTheme({
    components: {
        MuiListItemText: {
            styleOverrides: {
                secondary: {
                    color: 'inherit',
                },
            },
        },
    },

    typography: {
        fontSize: 18
    },
});





// Function to get all items from category and its subcategories
function ItemSearcher_V2(props: ItemSearcher_V2Props): React.ReactElement {
    const { raiseEvent ,openCategories,searchQuery,selection,loadDataTime, setValue, setProperty} = props;
    if (openCategories ==undefined){setProperty("openCategories" as any,new Set())}
    if (searchQuery ==undefined){setProperty("searchQuery" as any,'')}
    if (selection ==undefined){setProperty("selection" as any,{})}
    if (loadDataTime ==undefined){setProperty("loadDataTime" as any,0)}
    const [data, setData] = React.useState<Category[]>(props.itemData);
    const preSearchOpenCategories = React.useRef<Set<string>>(new Set());
    const currentOpenCategories = React.useRef<Set<string> >(new Set());
    const prevSearchValue = React.useRef<string>('')
    const [filteredData, setFilteredData] = React.useState<Category[]>([]); // New state for filtered data
    const requestTime = React.useRef<number | null>(null);
    // Handle search input changes
    const handleSearchChange = (event: React.ChangeEvent<HTMLInputElement>) => {
        setProperty("searchQuery" as any,event.target.value); // Update search query
    };


    function getTotalSelectedItems() {
        let total = 0;
        for (let catKey in selection) {
            for (let subcatKey in selection[catKey]) {
                total += selection[catKey][subcatKey].length;
            }
        }
        return total;
    }

    function expandIcon(collapseIcon: string, expandIcon: string, openCats, catName: string) {

        openCategories.has(catName) ? <ExpandLess /> : <ExpandMore />
        if (Boolean(expandIcon) && openCats.has(catName)) return (<Box
            component="img"
            sx={{
                height: 18,
                width: 18,
                marginLeft: '8px',
    
            }}
            alt="The category icon"
            src={expandIcon} // Use the item's img property
        />)
        else if (Boolean(collapseIcon) && !openCats.has(catName)) return (<Box
            component="img"
            sx={{
                height: 18,
                width: 18,
                marginLeft: '8px',
            }}
            alt="The category icon"
            src={collapseIcon} // Use the item's img property
        />)
        else if (!expandIcon && openCats.has(catName)) return (<ExpandLess />)
        else if (!expandIcon && !openCats.has(catName)) return (<ExpandMore />)
    }
    

    function findCategory(item: Item) {
        return props.itemData.find(category =>
            category.items?.some(existingItem => existingItem.value == item.value)
        ) || data[0];
    }

    function tickItem(item: Item, category: Category) {
        let selectCopy = structuredClone(selection)
        
        if (isSelected(item, category.name)) {
            //uncheck item
            for (let key in selectCopy) {
                let element = selectCopy[key]
                // Check if the element is an object containing nested arrays of Items
                for (let subKey in element) {
                    if (element[subKey]) {
                        let index = element[subKey].findIndex(a => a.name === item.name && (a.value === item.value || a.description === item.description));
                        if (index !== -1) {
                            element[subKey].splice(index, 1);
                            // If the subcategory is empty after removal, delete the subcategory
                            if (element[subKey].length === 0) {
                                delete element[subKey];
                            }
                        }
                    }
                }
                if (Object.values(selectCopy[key]).length == 0) {
                    delete selectCopy[key];
                }// If the category is empty after removal, delete the category
            }
        }
        else {
            // Check if we've reached maximum selectable items limit
            const totalSelectedItems = getTotalSelectedItems();
            if (props.setting?.maxSelectableItems && totalSelectedItems >= props.setting.maxSelectableItems) {
                // Prevent adding more items if limit is reached
                raiseEvent("error" as any, JSON.stringify({ type: "maxItemsReached", message: `You can only select up to ${props.setting.maxSelectableItems} items` }));
                return;
            }
            
            // Add the item to the specified category
            if (category) {
                if (category.parentCate) {
                    if (!selectCopy[category.parentCate]) {
                        selectCopy[category.parentCate] = {};
                    }
                    if (!selectCopy[category.parentCate][category.name]) {
                        selectCopy[category.parentCate][category.name] = [];
                    }
                    selectCopy[category.parentCate][category.name].push((delete item.score, item));
                }
                else {
                    if (!selectCopy[category.name]) {
                        selectCopy[category.name] = {};
                    }
                    if (!selectCopy[category.name]["directItem"]) {
                        selectCopy[category.name]["directItem"] = [];
                    }
                    selectCopy[category.name]["directItem"].push((delete item.score, item));
                }
            }
        }
        setProperty("selection" as any, selectCopy)
    }

    // Handle click on items to raise an event
    const handleItemClick = (item: Item, category: Category) => {
        if (props.setting?.checkbox) {
            if (props.setting?.triggerChange) {
                raiseEvent("changed", JSON.stringify(item)); // Pass the entire item data structure
            }
            if (props.setting?.triggerClick) {
                raiseEvent("clicked", JSON.stringify(item)); // Pass the entire item data structure
            }
            if (props.setting?.requestLayer && item.action == "displayLayer") {
                raiseEvent("clicked", JSON.stringify({ action: "openLayer", data: item })); // Pass the entire item data structure
            }
            if (!props.setting?.requestLayer || !(item.action == "displayLayer")) {
                tickItem(item, category)
            }
        }
        else {
            raiseEvent("clicked", JSON.stringify(item)); // Pass the entire item data structure
        }
    };

    const handleCategoryCheck = (event: React.MouseEvent<HTMLButtonElement, MouseEvent>, category: Category) => {
        event.stopPropagation();
        let selectCopy = structuredClone(selection);
        
        // For "allSelected" case, just remove all items (no limit check needed)
        if (isChildSelected(category) === "allSelected") {
            if (!category.parentCate) {
                delete selectCopy[category.name];
            } else {
                delete selectCopy[category.parentCate][category.name];
            }
        } 
        // For adding items, check limit
        else {
            const currentTotal = getTotalSelectedItems();
            let itemsToAdd = 0;
            
            // Calculate how many items would be added
            if (!category.parentCate) {
                if (category.items) {
                    for (let item of category.items) {
                        if (!isSelected(item, category.name)) {
                            itemsToAdd++;
                        }
                    }
                }
            } else {
                if (category.items) {
                    for (let item of category.items) {
                        if (!isSelected(item, category.name)) {
                            itemsToAdd++;
                        }
                    }
                }
            }
            
            // Check if adding these items would exceed the limit
            if (props.setting?.maxSelectableItems && (currentTotal + itemsToAdd > props.setting.maxSelectableItems)) {
                raiseEvent("error" as any, JSON.stringify({ type: "maxItemsReached", message: `You can only select up to ${props.setting.maxSelectableItems} items` }));
                return;
            }
            
            // Proceed with adding items if within limit
            if (!category.parentCate) {
                //only one level
                if (category.items) {
                    for (let item of category.items) {
                        if (!isSelected(item, category.name)) {
                            // If item is not selected, check it
                            if (!selectCopy[category.name]) {
                                selectCopy[category.name] = {}
                                selectCopy[category.name]["directItem"] = [];
                            }
                            selectCopy[category.name]["directItem"].push((delete item.score, item));
                        }
                    }
                }
            } else {
                //two levels
                if (category.items) {
                    for (let item of category.items) {
                        if (!isSelected(item, category.name)) {
                            if (!selectCopy[category.parentCate]) {
                                selectCopy[category.parentCate] = {};
                            }
                            // If item is not selected, check it
                            if (!selectCopy[category.parentCate][category.name]) {
                                selectCopy[category.parentCate][category.name] = [];
                            }
                            selectCopy[category.parentCate][category.name].push((delete item.score, item));
                        }
                    }
                }
            }
        }
        
        // Update selection state
        setProperty("selection" as any, selectCopy);
    };


    // Handle category toggle open/close manually
    const handleCategoryToggle = (categoryName: string) => {
        const updatedOpenCategories = new Set(openCategories); // Clone current state

        if (updatedOpenCategories.has(categoryName)) {
            updatedOpenCategories.delete(categoryName); // Close category
        } else {
            updatedOpenCategories.add(categoryName); // Open category
        }
        
        setProperty("openCategories" as any, updatedOpenCategories); // Update with new value
    };


    function countAllItems(category: Category): number {
        let itemCount = 0;

        // Add items of the current category (if any)
        if (category.items) {
            itemCount += category.items.length;
        }

        // Recursively count items in child categories (if any)
        if (category.childCategory) {
            category.childCategory.forEach(childCategory => {
                itemCount += countAllItems(childCategory);  // Recursive call for each child category
            });
        }

        return itemCount;
    }

    function isSelected(item: Item, cateName?: string) {
        // Type guard to check if an element is an Item
        if (!cateName) {
            for (let key in selection) {
                for (let subkey in selection[key]) {
                    let index = selection[key][subkey].findIndex(a => a.name === item.name && a.value === item.value);
                    if (index !== -1) {
                        return true;
                    }
                }
            }
        }
        else {
            if (selection[cateName]?.["directItem"]) {
                let index = selection[cateName]["directItem"].findIndex(a => a.name === item.name && a.value === item.value);
                if (index !== -1) {
                    return true;
                }
            }
            
                for (let key in selection) {
                    if (selection[key][cateName]) {
                        let index = selection[key][cateName].findIndex(a => a.name === item.name && a.value === item.value);
                        if (index !== -1) {
                            return true;
                        }
                    }
                }
        }
        return false;
    }


    const isChildSelected = (category: Category) => {
        let allSelected = true;
        let partSelected = false;
        let noneSelected = true;

        // Check items of the current category
        if (category.items) {
            for (let item of category.items) {
                const selected = isSelected(item, category.name);
                if (selected) {
                    partSelected = true;
                    noneSelected = false;
                } else {
                    allSelected = false;
                }
            }
        }

        // Recursively check child categories
        if (category.childCategory) {
            for (let child of category.childCategory) {
                const childStatus = isChildSelected(child);
                if (childStatus === 'notSelected') {
                    noneSelected = true;
                } else if (childStatus === 'partSelected') {
                    partSelected = true;
                } else if (childStatus === 'allSelected') {
                    allSelected = true;
                }
            }
        }

        // Determine the category selection state
        if (allSelected) return 'allSelected';
        if (partSelected) return 'partSelected';
        if (noneSelected) return 'notSelected';
        return "error"
    }




    // Get search rank for an item based on search query
    const getSearchRank = (item: Item, query: string) => {
        const keywords = query.split(/[\s,+;]+/).map((word) => word.trim().toLowerCase()).filter(Boolean);
        let score = 0;

        keywords.forEach((keyword) => {
            if (item.name.toLowerCase().includes(keyword)) score += 2.5;
            else if (item.description && item.description.toLowerCase().includes(keyword)) score += 2;
            else if (item.tag && item.tag.some((tag) => tag.toLowerCase().includes(keyword))) score += 1.5;
        });

        return score;
    };




    // Recursively get all items from category and its child categories
    function getAllItemsFromCategory(category: Category): Item[] {
        let flattenItems: Item[] = [];

        if (category.items) {
            flattenItems = category.items.map((item) => ({
                ...item,
                score: getSearchRank(item, searchQuery),
            }));
        }

        if (category.childCategory && Array.isArray(category.childCategory)) {
            for (const child of category.childCategory) {
                flattenItems = flattenItems.concat(getAllItemsFromCategory(child));
            }
        }

        return flattenItems;
    }

    const sumSelection = (category: Category): Item[] => {
        let result: Item[] = []
        if (!category.parentCate) {
            //category is first level, get all direct item and child cate items
            for (let key in selection[category.name]) {
                result = result.concat(selection[category.name][key])
            }
        }
        else {
            result = selection[category.parentCate]?.[category.name] || []
        }

        return result.length > 0 ? result : [];
    };


    function cleanSelection(selection: { [key1: string]: { [key2: string]: Item[] } }) {
        for (const key1 in selection) {
          if (Object.keys(selection[key1]).length>0) {
            const key2Object = selection[key1];
      
            // Iterate over key2 within key1
            for (const key2 in key2Object) {
              if (key2Object.key2) {
                // If key2 has an empty array, delete the key2
                if (key2Object[key2].length === 0) {
                  delete key2Object[key2];
                }
              }
            }
      
            // If key1 has no more key2s left, delete key1
            if (Object.keys(key2Object).length === 0) {
              delete selection[key1];
            }
          }
          else{
            delete selection[key1]
          }
        }
        return selection;
      }
      
          // Render categories with items and nested child categories
    const renderCategory = (category: Category, level: number): React.ReactNode => {

        function truncateString(str) {
            if (str.length > 10) {
                return str.substring(0, 8) + '...';
            }
            return str;
        }
        const chipLabel = () => {
            return sumSelection(category)?.length > 0 ?
                sumSelection(category)?.length > 1 ?
                    sumSelection(category).length + " selected" :
                    truncateString(sumSelection(category)[0].name)
                : ""
        }

        const handleDeleteSelection = (category: Category) => {
            if (category.parentCate) {
                const updatedSelection = { ...selection };
                updatedSelection[category.parentCate][category.name] = [];
                 setProperty("selection" as any,updatedSelection);
            } else {
                const updatedSelection = { ...selection };
                updatedSelection[category.name] = {};
                 setProperty("selection" as any,updatedSelection);
            }

        };

        const chipElement = (
            <Chip
                label={chipLabel()}
                sx={{
                    fontSize: '1.1rem',
                    color: 'var(--accentIconBackground)',
                    backgroundColor: 'var(--alertAmberBackground)',
                    '& .MuiChip-deleteIcon': { color: 'var(--accentIconBackground)' }

                }}
                onDelete={() => handleDeleteSelection(category)}
            />
        );

        const itemContent = (item: Item, cateName?: string) => (<div style={{ display: 'flex', alignItems: 'center' }}>

            {item.img && typeof item.img === 'string' && item.img.trim() !== '' && (
                <Box
                    component="img"
                    sx={{
                        height: 18,
                        width: 18,
                        marginLeft: '8px',
                    }}
                    alt="The item icon"
                    src={item.img} // Use the item's img property
                />
            )}
            {props.setting?.checkbox ? <ListItemIcon sx={{ minWidth: '35px' }}>
                <Checkbox sx={{ color: 'var(--accentIconBorder) !important' }} checked={isSelected(item, cateName)} />
            </ListItemIcon> : null}

            <ListItemText primary={item.name} secondary={item.description} sx={{ pl: item.img ? 2 : 0 }} primaryTypographyProps={{ style: { fontWeight: (900 - 200 * (level + 1)).toString()} }}   secondaryTypographyProps={{ style: { fontWeight: "300" } }}/>
        </div>)
        return (
            <React.Fragment key={category.name}>
                <ListItemButton onClick={() => handleCategoryToggle(category.name)} sx={{ pl: level * 2, py:0  , minHeight:'35px'}}> 
                    {props.setting?.checkbox && category.checkbox ?
                        <ListItemIcon sx={{ minHeight:'35px' }}>
                            <Checkbox sx={{ color: 'var(--accentIconBorder) !important' }}
                                checked={['allSelected', 'partSelected'].includes(isChildSelected(category))}
                                onClick={(event) => handleCategoryCheck(event, category)}
                            />
                        </ListItemIcon> : null
                    }
                    {category.img && typeof category.img === 'string' && category.img.trim() !== '' ? (
                        <Box
                            component="img"
                            sx={{
                                height: 18,
                                width: 18,
                                marginLeft: '8px',
                            }}
                            alt="The category icon"
                            src={category.img} // Use the item's img property
                        />
                    ) : (<ListItemIcon sx={{ minWidth: '20px' }}>
                        <FolderCopyIcon  sx={{ color: "var(--accentIconForeground)" }} />
                    </ListItemIcon>)
                    }
                    <ListItemText primary={props.setting?.showCount ? category.name + ` (${countAllItems(category)})` : category.name} secondary={category.description}   sx={{ pl: 1 }} primaryTypographyProps={{ style: {fontWeight: (900 - level * 200).toString() ,fontSize:level==0?'1.2rem':undefined} }}  secondaryTypographyProps={{ style: { fontWeight: "300" } }}/>
                    {//selection chip
                        props.setting?.checkbox && sumSelection(category).length > 0 ?
                            <LightTooltip
                                title={sumSelection(category).map(a => a.name).join(',')}
                                arrow>
                                {chipElement}
                            </LightTooltip>
                            : <></>
                    }
                    {expandIcon(props.setting?.collapseIcon ?? '', props.setting?.expandIcon ?? '', openCategories, category.name)}
                </ListItemButton>
                <Collapse in={openCategories && openCategories.has(category.name)} timeout="auto" unmountOnExit>
                    <List component="div" disablePadding>
                        {getMergedChildren(category).map((child, idx) => {
                            if (child.type === 'item') {
                                const item:Item = child.content;
                                return (
                                    <ListItemButton
                                        sx={{ pl: (level + 1) * 2, py:0 , minHeight:'35px'}}
                                        key={`item-${idx}`}
                                        onClick={() => handleItemClick(item, category)}
                                    >
                                        {props.setting?.tooltip ? (
                                            typeof props.setting?.tooltip == "string" ? (
                                                <LightTooltip title={item[props.setting?.tooltip]}  arrow>
                                                    {itemContent(item,category.name)}
                                                </LightTooltip>
                                            ) : (
                                                <LightTooltip
                                                    title={props.setting?.tooltip.map(key => item[key]).join(" ; ")}
                                                    arrow
                                                >
                                                    {itemContent(item,category.name)}
                                                </LightTooltip>
                                            )
                                        ) : (
                                            itemContent(item,category.name)
                                        )}
                                    </ListItemButton>
                                );
                            } else {
                                // Render child category
                                const childCategory = child.content;
                                return renderCategory(childCategory, level + 1);
                            }
                        })}
                    </List>
                </Collapse>
            </React.Fragment>
        );
    };

    const getMergedChildren = (category: Category) => {
        const items = category.items || [];
        const childCategories = category.childCategory || [];
    
        // Convert items to an array with type marker
        const itemsWithType = items.map(item => ({
            type: 'item',
            content: item
        }));
    
        // Convert child categories to an array with type marker
        const categoriesWithType = childCategories.map(category => ({
            type: 'category',
            content: category
        }));
    
        // Sort based on forceSort setting
        if (props.setting?.forceSort !== false) {
            // Default behavior: sort alphabetically
            categoriesWithType.sort((a, b) => 
                a.content.name.localeCompare(b.content.name)
            );
            
            itemsWithType.sort((a, b) => 
                a.content.name.localeCompare(b.content.name)
            );
        } else {
            // When forceSort is false: respect original order
            // For child categories, sort by index if available
            categoriesWithType.sort((a, b) => {
                if (a.content.index !== undefined && b.content.index !== undefined) {
                    return a.content.index - b.content.index;
                }
                return 0; // Keep original order if no index
            });
            
            // For items, we keep the original order by not sorting
        }
    
        // Return categories first, then items
        return [...categoriesWithType, ...itemsWithType];
    };


    // Filter items based on the search query
    const filteredItems = data
        .flatMap((category) => getAllItemsFromCategory(category))
        .map((item) => ({
            ...item,
            score: getSearchRank(item, searchQuery),
        }))
        .filter((item) => item.score > 0)
        .sort((a, b) => b.score - a.score); // Sort by total score in descending order


    React.useEffect(() => {
        function filterData(data: Category[], query: string): Category[] {
            const keywords = query.split(/[\s,+;]+/).map((word) => word.trim().toLowerCase()).filter(Boolean);
    
            function searchCategory(category: Category): Category | null {
            // Different matching logic based on the matchAnyKeyword setting
            let categoryMatches: boolean;
            
            if (props.setting?.matchAnyKeyword) {
                // ANY keyword matching (original logic) - using regex
                const keywordRegex = new RegExp(keywords.join("|"), "i");
                categoryMatches = keywordRegex.test(category.name);
                
                // Filter items based on name, description, or tags
                const matchedItems = category.items?.filter(item =>
                    keywordRegex.test(item.name) ||
                    (item.description && keywordRegex.test(item.description)) ||
                    (item.tag && item.tag.some(tag => keywordRegex.test(tag)))
                );
                
                // Recursively filter child categories
                const matchedChildCategories = category.childCategory
                    ?.map(childCat => searchCategory(childCat))
                    .filter((childCat): childCat is Category => childCat !== null);
                
                if (categoryMatches) {
                    return {
                        ...category,
                        items: category.items || [],
                        childCategory: category.childCategory || []
                    };
                }
                
                // If no match in the category name, check if items or child categories match
                if ((matchedItems && matchedItems.length > 0) || (matchedChildCategories && matchedChildCategories.length > 0)) {
                    return {
                        ...category,
                        items: matchedItems || [],
                        childCategory: matchedChildCategories || []
                    };
                }
            } else {
                // ALL keywords matching logic
                categoryMatches = keywords.every(keyword => 
                    category.name.toLowerCase().includes(keyword)
                );
                
                // Filter items that match ALL keywords
                const matchedItems = category.items?.filter(item =>
                    keywords.every(keyword => 
                        item.name.toLowerCase().includes(keyword) ||
                        (item.description && item.description.toLowerCase().includes(keyword)) ||
                        (item.tag && item.tag.some(tag => tag.toLowerCase().includes(keyword)))
                    )
                );
                
                // Recursively filter child categories
                const matchedChildCategories = category.childCategory
                    ?.map(childCat => searchCategory(childCat))
                    .filter((childCat): childCat is Category => childCat !== null);
                
                if (categoryMatches) {
                    return {
                        ...category,
                        items: category.items || [],
                        childCategory: category.childCategory || []
                    };
                }
                
                // If no match in the category name, check if items or child categories match
                if ((matchedItems && matchedItems.length > 0) || (matchedChildCategories && matchedChildCategories.length > 0)) {
                    return {
                        ...category,
                        items: matchedItems || [],
                        childCategory: matchedChildCategories || []
                    };
                }
            }
    
            // Exclude this category if no matches found in items or child categories
            return null;
        }
    
    
            // Apply the search to each root category and filter out null results
            const result = data.map(category => searchCategory(category)).filter((cat): cat is Category => cat !== null);
            return result
    
        }
    

        if (searchQuery) {
            
            // Store the current state when search begins
            if (searchQuery.length === 1) {
                preSearchOpenCategories.current = new Set(currentOpenCategories.current);
            }

            const searchResults = filterData(data, searchQuery);
            setFilteredData(searchResults);

            // Expand categories with search results
            const searchOpenCategories = new Set<string>();
            const addToOpenCategories = (category: Category) => {
                searchOpenCategories.add(category.name);
                category.childCategory?.forEach(addToOpenCategories);
            };

            searchResults.forEach(addToOpenCategories);
             setProperty("openCategories" as any,searchOpenCategories);
        }
        if (!searchQuery) {
            // Restore pre-search state if exists
            if (prevSearchValue.current==''){
                currentOpenCategories.current = openCategories;
            }
            else {
            if (preSearchOpenCategories.current.size > 0) {
                setProperty("openCategories" as any,preSearchOpenCategories.current);
                setFilteredData([]);
                preSearchOpenCategories.current = new Set()
            }
            return;
        }
    }
        prevSearchValue.current=searchQuery

    }, [searchQuery, data, props.setting?.matchAnyKeyword, openCategories, setValue, setProperty]);



    React.useEffect(() => {

        function removeDirectItems() {
            let selectCopy = structuredClone(selection)
            let result = {}
            for (let key in selection) {
                result[key] = selectCopy[key].directItem;
            }
            return result;
        }

        if (props.setting?.checkbox && props.request && props.request.type == "submit" && (requestTime.current) !== (props.request.timestamp)) {
            raiseEvent("clicked", JSON.stringify({ action: "submit", selectionResult: props.setting.flatResut ? removeDirectItems() : cleanSelection(selection) })); // Pass the entire item data structure
            requestTime.current = props.request.timestamp;
        }
        if (props.setting?.checkbox && props.request && props.request.type == "clearSelection" && (requestTime.current) !== (props.request.timestamp)) {
            setProperty("selection" as any,{})
            requestTime.current = props.request.timestamp;
        }
        if (props.setting?.checkbox){
            setValue( JSON.stringify( {selectionResult:props.setting.flatResut ? removeDirectItems() : cleanSelection(selection)}))
            setProperty("selectionResult" as any,props.setting.flatResut ? removeDirectItems() : cleanSelection(selection))
        }

    }, [props.request, props.setting, raiseEvent, selection, setProperty, setValue])


    React.useEffect(() => {
        if (props.request && props.request.type == "displayLayer" && (requestTime.current) !== (props.request.timestamp)) {
            let dataCopy = structuredClone(data)

            dataCopy.forEach((category: Category) => {
                if (category.items) {
                    const item = category.items.find(item => item.name === props.request?.data.title);
                    const foundindex = category.items.findIndex(item => item.name === props.request?.data.title) - 1;
                    if (item !== undefined) {
                        if (!category.childCategory) {
                            category.childCategory = [];
                        }
                        category.childCategory.push({
                            name: props.request?.data.title,
                            index: foundindex,
                            checkbox: true,
                            items: props.request?.data.layers.map(sublayer => ({
                                "name": sublayer.title,
                                "value": sublayer.id,
                                "parentName": item.name,
                                "parentValue": item.value,
                            }))
                        })
                        category.items = category.items.filter(item => item.name !== props.request?.data.title);
                        
                            const newState = new Set(openCategories);
                            // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
                            newState.add(props.request?.data.title || ''); // Open category if it's closed
                            setProperty("openCategories" as any, newState);
                       
                    }
                }
            })
            setData(dataCopy)
            requestTime.current = props.request.timestamp
        }
    }, [data, openCategories, props.request, setProperty])

    React.useEffect(() => {
        if (props.setting?.reset && (loadDataTime) !== (props.setting.reset)) {
            setData(props.itemData)
            setProperty("selection" as any,{})
            setProperty("openCategories" as any,new Set())
            setProperty("searchQuery" as any,'')
            setProperty("loadDataTime" as any,props.setting.reset)
        }
    }, [loadDataTime, props.itemData, props.setting?.reset, setProperty])




    return (
        <>
            <Search sx={{ border: 2, borderColor: 'var(--alertGrayForegroundHover)', marginLeft: '0px !important', marginRight: '0px' }} >
                <SearchIconWrapper>
                    <SearchIcon />
                </SearchIconWrapper>
                <StyledInputBase
                    placeholder={props.setting?.searchHolder || "Search…"}
                    inputProps={{ 'aria-label': 'search' }}
                    value={searchQuery}
                    onChange={handleSearchChange}
                    sx={{ padding: '1px', fontSize: '1.4rem', color: 'var(--accentIconBorder) !important', width: '100%' }}
                />
                {searchQuery && (
                    <IconButton
                        onClick={() => {  setProperty("searchQuery" as any,'') }}
                        size="large"
                        sx={{ position: 'absolute', right: '8px', color: 'var(--alertGrayBackground) !important' }}
                    >
                        <ClearIcon />
                    </IconButton>
                )}
            </Search>

            <ThemeProvider theme={defaultTheme}>

                <List sx={{ width: '100%' }} component="nav">
                    {props.setting?.simpleSearch && searchQuery && filteredItems.length > 0 ? (
                        filteredItems.map((item, index) => (
                            <ListItemButton key={index} sx={{minHeight:'35px'}}>
                                {item.img && typeof item.img === 'string' && item.img.trim() !== '' && (
                                    <Box
                                        component="img"
                                        sx={{
                                            height: 18,
                                            width: 18,
                                            marginLeft: '8px',
                                        }}
                                        alt="The item icon"
                                        src={item.img} // Use the item's img property
                                    />
                                )}
                                {props.setting?.checkbox ? <ListItemIcon sx={{ minWidth: '35px' }}>
                                    <Checkbox sx={{ color: 'var(--accentIconBorder) !important' }} checked={isSelected(item)} />
                                </ListItemIcon> : null}
                                <ListItemText
                                    primary={item.name}
                                    secondary={item.description}
                                    onClick={() => handleItemClick(item, findCategory(item))}
                                    primaryTypographyProps={{ style: { fontWeight: '600' } }}
                                />

                            </ListItemButton>
                        ))
                    ) : searchQuery && filteredData.length > 0 ?
                        (
                            filteredData.map((category) => renderCategory(category, 0))
                        ) :
                        searchQuery && filteredData.length === 0 ? (
                            <Typography variant="body1" sx={{ padding: '10px', fontSize: '1.5rem' }}>No result found</Typography>
                        ) : (
                            data.map((category) => renderCategory(category, 0))
                        )}
                </List>


            </ThemeProvider>
        </>
    );
}

const ItemSearcher_V2ElementRegistration: FormElementRegistration<ItemSearcher_V2Props> = {
    component: ItemSearcher_V2,
    id: "ItemSearcher_V2",
    getInitialProperties: () => ({
        itemData: [{name:"Cate1",items:[{name:"ItemA"}]},{name:"Cate2",checkbox:true,items:[{name:"ItemB"}]}],
        setting: {initialCollapse:true,checkbox:true,showCount:true},
    }),
};

export default ItemSearcher_V2ElementRegistration;
