src_pages_DatabaseLookup.jsx

import { useState, useEffect, useCallback, useRef } from "react";
import * as d3 from "d3";

const backendURL = __APP_CONFIG__.backendURL;

/**
 * @module DatabaseLookup
 */

/**
 * A React component for searching and visualizing database entries.
 * This page cannot be opened as a tab (Handler does not exist in App.jsx)
 *
 * @component
 * @param {Object} [initialData={}] - Initial data to populate the search state.
 * @param {string} [initialData.query=""] - Initial search query.
 * @param {Object} [initialData.parameters] - Initial search parameters.
 * @param {Function} [onStateChange] - Callback function invoked when component state changes.
 * @param {Object} [savedState={}] - Saved state to restore component state.
 * @param {string} [savedState.query] - Saved search query.
 * @param {Object} [savedState.parameters] - Saved search parameters.
 * @param {Array} [savedState.results=[]] - Saved search results.
 * @param {boolean} [savedState.filter=false] - Saved filter visibility state.
 * @param {boolean} [savedState.graphView=false] - Saved graph view toggle state.
 * @returns {JSX.Element} The database lookup interface with search, filtering, and visualization capabilities.
 */
const DBLookup = ({ initialData = {}, onStateChange, savedState = {} }) => {

    /**
     * Enum for different types of database items.
     * @enum {string}
     * @readonly
     * @property {string} CORT - Cortical/Subcortical items.
     * @property {string} GM - Gray Matter items.
     * @property {string} FUNCTION - Function items.
     * @property {string} TEST - Test items.
     */
    const ItemTypes = Object.freeze({
        CORT: "Cortical/Subcortical",
        GM: "Gray Matter",
        FUNCTION: "Function",
        TEST: "Test",
    });

    // Search string
    const [query, setQuery] = useState(
        savedState.query || initialData.query || "",
    );

    // Filtering
    const [parameters, setParameters] = useState(
        savedState.parameters ||
            initialData.parameters || {
                hemisphere: [],
                lobe: [],
            },
    );

    // Search result
    const [searchResult, setSearchResult] = useState(savedState.results || []);
    const [isLoading, setIsLoading] = useState(false);
    const [error, setError] = useState(null);

    // Auto-populate suggestion thing
    const [suggestions, setSuggestions] = useState([]);
    const [activeSuggestions, setActiveSuggestions] = useState(false);

    // Filter option for lobe
    const [lobeOptions, setLobeOptions] = useState([]);

    const [showFilters, setShowFilters] = useState(savedState.filter || false);
    const debounceTimer = useRef(null);

    const graphRef = useRef(null);
    const [graphDimensions, setGraphDimensions] = useState({
        width: 800,
        height: 600,
    });
    const [graphView, setGraphView] = useState(savedState.graphView || false); // Toggle between table and graph view

    // Color scale for different node types
    const colorScale = d3
        .scaleOrdinal()
        .domain([
            ItemTypes.CORT,
            ItemTypes.GM,
            ItemTypes.FUNCTION,
            ItemTypes.TEST,
        ])
        .range(["#4e79a7", "#f28e2b", "#e15759", "#76b7b2"]);

    /**
     * Fetch every options for lobe from backend
     */
    useEffect(() => {
        // Fetch lobe options from backend
        const fetchLobeOptions = async () => {
            try {
                const res = await fetch(`${backendURL}/api/lobe-options`);
                if (!res.ok) throw new Error(`Error: ${res.status}`);
                const data = await res.json();
                setLobeOptions(data.lobes);
            } catch (err) {
                console.error("Failed to fetch lobe options:", err);
            }
        };

        fetchLobeOptions();
    }, []);

    useEffect(() => {
        if (onStateChange) {
            onStateChange({
                parameters,
                results: searchResult,
                filter: showFilters,
                query: query,
                graphView: graphView,
            });
        }
    }, [parameters, searchResult, showFilters, query, graphView]);

    /**
     * Call backend with given parameters to search.
     * Search result will be transformed and put into searchResult variable
     * @async
     */
    const performSearch = useCallback(async () => {
        setIsLoading(true);
        setError(null);

        try {
            const queryParams = {
                query,
                ...Object.fromEntries(
                    Object.entries(parameters).filter(
                        ([_, v]) => v !== "" && v.length !== 0,
                    ),
                ),
            };

            const res = await fetch(`${backendURL}/api/search`, {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify(queryParams),
            });

            if (!res.ok) throw new Error(`Error: ${res.status}`);
            const data = await res.json();

            // Transform the data for display
            const transformedResults = transformSearchResults(data);
            setSearchResult(transformedResults);
        } catch (err) {
            setError(err.message);
        } finally {
            setIsLoading(false);
        }
    }, [query, parameters]);

    /**
     * Call search when user press enter
     */
    const handleSearch = (e) => {
        e.preventDefault();
        performSearch();
    };

    /**
     * Call backend with given search string to get relevant items as suggestion
     * Backend and database takes care of ordering
     * @async
     */
    const fetchSuggestions = async (input) => {
        if (!input) return;
        try {
            const res = await fetch(`${backendURL}/api/suggest`, {
                method: "POST",
                headers: { "Content-Type": "application/json" },
                body: JSON.stringify({ query: input }),
            });
            if (!res.ok) throw new Error(`Error: ${res.status}`);
            const data = await res.json();
            setSuggestions(data.suggestions);
        } catch (err) {
            console.error("Failed to fetch suggestions:", err);
        }
    };

    /**
     * Transforms raw results into a structured format for display.
     *
     * @param {Object} data - The raw search results from the backend.
     * @param {Array} [data.cort] - Cortical/Subcortical items.
     * @param {Array} [data.gm] - Gray Matter items.
     * @param {Array} [data.functions] - Function items.
     * @param {Array} [data.tests] - Test items.
     * @returns {Array} Transformed results with relevance scoring and relationships.
     */
    const transformSearchResults = (data) => {
        let results = [];
        const searchString = query.toLowerCase();

        // Helper function to check if text contains the search string
        const containsString = (text) => {
            if (!text) return false;
            return text.toString().toLowerCase().includes(searchString);
        };

        // Helper function to determine relevance level
        const getRelevanceLevel = (item, type) => {
            // Level 1: Name matches
            if (containsString(item.name || item.title)) return 1;

            // Level 2: Acronym or description matches
            if (
                containsString(item.acronym) ||
                containsString(item.description) ||
                containsString(item.lobe)
            )
                return 2;

            // Default to level 4 (will be upgraded if direct relationships found)
            return 4;
        };

        // Helper function to add item if it doesn't exist
        const addItem = (item, type, relevance = null) => {
            if (!item || !item.id) return;

            const existsIndex = results.findIndex(
                (r) => r.type === type && r.id === item.id,
            );

            const relevanceLevel =
                relevance !== null ? relevance : getRelevanceLevel(item, type);

            // Take care of related items
            if (existsIndex === -1) {
                let relatedItems = [];
                switch (type) {
                    case ItemTypes.CORT:
                        item.name = `${item.hemisphere === "l" ? "Left" : "Right"} ${item.name}`;
                        for (let relatedGM in item.cort_gm) {

                            // Add GM as related only if they are not duplicate
                            if (!item.cort_gm[relatedGM].gm.duplicate) {
                                relatedItems.push({
                                    id: item.cort_gm[relatedGM].gm.id,
                                    type: ItemTypes.GM,
                                    name: item.cort_gm[relatedGM].gm.name,
                                    reference: item.cort_gm[relatedGM].reference,
                                });
                            }
                        }
                        break;
                    case ItemTypes.GM:
                        for (let relatedCort in item.cort_gm) {
                            relatedItems.push({
                                id: item.cort_gm[relatedCort].cort.id,
                                type: ItemTypes.CORT,
                                name: item.cort_gm[relatedCort].cort.name,
                                reference: item.cort_gm[relatedCort].reference,
                            });
                        }
                        for (let relatedFunc in item.gm_function) {
                            relatedItems.push({
                                id: item.gm_function[relatedFunc].function.id,
                                type: ItemTypes.FUNCTION,
                                name: item.gm_function[relatedFunc].function
                                    .name,
                                reference:
                                    item.gm_function[relatedFunc].reference,
                            });
                        }
                        break;
                    case ItemTypes.FUNCTION:
                        for (let relatedGM in item.gm_function) {

                            // Add GM as related only if they are not duplicate
                            if (!item.gm_function[relatedGM].gm.duplicate) {
                                relatedItems.push({
                                    id: item.gm_function[relatedGM].gm.id,
                                    type: ItemTypes.GM,
                                    name: item.gm_function[relatedGM].gm.name,
                                    reference:
                                        item.gm_function[relatedGM].reference,
                                });

                            }
                        }
                        for (let relatedTest in item.function_test) {
                            relatedItems.push({
                                id: item.function_test[relatedTest].test.id,
                                type: ItemTypes.TEST,
                                name: item.function_test[relatedTest].test.name,
                                reference:
                                    item.function_test[relatedTest].reference,
                            });
                        }
                        break;
                    case ItemTypes.TEST:
                        for (let relatedFunc in item.function_test) {
                            relatedItems.push({
                                id: item.function_test[relatedFunc].function.id,
                                type: ItemTypes.FUNCTION,
                                name: item.function_test[relatedFunc].function
                                    .name,
                                reference:
                                    item.function_test[relatedFunc].reference,
                            });
                        }
                        break;
                }

                results.push({
                    type: type,
                    id: item.id,
                    name: item.name,
                    details: getItemDetails(item, type),
                    related: relatedItems,
                    relevance: relevanceLevel,
                    duplicate: item.duplicate === true,
                });
            } else if (relevanceLevel < results[existsIndex].relevance) {
                // Update with better relevance if found
                results[existsIndex].relevance = relevanceLevel;
            }
        };

        // Helper to get display details for each item type
        const getItemDetails = (item, type) => {
            switch (type) {
                case ItemTypes.CORT:
                    const side = item.hemisphere === "l" ? "left" : "right";
                    return {
                        hemisphere: side,
                        lobe: item.lobe,
                        electrode_label: item.electrode_label,
                        acronym: item.acronym,
                    };
                case ItemTypes.GM:
                    return { acronym: item.acronym };
                case ItemTypes.FUNCTION:
                    return { description: item.description };
                case ItemTypes.TEST:
                    return { description: item.description };
                default:
                    return {};
            }
        };

        // add all items with their direct relevance
        data.cort?.forEach((cort) => addItem(cort, ItemTypes.CORT));
        data.gm?.forEach((gm) => addItem(gm, ItemTypes.GM));
        data.functions?.forEach((func) => addItem(func, ItemTypes.FUNCTION));
        data.tests?.forEach((test) => addItem(test, ItemTypes.TEST));

        // resolve duplicates in gm
        results.forEach((entry) => {
            if (entry.duplicate) {
                entry.related.forEach((relative) => {
                    let original = results.find(r => (r.id === relative.id) && (r.type === relative.type));
                    if (original) {
                        original.related = original.related.concat(entry.related.filter(r => (r.id !== relative.id) && (r.type !== relative.type)))
                    }
                })
            }
        })
        results = results.filter(r => r.duplicate !== true)

        // find direct relationships (level 3)
        const directlyRelated = new Set();
        results.forEach((item) => {
            if (item.relevance <= 2) {
                // If item is level 1 or 2
                item.related?.forEach((rel) => {
                    directlyRelated.add(`${rel.type}-${rel.id}`);
                });
            }
        });

        // Update relevance for directly related items
        results.forEach((item) => {
            const itemKey = `${item.type}-${item.id}`;
            if (directlyRelated.has(itemKey) && item.relevance > 3) {
                item.relevance = 3;
            }
        });

        // Sort results by relevance (lower numbers first)
        results.sort((a, b) => a.relevance - b.relevance);

        return results;
    };

    /**
     * Clear out all parameters
     */
    const handleReset = () => {
        setQuery("");
        setParameters({ hemisphere: [], lobe: [] });
        setSearchResult([]);
        setSuggestions([]);
        setError(null);
    };

    /**
     * Search again when hemisphere filter changes
     */
    const handleHemisphereChange = (side) => {
        setParameters((prev) => {
            const newHemisphere = prev.hemisphere.includes(side)
                ? prev.hemisphere.filter((h) => h !== side)
                : [...prev.hemisphere, side];
            const newParams = { ...prev, hemisphere: newHemisphere };

            if (searchResult.length > 0 || query) {
                if (searchResult.length > 0 || query) {
                    clearTimeout(debounceTimer.current);
                    debounceTimer.current = setTimeout(() => {
                        performSearch();
                    }, 300); // 300ms delay
                }
            }
            return newParams;
        });
    };

    /**
     * Search again when lobe filter changes
     */
    const handleLobeChange = (lobe) => {
        setParameters((prev) => {
            const newLobe = prev.lobe.includes(lobe)
                ? prev.lobe.filter((l) => l !== lobe)
                : [...prev.lobe, lobe];
            const newParams = { ...prev, lobe: newLobe };

            if (searchResult.length > 0 || query) {
                if (searchResult.length > 0 || query) {
                    clearTimeout(debounceTimer.current);
                    debounceTimer.current = setTimeout(() => {
                        performSearch();
                    }, 300); // 300ms delay
                }
            }
            return newParams;
        });
    };

    useEffect(() => {
        if (
            (searchResult.length > 0 || query) &&
            Object.keys(parameters).length > 0
        ) {
            if (searchResult.length > 0 || query) {
                clearTimeout(debounceTimer.current);
                debounceTimer.current = setTimeout(() => {
                    performSearch();
                }, 300); // 300ms delay
            }
        }
    }, [parameters.hemisphere, parameters.lobe]);

    // Add this useEffect to handle window resize
    useEffect(() => {
        const handleResize = () => {
            if (graphRef.current) {
                const width = graphRef.current.clientWidth;
                const height = Math.min(600, window.innerHeight - 200);
                setGraphDimensions({ width, height });
            }
        };

        window.addEventListener("resize", handleResize);
        handleResize(); // Initial call

        return () => window.removeEventListener("resize", handleResize);
    }, []);

    useEffect(() => {
        if (graphView && searchResult.length > 0 && graphRef.current) {
            const cleanup = renderGraph();
            return () => {
                // Clean up when switching views or unmounting
                if (graphRef.current) {
                    d3.select(graphRef.current).selectAll("*").remove();
                }
                if (cleanup) cleanup();
            };
        } else {
            // Ensure clean removal when not in graph view
            if (graphRef.current) {
                d3.select(graphRef.current).selectAll("*").remove();
            }
        }
    }, [searchResult, graphView, graphDimensions]);

    /**
     * Renders a graph visualization of search results using D3.js.
     *
     * @returns {Function} A cleanup function to remove the graph when unmounting.
     */
    const renderGraph = () => {
        // Helper function to wrap long labels
        function wrap(text, width) {
            text.each(function () {
                var text = d3.select(this),
                    words = text.text().split(/\s+/),
                    word,
                    line = [],
                    lineHeight = 1.2, // ems
                    dy = -2 * lineHeight,
                    tspan = text
                        .text(null)
                        .append("tspan")
                        .attr("text-anchor", "middle")
                        .attr("dy", dy + "em");
                while ((word = words.pop())) {
                    line = [word, ...line];
                    tspan.text(line.join(" "));
                    if (tspan.node().getComputedTextLength() > width) {
                        line.shift();
                        tspan.text(line.join(" "));
                        line = [word];
                        tspan = text
                            .append("tspan")
                            .attr("text-anchor", "middle")
                            .attr("dy", dy + lineHeight + "em")
                            .attr("dx", -tspan.node().getComputedTextLength())
                            .text(word);
                    }
                }
            });
        }

        // Clear previous graph completely
        const container = d3.select(graphRef.current);
        container.selectAll("*").remove();

        // Prepare nodes and links data
        const nodes = [];
        const links = [];
        const nodeMap = new Map();

        // Create nodes from search results
        searchResult.forEach((item, index) => {
            const nodeId = `${item.type}-${item.id}`;
            nodeMap.set(nodeId, index);
            nodes.push({
                id: nodeId,
                name: item.name,
                type: item.type,
                details: item.details,
                // Store the original item for relationship lookup
                originalItem: item,
            });
        });

        // Create links from related items - FIXED LINK CREATION
        nodes.forEach((sourceNode, sourceIndex) => {
            sourceNode.originalItem.related?.forEach((rel) => {
                // Create target ID in the same format as node IDs
                const targetId = `${rel.type}-${rel.id}`;
                const targetIndex = nodeMap.get(targetId);

                // Only create link if target exists and it's not linking to itself
                if (targetIndex !== undefined && targetIndex !== sourceIndex) {
                    links.push({
                        source: sourceIndex,
                        target: targetIndex,
                        value: 1,
                    });
                }
            });
        });

        // Create the force simulation with proper node references
        const simulation = d3
            .forceSimulation(nodes)
            .force(
                "link",
                d3
                    .forceLink(links)
                    .id((d) => nodes.indexOf(d)) // Use node index as ID
                    .distance(130),
            )
            .force("charge", d3.forceManyBody().strength(-20))
            .force(
                "center",
                d3.forceCenter(
                    graphDimensions.width / 2,
                    graphDimensions.height / 2,
                ),
            )
            .force("collision", d3.forceCollide().radius(40));

        // Create SVG container with Tailwind classes
        const svg = container
            .append("svg")
            .attr("class", "w-full h-full")
            .attr(
                "viewBox",
                `0 0 ${graphDimensions.width} ${graphDimensions.height}`,
            )
            .attr("preserveAspectRatio", "xMidYMid meet");

        // Add zoom behavior
        const zoom = d3
            .zoom()
            .scaleExtent([0.5, 3])
            .on("zoom", (event) => {
                g.attr("transform", event.transform);
            });

        svg.call(zoom);

        const g = svg.append("g");

        // Draw links
        const link = g
            .append("g")
            .selectAll("line")
            .data(links)
            .join("line")
            .attr("class", "stroke-gray-400 stroke-opacity-60 stroke-2")
            .attr("stroke", "#999") // Explicit color as fallback
            .attr("stroke-width", 2);

        // Create a size scale based on relevance
        const sizeScale = d3
            .scaleLinear()
            .domain([1, 4]) // Relevance levels from 1 (highest) to 4 (lowest)
            .range([20, 10]); // Size range from 30px (largest) to 15px (smallest)

        // Draw nodes
        const node = g
            .append("g")
            .selectAll("circle")
            .data(nodes)
            .join("circle")
            .attr("r", (d) => sizeScale(d.originalItem.relevance))
            .attr("fill", (d) => colorScale(d.type))
            .attr("class", "stroke-white stroke-2")
            .call(drag(simulation));

        // Add labels
        const label = g
            .append("g")
            .selectAll("text")
            .data(nodes)
            .join("text")
            .attr(
                "class",
                (d) =>
                    `text-xs text-[${12 - d.originalItem.relevance}px] fill-gray-800 max-w-24`,
            )
            .text((d) => d.name)
            .call(wrap, 120);

        // Add node type indicators
        const typeLabel = g
            .append("g")
            .selectAll("text")
            .data(nodes)
            .join("text")
            .attr("dy", (d) => 30 - d.originalItem.relevance * 3)
            .attr("text-anchor", "middle")
            .attr("class", "text-[8px] fill-gray-600")
            .text((d) => d.type.split("/")[0]);

        // Tooltip with Tailwind classes
        const tooltip = d3
            .select("body")
            .append("div")
            .attr(
                "class",
                "fixed invisible bg-white border border-gray-200 rounded p-2 pointer-events-none z-50 max-w-[300px] shadow-md",
            );

        // Add interactivity
        node.on("mouseover", (event, d) => {
            tooltip
                .html(
                    `
                    <strong class="font-semibold">${d.name}</strong><br>
                    <em class="text-gray-600">${d.type}</em><br>
                    ${formatDetails(d.details)}
                `,
                )
                .classed("invisible", false)
                .classed("visible", true);
        })
            .on("mousemove", (event) => {
                tooltip
                    .style("top", `${event.pageY - document.body.scrollTop}px`)
                    .style("left", `${event.pageX}px`);
            })
            .on("mouseout", () => {
                tooltip.classed("invisible", true).classed("visible", false);
            });

        // Update positions on each tick
        simulation.on("tick", () => {
            link.attr("x1", (d) => d.source.x)
                .attr("y1", (d) => d.source.y)
                .attr("x2", (d) => d.target.x)
                .attr("y2", (d) => d.target.y);

            node.attr("cx", (d) => d.x).attr("cy", (d) => d.y);

            label.attr("x", (d) => d.x).attr("y", (d) => d.y);

            typeLabel.attr("x", (d) => d.x).attr("y", (d) => d.y);
        });

        // Drag functions
        function drag(simulation) {
            function dragstarted(event, d) {
                if (!event.active) simulation.alphaTarget(0.3).restart();
                d.fx = d.x;
                d.fy = d.y;
            }

            function dragged(event, d) {
                d.fx = event.x;
                d.fy = event.y;
            }

            function dragended(event, d) {
                if (!event.active) simulation.alphaTarget(0);
                d.fx = null;
                d.fy = null;
            }

            return d3
                .drag()
                .on("start", dragstarted)
                .on("drag", dragged)
                .on("end", dragended);
        }

        // Helper to format details for tooltip
        function formatDetails(details) {
            if (!details) return "";
            return Object.entries(details)
                .map(
                    ([key, value]) =>
                        `<span class="text-gray-500">${key}:</span> ${value}`,
                )
                .join("<br>");
        }

        // Return cleanup function
        return () => {
            simulation.stop();
            tooltip.remove();
        };
    };

    /**
     * @returns {JSX.Element} Table / Graph for the search result
     */
    const renderResultsTable = () => {
        if (searchResult.length === 0) {
            return (
                <p>No results to display. Perform a search to see results.</p>
            );
        }

        return (
            <div className="space-y-4">
                <div className="flex justify-between items-center">
                    <h3 className="text-lg font-semibold">
                        Results ({searchResult.length})
                    </h3>
                    <button
                        onClick={() => {
                            setGraphView(!graphView);
                            // Force a re-render by resetting the graph dimensions
                            const container = d3.select(graphRef.current);
                            container.selectAll("*").remove();
                        }}
                        className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition-colors"
                    >
                        {graphView ? "Show Table" : "Show Graph"}
                    </button>
                </div>

                {graphView ? (
                    <div
                        ref={graphRef}
                        className="border rounded bg-white w-full h-[600px]"
                    />
                ) : (
                    <div className="overflow-auto border rounded">
                        <table className="min-w-full divide-y divide-gray-200 text-sm">
                            <thead className="bg-gray-100">
                                <tr>
                                    <th className="px-4 py-2 text-left font-medium text-gray-700">
                                        Type
                                    </th>
                                    <th className="px-4 py-2 text-left font-medium text-gray-700">
                                        Name
                                    </th>
                                    <th className="px-4 py-2 text-left font-medium text-gray-700">
                                        Details
                                    </th>
                                    <th className="px-4 py-2 text-left font-medium text-gray-700 w-1/2">
                                        Related Results
                                    </th>
                                </tr>
                            </thead>
                            <tbody className="bg-white divide-y divide-gray-200">
                                {searchResult.map((item) => (
                                    <tr
                                        key={`${item.type}-${item.id}`}
                                        className="hover:bg-gray-50"
                                    >
                                        <td className="px-4 py-2 capitalize">
                                            {item.type}
                                        </td>
                                        <td className="px-4 py-2 font-medium capitalize">
                                            {item.name}
                                        </td>
                                        <td className="px-4 py-2">
                                            {item.type === ItemTypes.CORT && (
                                                <div className="space-y-1">
                                                    <div>
                                                        <span className="text-gray-500">
                                                            Hemisphere:
                                                        </span>{" "}
                                                        {
                                                            item.details
                                                                .hemisphere
                                                        }
                                                    </div>
                                                    <div>
                                                        <span className="text-gray-500">
                                                            Lobe:
                                                        </span>{" "}
                                                        {item.details.lobe}
                                                    </div>
                                                    <div>
                                                        <span className="text-gray-500">
                                                            Electrode:
                                                        </span>{" "}
                                                        {
                                                            item.details
                                                                .electrode_label
                                                        }
                                                    </div>
                                                    <div>
                                                        <span className="text-gray-500">
                                                            Acronym:
                                                        </span>{" "}
                                                        {item.details.acronym}
                                                    </div>
                                                </div>
                                            )}
                                            {item.type === ItemTypes.GM && (
                                                <div>
                                                    <span className="text-gray-500">
                                                        Acronym:
                                                    </span>{" "}
                                                    {item.details.acronym}
                                                </div>
                                            )}
                                            {item.type ===
                                                ItemTypes.FUNCTION && (
                                                <div>
                                                    <span className="text-gray-500">
                                                        Description:
                                                    </span>{" "}
                                                    {item.details.description}
                                                </div>
                                            )}
                                            {item.type === ItemTypes.TEST && (
                                                <div>
                                                    <span className="text-gray-500">
                                                        Description:
                                                    </span>{" "}
                                                    {item.details.description}
                                                </div>
                                            )}
                                        </td>
                                        <td className="px-4 py-2">
                                            {item.related?.length > 0 ? (
                                                <div className="space-y-1">
                                                    {item.related.map(
                                                        (rel, i) => (
                                                            <div
                                                                key={i}
                                                                className="text-xs bg-gray-100 rounded px-2 py-1"
                                                            >
                                                                {rel.reference ? (
                                                                    <details className="flex cursor-pointer">
                                                                        <summary>
                                                                            <span className="font-medium capitalize">
                                                                                {
                                                                                    rel.type
                                                                                }
                                                                                :
                                                                            </span>{" "}
                                                                            <span className="capitalize">
                                                                                {
                                                                                    rel.name
                                                                                }
                                                                            </span>
                                                                        </summary>
                                                                        <p className="pl-5">
                                                                            {`${rel.reference.authors}. (${rel.reference.publication_date}). ${rel.reference.title}. `}
                                                                            <i>{`${rel.reference.publisher}.`}</i>
                                                                            {` doi:${rel.reference.isbn_issn_doi}`}
                                                                        </p>
                                                                    </details>
                                                                ) : (
                                                                    <div>
                                                                        <span className="font-medium capitalize">
                                                                            {
                                                                                rel.type
                                                                            }
                                                                            :
                                                                        </span>{" "}
                                                                        <span className="capitalize">
                                                                            {
                                                                                rel.name
                                                                            }
                                                                        </span>
                                                                    </div>
                                                                )}
                                                            </div>
                                                        ),
                                                    )}
                                                </div>
                                            ) : (
                                                <span className="text-gray-400">
                                                    None
                                                </span>
                                            )}
                                        </td>
                                    </tr>
                                ))}
                            </tbody>
                        </table>
                    </div>
                )}
            </div>
        );
    };

    return (
        <div className="flex flex-col mx-10 p-4 space-y-4 relative">
            <h2 className="text-2xl font-semibold">Database Lookup</h2>

            {/* Search Bar */}
            <form onSubmit={handleSearch} onReset={handleReset}>
                <div className="flex gap-2 w-full mb-4 relative">
                    <button
                        type="button"
                        className="px-4 py-2 border border-gray-400 rounded hover:bg-gray-100"
                        onClick={() => setShowFilters(!showFilters)}
                    >
                        {showFilters ? "Hide Filters" : "Show Filters"}
                    </button>
                    <input
                        type="text"
                        className="flex-grow p-2 border rounded-l-lg border-gray-300 focus:outline-none focus:ring-2 focus:ring-blue-500"
                        placeholder="Search database..."
                        value={query}
                        onChange={(e) => {
                            setQuery(e.target.value);
                            fetchSuggestions(e.target.value);
                            setActiveSuggestions(true);
                        }}
                        onFocus={() => setActiveSuggestions(true)}
                        onBlur={() =>
                            setTimeout(() => setActiveSuggestions(false), 200)
                        }
                    />
                    <button
                        type="submit"
                        className="px-4 py-2 bg-blue-600 text-white rounded-r-lg hover:bg-blue-700 disabled:opacity-50"
                        disabled={isLoading}
                    >
                        {isLoading ? "Searching..." : "Search"}
                    </button>
                    <button
                        type="reset"
                        className="px-4 py-2 border border-gray-400 rounded hover:bg-gray-100"
                    >
                        Reset
                    </button>
                    {isLoading && (
                        <div className="absolute right-0 -bottom-6 text-sm text-gray-500">
                            Loading...
                        </div>
                    )}
                </div>

                {/* Suggestions Dropdown */}
                {activeSuggestions && suggestions && suggestions.length > 0 && (
                    <ul className="bg-white border border-gray-300 rounded shadow max-h-60 overflow-y-auto absolute z-10 w-full">
                        {suggestions.map((sug, i) => (
                            <li
                                key={i}
                                className="p-2 hover:bg-gray-100 cursor-pointer"
                                onClick={() => {
                                    setQuery(sug);
                                    setActiveSuggestions(false);
                                }}
                            >
                                {sug}
                            </li>
                        ))}
                    </ul>
                )}
            </form>

            <div className="flex gap-4">
                {showFilters && (
                    <div className="w-1/6 space-y-4">
                        <h3 className="text-lg font-semibold">Filters</h3>

                        {/* Hemisphere Checkboxes */}
                        <div>
                            <label className="block text-sm font-medium mb-1">
                                Hemisphere
                            </label>
                            <div className="flex gap-2">
                                {["left", "right"].map((side) => (
                                    <label
                                        key={side}
                                        className="inline-flex items-center gap-2"
                                    >
                                        <input
                                            type="checkbox"
                                            checked={parameters.hemisphere.includes(
                                                side,
                                            )}
                                            onChange={() =>
                                                handleHemisphereChange(side)
                                            }
                                            className="rounded border-gray-300 text-blue-600
                                                       cursor-pointer focus:ring-blue-500"
                                        />
                                        {side.charAt(0).toUpperCase() +
                                            side.slice(1)}
                                    </label>
                                ))}
                            </div>
                        </div>

                        {/* Lobe Checkboxes */}
                        <div>
                            <label className="block text-sm font-medium mb-1">
                                Lobe
                            </label>
                            <div className="space-y-2">
                                {lobeOptions.map((lobe) => (
                                    <label
                                        key={lobe}
                                        className="flex items-center gap-2"
                                    >
                                        <input
                                            type="checkbox"
                                            checked={parameters.lobe.includes(
                                                lobe,
                                            )}
                                            onChange={() =>
                                                handleLobeChange(lobe)
                                            }
                                            className="rounded border-gray-300 text-blue-600
                                                       cursor-pointer focus:ring-blue-500"
                                        />
                                        {lobe}
                                    </label>
                                ))}
                            </div>
                        </div>
                    </div>
                )}

                {/* Results */}
                <div className={showFilters ? "w-3/4" : "w-full"}>
                    {error && <div className="text-red-500 mb-2">{error}</div>}
                    {renderResultsTable()}
                </div>
            </div>
        </div>
    );
};

export default DBLookup;