import { useState, useRef, useEffect } from '@wordpress/element';
import { AgGridReact } from 'ag-grid-react';
import 'ag-grid-community/styles/ag-grid.css';
import 'ag-grid-community/styles/ag-theme-alpine.css';

import LCPDataGridHeader from './LCPDataGridHeader';
import LCPGridColorRender from './LCPGridColorRender';

const LCPDataGrid = ({ dataset, index, updateDataset }) => {


    const onCellValueChanged = (event) => {
       // console.log('Cell value changed. Grid index:', index);
        
        // Access the grid API
        const gridApi = gridRef.current.api;

        // Fetch all the data in the grid after the change
        const allRowData = [];
        gridApi.forEachNode(node => allRowData.push(node.data));

        // Log the entire row data
        //console.log('All Grid Data:', allRowData);

        // Optionally, log the updated row data (just the changed row)
      //  console.log('Updated Row Data:', event.data);

        // Optionally, log the updated cell value
      //  console.log('Updated Cell Value:', event.newValue);

        // Create a fresh copy of the dataset and update its data
        const updatedDataset = { data: allRowData };
        console.log(updatedDataset.data);

        // Send the updated data back to the parent (LCPDatasetBuilder) through updateDataset
        updateDataset(index, updatedDataset);  // Call the parent function to update the dataset
    
    };


// lcpCellRenderer to dynamically assign the right cellRenderer
function lcpCellRenderer(params) {
    const value = params.value;

    // First, check if the value is a Date object
    if (value instanceof Date) {
        return value.toLocaleString(); // Return a formatted string (you can format it however you want)
    }

    // Check if the value is a valid CSS color (this works for string values like '#232323' or 'red')
    if (isValidCSSColor(value)) {
        return <LCPGridColorRender value={value} />; // Use the color renderer
    }

    // Default rendering for other types of data (like text, number, etc.)
    return value;
}
    const [rowsToAdd, setRowsToAdd] = useState(1); // Number of rows the user wants to add

    const [currentRowCount, setCurrentRowCount] = useState(dataset.length); // Track the current row count

    // Button click handler to add a new row
    const addRows = () => {
        if (gridApi) {
            const rows = [];
            // Create the specified number of new rows (empty rows in this case)
            for (let i = 0; i < rowsToAdd; i++) {
                rows.push({}); // You can customize the empty row content if needed
            }
            
            // Use the grid API to add the new rows
            gridApi.applyTransaction({ add: rows });

            // Update the current row count
            setCurrentRowCount(prevCount => prevCount + rowsToAdd);
        } else {
            console.log('Grid is not ready yet');
        }
    };


    const gridData = dataset;
    // Helper function to detect the data type of a value
    const getDataType = (value) => {
        if (typeof value === 'number' && !isNaN(value)) {
            return 'number';  // Identifies numerical values
        }
        return 'text'; // Defaults to 'text' for strings and others
    };


// Helper function to detect if a value is a valid CSS color
const isValidCSSColor = (value) => {
    // Ensure the value is a string before trimming
    value = String(value).trim();  // Convert to string if it's not already
    // If the value is a Date object, it should not be processed as a CSS color
    if (value instanceof Date) {
        return false;  // Return false if it's a Date object
    }

    // Regex to match valid CSS color formats (hex, rgb, rgba, hsl, hsla, named colors)
    const cssColorRegex = /^(#([0-9a-fA-F]{3}){1,2}|[a-zA-Z]+|rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)|rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(0(\.\d+)?|1(\.0+)?)\)|hsl\((\d{1,3}),\s*(\d{1,3})%,\s*(\d{1,3})%\)|hsla\((\d{1,3}),\s*(\d{1,3})%,\s*(\d{1,3})%,\s*(0(\.\d+)?|1(\.0+)?)\))$/i;

    // First check if the value matches the general color regex
    if (!cssColorRegex.test(value)) {
        return false; // If it doesn't match any known color format, it's not valid
    }

    // Now, we'll do more detailed checks for RGB, RGBA, HSL, and HSLA formats

    // Check if it's a valid RGB format (rgb(r, g, b))
    const rgbMatch = /^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/i.exec(value);
    if (rgbMatch) {
        const r = parseInt(rgbMatch[1], 10);
        const g = parseInt(rgbMatch[2], 10);
        const b = parseInt(rgbMatch[3], 10);
        // RGB values should be in the range 0-255
        return r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255;
    }

    // Check if it's a valid RGBA format (rgba(r, g, b, a))
    const rgbaMatch = /^rgba\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3}),\s*(0(\.\d+)?|1(\.0+)?)\)$/i.exec(value);
    if (rgbaMatch) {
        const r = parseInt(rgbaMatch[1], 10);
        const g = parseInt(rgbaMatch[2], 10);
        const b = parseInt(rgbaMatch[3], 10);
        const a = parseFloat(rgbaMatch[4]);
        // RGBA values should be in the range 0-255 for RGB and 0-1 for alpha
        return r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255 && a >= 0 && a <= 1;
    }

    // Check if it's a valid HSL format (hsl(h, s%, l%))
    const hslMatch = /^hsl\((\d{1,3}),\s*(\d{1,3})%,\s*(\d{1,3})%\)$/i.exec(value);
    if (hslMatch) {
        const h = parseInt(hslMatch[1], 10);
        const s = parseInt(hslMatch[2], 10);
        const l = parseInt(hslMatch[3], 10);
        // Hue should be 0-360, saturation and lightness should be 0-100
        return h >= 0 && h <= 360 && s >= 0 && s <= 100 && l >= 0 && l <= 100;
    }

    // Check if it's a valid HSLA format (hsla(h, s%, l%, a))
    const hslaMatch = /^hsla\((\d{1,3}),\s*(\d{1,3})%,\s*(\d{1,3})%,\s*(0(\.\d+)?|1(\.0+)?)\)$/i.exec(value);
    if (hslaMatch) {
        const h = parseInt(hslaMatch[1], 10);
        const s = parseInt(hslaMatch[2], 10);
        const l = parseInt(hslaMatch[3], 10);
        const a = parseFloat(hslaMatch[4]);
        // Hue should be 0-360, saturation and lightness should be 0-100, alpha should be 0-1
        return h >= 0 && h <= 360 && s >= 0 && s <= 100 && l >= 0 && l <= 100 && a >= 0 && a <= 1;
    }

    // If it's a valid named color (like "red", "blue", etc.)
    const namedColors = [
        "red", "green", "blue", "yellow", "black", "white", "gray", "purple", "orange", "brown", "pink", "cyan", "magenta",
        // Add more CSS named colors as necessary
    ];
    if (namedColors.includes(value.toLowerCase())) {
        return true;  // It's a valid named color
    }

    // If it's a valid hex color (hex format should be valid)
    const hexMatch = /^#([0-9a-fA-F]{3}){1,2}$/i.exec(value);
    if (hexMatch) {
        return true; // It's a valid hex color
    }

    // If it passed regex but didn't pass the detailed checks, it's invalid
    return false;
};




    // Helper function to detect if a value contains HTML tags
    const isHTML = (value) => {
        const htmlRegex = /<([a-z][\s\S]*)>/i;  // A simple check for any HTML tag
        return htmlRegex.test(value);
    };

    // Helper function to detect if a value is a valid date
    const isDate = (value) => {
        const date = new Date(value);
        return !isNaN(date.getTime());  // Return true if it's a valid date
    };

    // Detect the data type of each key by checking all rows
    const detectDataTypes = (data) => {
        const keys = Object.keys(data[0]);
        let dataTypes = {};

    keys.forEach((key) => {
        // Check all values for the key across all rows
        const allValuesAreNumbers = data.every(row => !isNaN(Number(row[key])));
        const allValuesAreColors = data.every(row => isValidCSSColor(row[key]));
        const allValuesAreHTML = data.every(row => isHTML(row[key]));
        const allValuesAreDates = data.every(row => isDate(row[key]));

        // If all values for that key are valid numbers, mark it as 'number'
        if (allValuesAreNumbers) {
            dataTypes[key] = 'number';
        }
        // If all values for that key are valid CSS colors, mark it as 'color'
        else if (allValuesAreColors) {
            dataTypes[key] = 'color';
        }
        // If all values for that key contain HTML, mark it as 'html'
        else if (allValuesAreHTML) {
            dataTypes[key] = 'html';
        }
        // If all values for that key are valid dates, mark it as 'date'
        else if (allValuesAreDates) {
            dataTypes[key] = 'date';
        }
        // If neither, mark it as 'text'
        else {
            dataTypes[key] = 'text';
        }
    });

    return dataTypes;
};

    // Use useEffect to update dataTypes whenever data changes
    useEffect(() => {
        if (data && data.length > 0) {
            const detectedDataTypes = detectDataTypes(data);
            setAttributes({ dataTypes: detectedDataTypes });
        }
    }, [data, setAttributes]);
    
    // Assuming gridData is already populated
    const gridDataTypes = detectDataTypes(gridData);  // This will detect the types of each field

    console.log(gridDataTypes); // For debugging, will output the detected types for each column

    // Helper function to convert index to letters, accounting for 'A1', 'B1', 'C1', etc.
    const getColumnLabel = (index) => {
        const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
        let label = '';

        while (index >= 0) {
            label = alphabet[index % 26] + label;
            index = Math.floor(index / 26) - 1;
        }

        return label;
    };

    function lcpDataTypeParser(params) {
        const columnName = params.colDef.field;  // Get the column field name (e.g., 'Budget', 'MeetAt')
        const dataType = gridDataTypes[columnName];  // Get the data type for this column from gridDataTypes
        
        if (params.node.rowPinned) {
            // If it's the pinned top row, return the value as-is
            return params.newValue;
        } else {
            // Check if the value is a Date object first
            let value = params.newValue;
    
            if (value instanceof Date) {
                // If it's a Date object, return the string representation (this could be customized if needed)
                return value.toLocaleString(); // or use `toISOString()` if you prefer
            }
    
            // Process based on the detected data type
            switch (dataType) {
                case 'number':
                    // If it's a number, return the number or NaN
                    return Number(value);
                case 'date':
                    const dateStr = value;
    
                    // Check if the value is an empty string or undefined
                    if (!dateStr || dateStr.trim() === '') {
                        return null; // If empty, return null or any default value you prefer
                    }
    
                    let parsedDate;
                    // Check if the date is in the format "MM/DD/YYYY"
                    const isValidMMDDYYYY = /^\d{2}\/\d{2}\/\d{4}$/.test(dateStr);
                    
                    if (isValidMMDDYYYY) {
                        const [month, day, year] = dateStr.split('/');
                        const isoDateStr = `${year}-${month}-${day}`;
                        parsedDate = new Date(isoDateStr);
                    } else {
                        // For other formats, try using the default Date parser
                        parsedDate = new Date(dateStr);
                    }
                    
                    // Return parsed date if valid, otherwise return the original string
                    return !isNaN(parsedDate.getTime()) ? parsedDate : dateStr;
                case 'color':
                    // If it's a color, return the value as-is
                    return value;
                case 'html':
                    // If it's HTML, we can either sanitize or leave the value as is (for now, leaving it as is)
                    return value;
                default:
                    // If it's text (or any other type), return the value as-is
                    return value;
            }
        }
    }
    
    
    
    

    // Create columnDefs dynamically
    const [columnDefs, setColumnDefs] = useState([]);
    const [pinnedTopRowData, setPinnedTopRowData] = useState([]);

    useEffect(() => {
        if (columnDefs.length > 0) return; // Prevent rerun if columnDefs already set

        if (gridData.length > 0) {
            const keys = Object.keys(gridData[0]); // Get the keys from the first row (Department2, Budget, etc.)

            const columns = keys.map((key, index) => {
                return {
                    colId: getColumnLabel(index),  // 'A', 'B', 'C', ...
                    headerName: getColumnLabel(index),  // 'A', 'B', 'C', ...
                    field: key, // Field will match the key (Department2, Budget, etc.)
                    valueParser: lcpDataTypeParser,
                    cellRenderer: lcpCellRenderer, // Reference the custom cell renderer
                    headerComponent: LCPDataGridHeader,
                    lcpDataType: 'myCustomDataType'  // Add your custom parameter here
                    
                };
            });

            // Set the pinned top row data with the actual field names (like 'Department2', 'Budget')
            const topRowData = keys.reduce((acc, key) => {
                acc[key] = key; // Set key as the value in top row (use field names)
                return acc;
            }, {});

            // Ensure pinnedTopRowData is set with correct data
            setPinnedTopRowData([topRowData]);

            // Add the row number column as the first column
            const rowNumberColumn = {
                headerName: '',
                valueGetter: (params) => {
                    const rowIndex = params.node.rowPinned ? params.node.rowIndex + 1 : params.node.rowIndex + 2;
                    return rowIndex;
                },
                suppressMovable: true,
                editable: false,
                filter: false,
                sortable: false,
                width: 45,
                resizable: false,
                flex: 0,
                cellStyle: { backgroundColor: '#e0e0e0' },
            };

            setColumnDefs([rowNumberColumn, ...columns]);  // Set columns along with row number column
        }
    }, [gridData, columnDefs]); // R

    const [rowData] = useState(gridData);

    // AG Grid instance reference
    const gridRef = useRef(null);
    const [gridApi, setGridApi] = useState(null); // Store grid API in state


    // Default Column Properties
    const defaultColDef = {
        flex: 1,
        minWidth: 45,
        editable: true,
        sortable: true,
        filter: true,
        suppressMovable: true,
        cellDataType: false, //By default column can be any data type 
        cellStyle: (params) => {
            if (params.node.rowPinned) {
                return { backgroundColor: 'rgb(197, 219, 229)' };
            }
        }
    };

    const gridOptions = {
        rowDragManaged: true,
        animateRows: true,
        rowHeight: 35,
        getRowStyle: (params) => {
            if (params.node.rowPinned) {
                return { backgroundColor: 'rgb(197, 219, 229)' }; // Light gray for pinned rows
            }
            return null; // No special style for non-pinned rows
        }
    };

    const onGridReady = (params) => {
        // Store the grid API when the grid is ready
        setGridApi(params.api);
    };

    // Reset row number on sorted
    const onSortChanged = (e) => {
        e.api.refreshCells();
    };

    // Reset row number on filtered
    const onFilterChanged = (e) => {
        e.api.refreshCells();
    };

    return (
        <div>
            <div
                id="lcp-data-grid"
                className="ag-theme-alpine"
                style={{ width: '100%', height: '300px' }}
            >
                <AgGridReact
                    ref={gridRef}
                    columnDefs={columnDefs}
                    rowData={rowData}
                    defaultColDef={defaultColDef}
                    gridOptions={gridOptions}
                    pinnedTopRowData={pinnedTopRowData}
                    pinnedLeftColCount={1}
                    onSortChanged={onSortChanged}
                    onFilterChanged={onFilterChanged}
                    onGridReady={onGridReady}
                    onCellValueChanged={onCellValueChanged}

                />
            </div>

            {/* Grid Footer */}
            <div style={{padding:'15px 0px',display:'flex', flexDirection:'row', justifyContent:'space-between', background:'white'}}>
                {/* Input to add multiple rows */}
                <div style={{display:'flex', flexDirection:'row'}}>
                    <input
                        type="number"
                        value={rowsToAdd}
                        onChange={(e) => setRowsToAdd(Math.max(1, parseInt(e.target.value, 10)))}
                        min="1"
                    />
                    <button onClick={addRows}>Add Row(s)</button>
                </div>
                <div>
                    <span>{currentRowCount} Rows</span>
                </div>
            </div>
        </div>
    );
};

export default LCPDataGrid;
