import React, { useState, useEffect } from 'react';
import {
    Table,
    TableBody,
    TableCell,
    TableContainer,
    TableHead,
    TableRow,
    Paper,
    Tooltip,
    TextField,
    Typography,
    Button,
    Box,
    Dialog,
    DialogActions,
    DialogContent,
    DialogContentText,
    DialogTitle
} from '@mui/material';
import { SpreadsheetService, ShipmentsService, DewarsService, ApiError } from '../../openapi';
import * as ExcelJS from 'exceljs';
import { saveAs } from 'file-saver';

const SpreadsheetTable = ({
                              raw_data,
                              errors,
                              headers,
                              setRawData,
                              onCancel,
                              fileBlob,
                              selectedShipment,
                              addinfo,
                              activePgroup,
                          }) => {
    const [localErrors, setLocalErrors] = useState(errors || []);
    const [editingCell, setEditingCell] = useState({});
    const [nonEditableCells, setNonEditableCells] = useState(new Set());
    const [isSubmitting, setIsSubmitting] = useState(false);
    const [showUpdateDialog, setShowUpdateDialog] = useState(false);
    const [dewarsToReplace, setDewarsToReplace] = useState([]);
    const [dewarsToCreate, setDewarsToCreate] = useState(new Map());
    const [correctionMetadata, setCorrectionMetadata] = useState(addinfo || []); // Store addinfo
    const [errorDialogOpen, setErrorDialogOpen] = useState(false); // Controls dialog visibility
    const [errorMessages, setErrorMessages] = useState<string[]>([]); // Error messages from the server

    const enhancedRawData = raw_data.map((row) => {
        const metadata = correctionMetadata.find((info) => info.row_num === row.row_num) || {};

        // Combine original row data with metadata
        return {
            ...row,
            corrected_columns: metadata.corrected_columns || [], // Columns corrected
            default_set_columns: metadata.default_set || [],     // Specific columns default-assigned
        };
    });

    useEffect(() => {
        console.log("Correction Metadata:", correctionMetadata);
        console.log("Addinfo:", addinfo);
    }, [correctionMetadata, addinfo]);
    const initialNewDewarState = {
        pgroups: activePgroup,
        number_of_pucks: 0,
        number_of_samples: 0,
        contact_id: selectedShipment?.contact?.id,
        return_address_id: selectedShipment?.return_address?.id,
        dewar_name: '',
        tracking_number: 'UNKNOWN',
        status: 'active',
        pucks: [] // Ensure 'pucks' array exists
    };

    const [newDewar, setNewDewar] = useState(initialNewDewarState);

    useEffect(() => {
        setNewDewar((prev) => ({
            ...prev,
            contact_id: selectedShipment?.contact?.id,
            return_address_id: selectedShipment?.return_address?.id
        }));
    }, [selectedShipment]);

    const generateErrorMap = (errorsList) => {
         const errorMap = new Map();
         if (Array.isArray(errorsList)) {
             errorsList.forEach((error) => {
                 const colIndex = headers.findIndex(header => header === error.column);
                 if (colIndex > -1) {
                     const key = `${error.row}-${headers[colIndex]}`;
                     errorMap.set(key, error.message);
                 }
             });
         }
         return errorMap;
     };

    const errorMap = generateErrorMap(localErrors);

    useEffect(() => {
        const updatedNonEditableCells = new Set();
        raw_data.forEach((row, rowIndex) => {
            headers.forEach((_, colIndex) => {
                const key = `${row.row_num}-${headers[colIndex]}`;
                if (!errorMap.has(key)) {
                    updatedNonEditableCells.add(`${rowIndex}-${colIndex}`);
                }
            });
        });
        setNonEditableCells(updatedNonEditableCells);
        console.log("Recalculated nonEditableCells:", updatedNonEditableCells);
    }, [raw_data, headers, errorMap]);

    const handleCellEdit = async (rowIndex, colIndex) => {
        const updatedRawData = [...raw_data];
        const columnName = headers[colIndex];
        const currentRow = updatedRawData[rowIndex];
        const newValue = editingCell[`${rowIndex}-${colIndex}`];

        if (newValue === undefined) return; // Ensure value is provided

        // Prepare for validation request
        if (!currentRow.data) {
            currentRow.data = [];
        }
        currentRow.data[colIndex] = newValue;

        // Reset editing state
        setEditingCell((prev) => {
            const updated = { ...prev };
            delete updated[`${rowIndex}-${colIndex}`];
            return updated;
        });

        try {
            const response = await SpreadsheetService.validateCellValidateCellPost({
                row: currentRow.row_num,
                column: columnName,
                value: newValue,
            });

            if (response && response.is_valid !== undefined) {
                if (response.is_valid) {
                    const correctedValue = response.corrected_value ?? newValue;
                    currentRow.data[colIndex] = correctedValue;
                    updatedRawData[rowIndex] = currentRow;

                    setRawData(updatedRawData);

                    // Remove the error and mark as non-editable
                    const updatedErrors = localErrors.filter(
                        (error) =>
                            !(
                                error.row === currentRow.row_num &&
                                error.cell === colIndex &&
                                error.message.toLowerCase().includes("duplicate position")
                            )
                    );
                    setLocalErrors(updatedErrors); // Update error list

                    setNonEditableCells((prev) => new Set([...prev, `${rowIndex}-${colIndex}`]));
                } else {
                    // If not valid, don't add to nonEditableCells and update the error list
                    const errorMessage = response.message || "Invalid value.";
                    const newError = {
                        row: currentRow.row_num,
                        cell: colIndex,
                        message: errorMessage,
                    };

                    const updatedErrors = [
                        ...localErrors.filter(
                            (error) => !(error.row === newError.row && error.cell === newError.cell)
                        ),
                        newError,
                    ];
                    setLocalErrors(updatedErrors);

                    setNonEditableCells((prev) => {
                        const updatedSet = new Set(prev);
                        updatedSet.delete(`${rowIndex}-${colIndex}`); // Ensure it stays editable
                        return updatedSet;
                    });
                }
            } else {
                console.error("Unexpected response from backend:", response);
            }
        } catch (error) {
            console.error("Validation request failed:", error);
        }
    };

    const handleCellBlur = (rowIndex, colIndex) => {
        handleCellEdit(rowIndex, colIndex);
    };

    const allCellsValid = () =>
        nonEditableCells.size === raw_data.length * headers.length &&
        !localErrors.some((error) =>
            error.message.toLowerCase().includes("duplicate position")
        );

    const fieldToCol = {
        'dewarname': 0,
        'puckname': 1,
        'pucktype': 2,
        'crystalname': 3,
        'positioninpuck': 4,
        'priority': 5,
        'comments': 6,
        'directory': 7,
        'proteinname': 8,
        'oscillation': 9,
        'aperture': 10,
        'exposure': 11,
        'totalrange': 12,
        'transmission': 13,
        'dose': 14,
        'targetresolution': 15,
        'datacollectiontype': 16,
        'processingpipeline': 17,
        'spacegroupnumber': 18,
        'cellparameters': 19,
        'rescutkey': 20,
        'rescutvalue': 21,
        'userresolution': 22,
        'pdbid': 23,
        'autoprocfull': 24,
        'procfull': 25,
        'adpenabled': 26,
        'noano': 27,
        'ffcscampaign': 28,
        'trustedhigh': 29,
        'autoprocextraparams': 30,
        'chiphiangles': 31
    };

    const checkIfDewarExists = async (dewarName: string) => {
        if (!selectedShipment) return null;

        try {
            // Fetch dewars related to the current shipment via API
            const shipDewars = await ShipmentsService.getDewarsByShipmentIdProtectedShipmentsShipmentIdDewarsGet(selectedShipment.id);

            // Search for dewar by name within the shipment
            const existingDewar = shipDewars.find((d) => d.dewar_name === dewarName);

            if (existingDewar) {
                console.log(`Dewar "${dewarName}" exists with ID: ${existingDewar.id}`);
                return existingDewar;
            }
            return null;
        } catch (error) {
            console.error("Failed to fetch existing dewars:", error);
            return null;
        }
    };

    const createOrUpdateDewarsFromSheet = async (data, contact, address) => {
        if (!contact?.id || !address?.id) {
            console.error('contact_id or return_address_id is missing');
            return null;
        }

        const dewars = new Map();
        const puckPositionMap = new Map();
        const dewarsToReplace = [];

        for (let rowIndex = 0; rowIndex < data.length; rowIndex++) {
            const row = data[rowIndex];

            if (!row.data) {
                console.error(`Row ${rowIndex}: Missing or invalid data`);
                continue;
            }

            const dewarName = sanitizeAndValidateColumn(row, fieldToCol['dewarname'], 'string', 'Dewar Name');
            const puckName = sanitizeAndValidateColumn(row, fieldToCol['puckname'], 'string', 'Puck Name');
            const puckType = sanitizeAndValidateColumn(row, fieldToCol['pucktype'], 'string', 'Unipuck', true); // Default: `Unipuck`
            const sampleName = sanitizeAndValidateColumn(row, fieldToCol['crystalname'], 'string', 'Sample Name');

            if (dewarName && puckName) {
                let dewar;
                if (!dewars.has(dewarName)) {
                    // Initialize new dewar object
                    dewar = {
                        ...initialNewDewarState,
                        pgroups: activePgroup,
                        dewar_name: dewarName,
                        contact_id: contact.id,
                        return_address_id: address.id,
                        pucks: [],
                    };
                    dewars.set(dewarName, dewar);
                    puckPositionMap.set(dewarName, new Map());

                    // Check if dewar exists using backend
                    const existingDewar = await checkIfDewarExists(dewarName);
                    if (existingDewar) {
                        dewarsToReplace.push(existingDewar);
                    }
                } else {
                    dewar = dewars.get(dewarName);
                }

                // Handle puck positions
                let puckPositions = puckPositionMap.get(dewarName);
                if (!puckPositions.has(puckName)) {
                    puckPositions.set(puckName, puckPositions.size + 1);
                }

                const puckPosition = puckPositions.get(puckName);

                // Create puck and attach it to the dewar
                let puck = dewar.pucks.find((p) => p.puck_name === puckName);
                if (!puck) {
                    puck = { puck_name: puckName, puck_type: puckType, puck_location_in_dewar: puckPosition, samples: [] };
                    dewar.pucks.push(puck);
                }

                // Add sample to puck
                const sample = {
                    sample_name: sampleName,
                    position: sanitizeIntColumn(row, fieldToCol['positioninpuck'], 'Sample Position'),
                    proteinname: sanitizeAndValidateColumn(row, fieldToCol['proteinname'], 'string', 'Protein Name'),
                    priority: sanitizeIntColumn(row, fieldToCol['priority'], 'Priority'),
                    comments: sanitizeAndValidateColumn(row, fieldToCol['comments'], 'string', 'Comments', true),
                    data_collection_parameters: collectDataParameters(row), // Consolidate data parameters
                };

                puck.samples.push(sample);
            } else {
                console.error(`Row ${rowIndex} is missing required fields for dewar/puck creation.`);
            }
        }

        const dewarsArray = Array.from(dewars.values());

        // Save for update dialog control
        setDewarsToCreate(dewars);

        if (dewarsArray.length > 0 && dewarsToReplace.length > 0) {
            setDewarsToReplace(dewarsToReplace);
            setShowUpdateDialog(true);
        } else if (dewarsArray.length > 0) {
            await handleDewarCreation(dewarsArray);
        }
    };

    const sanitizeAndValidateColumn = (row, colIndex, type, columnName, isOptional = false) => {
        const value = row?.data?.[colIndex];
        const sanitizedValue = type === 'string' ? value?.trim() : value;

        if (!sanitizedValue && !isOptional) {
            console.error(`${columnName} is missing or invalid.`);
            return null;
        }

        if (type === 'number' && isNaN(sanitizedValue)) {
            console.error(`${columnName} is not a valid number.`);
        }

        return sanitizedValue;
    };

// Utility to sanitize integer columns
    const sanitizeIntColumn = (row, colIndex, columnName) => {
        const rawValue = row?.data?.[colIndex];
        const intValue = parseInt(rawValue, 10);

        if (isNaN(intValue)) {
            console.error(`${columnName} is not a valid integer.`);
        }

        return intValue;
    };

// Consolidate data collection parameters
    const collectDataParameters = (row) => ({
        directory: sanitizeAndValidateColumn(row, fieldToCol['directory'], 'string', 'Directory', true),
        oscillation: parseFloat(sanitizeAndValidateColumn(row, fieldToCol['oscillation'], 'number', 'Oscillation', true)),
        aperture: sanitizeAndValidateColumn(row, fieldToCol['aperture'], 'string', 'Aperture', true),
        exposure: parseFloat(sanitizeAndValidateColumn(row, fieldToCol['exposure'], 'number', 'Exposure', true)),
        totalrange: sanitizeIntColumn(row, fieldToCol['totalrange'], 'Total Range'),
        transmission: sanitizeIntColumn(row, fieldToCol['transmission'], 'Transmission'),
        dose: parseFloat(sanitizeAndValidateColumn(row, fieldToCol['dose'], 'number', 'Dose', true)),
        targetresolution: parseFloat(sanitizeAndValidateColumn(row, fieldToCol['targetresolution'], 'number', 'Target Resolution', true)),
        datacollectiontype: sanitizeAndValidateColumn(row, fieldToCol['datacollectiontype'], 'string', 'Data Collection Type', true),
        processingpipeline: sanitizeAndValidateColumn(row, fieldToCol['processingpipeline'], 'string', 'Processing Pipeline', true),
        spacegroupnumber: sanitizeIntColumn(row, fieldToCol['spacegroupnumber'], 'Space Group Number'),
        cellparameters: sanitizeAndValidateColumn(row, fieldToCol['cellparameters'], 'string', 'Cell Parameters', true),
        rescutkey: sanitizeAndValidateColumn(row, fieldToCol['rescutkey'], 'string', 'Resolution Cut Key', true),
        rescutvalue: parseFloat(sanitizeAndValidateColumn(row, fieldToCol['rescutvalue'], 'number', 'Resolution Cut Value', true)),
        userresolution: parseFloat(sanitizeAndValidateColumn(row, fieldToCol['userresolution'], 'number', 'User Resolution', true)),
        pdbid: sanitizeAndValidateColumn(row, fieldToCol['pdbid'], 'string', 'PDB ID', true),
        autoprocfull: row.data[fieldToCol['autoprocfull']] === true,
        procfull: row.data[fieldToCol['procfull']] === true,
        adpenabled: row.data[fieldToCol['adpenabled']] === true,
        noano: row.data[fieldToCol['noano']] === true,
        ffcscampaign: row.data[fieldToCol['ffcscampaign']] === true,
        trustedhigh: parseFloat(sanitizeAndValidateColumn(row, fieldToCol['trustedhigh'], 'number', 'Trusted High', true)),
        autoprocextraparams: sanitizeAndValidateColumn(row, fieldToCol['autoprocextraparams'], 'string', 'Autoproc Extra Params', true),
        chiphiangles: parseFloat(sanitizeAndValidateColumn(row, fieldToCol['chiphiangles'], 'number', 'Chi/Phi Angles', true)),
    });

    const handleConfirmUpdate = async () => {
        if (dewarsToReplace.length === 0) return;

        try {
            const dewarsArray = Array.from(dewarsToCreate.values());

            // Perform the creation/update operation
            await handleDewarCreation(dewarsArray);

            console.log("Dewars replaced successfully");
        } catch (error: any) {
            console.error("Error replacing dewars:", error);

            let errorMessage = error?.message || "Unexpected error occurred while replacing dewars.";
            setErrorMessages((prevMessages) => [...prevMessages, errorMessage]);
            setErrorDialogOpen(true);
        }

        // Reset controls after either success or failure
        setShowUpdateDialog(false);
        setDewarsToReplace([]);
        setDewarsToCreate(new Map());
    };

    const handleCancelUpdate = () => {
        setShowUpdateDialog(false);
        setDewarsToReplace([]);
        setDewarsToCreate(new Map());
    };

    const handleDewarCreation = async (dewarsArray: any[]) => {
        const errorMessages: string[] = []; // Collect error messages for display

        for (const dewar of dewarsArray) {
            try {
                const { number_of_pucks, number_of_samples, ...payload } = dewar;

                // Attempt to create or update a dewar
                await DewarsService.createOrUpdateDewarProtectedDewarsPost(selectedShipment.id, payload);

                console.log(`Dewar "${dewar.dewar_name}" created/updated successfully.`);
            } catch (error: any) {
                // Log the full error object for debugging purposes
                console.error("Full error object:", error);

                let backendReason = "Unexpected error occurred."; // Default fallback message

                if (error instanceof ApiError && error.body) {
                    // API error response (similar to delete route)
                    console.error("API error body:", error.body);
                    backendReason = error.body.detail || backendReason;
                } else if (error?.response?.data?.detail) {
                    // Fallback for Axios-like errors
                    backendReason = error.response.data.detail;
                } else if (error?.response) {
                    // Unexpected HTTP response (no error detail)
                    backendReason = `Unknown error occurred (Status: ${error.response.status}).`;
                    console.error("Error Response Data:", error.response.data);
                } else if (error?.message) {
                    // Client-side error (e.g., network issues)
                    backendReason = error.message;
                }

                // Append the detailed error message to the list
                errorMessages.push(`Dewar "${dewar.dewar_name}": ${backendReason}`);
            }
        }

        // Notify the user if there are error messages
        if (errorMessages.length > 0) {
            setErrorMessages(errorMessages); // Set state to render an error dialog
            setErrorDialogOpen(true); // Open the error dialog

            // Re-throw the error to let the parent function (e.g., handleConfirmUpdate) know something went wrong
            throw new Error("Error(s) occurred while creating/updating dewars.");
        } else {
            console.log("All dewars processed successfully.");
            alert("All dewars created successfully!"); // Show success message
        }
    };

    const handleSubmit = async () => {
        if (isSubmitting) return;
        if (!headers || headers.length === 0) {
            console.error('Cannot submit, headers are not defined or empty');
            return;
        }

        if (allCellsValid()) {
            setIsSubmitting(true);
            console.log('All data is valid. Proceeding with submission...');

            await createOrUpdateDewarsFromSheet(
                raw_data,
                selectedShipment?.contact,
                selectedShipment?.return_address
            );

            setIsSubmitting(false);
        } else {
            console.log('There are validation errors in the dataset. Please correct them before submission.');
        }
    };

    const downloadCorrectedSpreadsheet = async () => {
        const workbook = new ExcelJS.Workbook();
        await workbook.xlsx.load(fileBlob);
        const worksheet = workbook.getWorksheet(1);

        raw_data.forEach((row, rowIndex) => {
            row.data.forEach((value, colIndex) => {
                worksheet.getRow(row.row_num).getCell(colIndex + 1).value = value;
            });
        });

        workbook.xlsx.writeBuffer().then((buffer) => {
            const blob = new Blob([buffer], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
            saveAs(blob, 'corrected_spreadsheet.xlsx');
        });
    };

    return (
        <TableContainer component={Paper}>
            <Box display="flex" justifyContent="space-between" mb={2}>
                <Typography variant="body2" style={{ backgroundColor: "#e6fbe6", padding: "4px 8px", borderRadius: "4px" }}>
                    Default Assigned (Light Green)
                </Typography>
                <Typography variant="body2" style={{ backgroundColor: "#fff8e1", padding: "4px 8px", borderRadius: "4px" }}>
                    Corrected (Light Yellow)
                </Typography>
            </Box>
            <Table>
                <TableHead>
                    <TableRow>
                        {headers.map((header, index) => (
                            <TableCell key={index} align="center">
                                <Typography variant="body2">{header}</Typography>
                            </TableCell>
                        ))}
                    </TableRow>
                </TableHead>
                <Box display="flex" justifyContent="space-between" mt={2}>
                    <Button variant="contained" color="secondary" onClick={onCancel}>
                        Cancel
                    </Button>
                    <Button variant="contained" color="primary" onClick={handleSubmit} disabled={!allCellsValid() || isSubmitting}>
                        Submit
                    </Button>
                    <Button variant="contained" onClick={downloadCorrectedSpreadsheet} disabled={!allCellsValid()}>
                        Download Corrected Spreadsheet
                    </Button>
                </Box>
                <TableBody>
                    {enhancedRawData.map((row, rowIndex) => (
                        <TableRow key={rowIndex}>
                            {headers.map((header, colIndex) => {
                                const key = `${row.row_num}-${header}`;
                                const errorMessage = errorMap.get(key);
                                const isInvalid = !!errorMessage;
                                const isDuplicateError = errorMessage?.toLowerCase().includes("duplicate position"); // Detect duplicate-specific messages
                                const cellValue = row.data[colIndex];
                                const editingValue = editingCell[`${rowIndex}-${colIndex}`];
                                const isCellCorrected = row.corrected_columns?.includes(header); // Use corrected metadata
                                const isDefaultAssigned = row.default_set_columns?.includes(header); // Dynamically match header name

                                return (
                                    <TableCell
                                        key={colIndex}
                                        align="center"
                                        style={{
                                            backgroundColor: isDefaultAssigned
                                                ? "#e6fbe6" // Light green for default values
                                                : isCellCorrected
                                                    ? "#fff8e1" // Light yellow for corrections
                                                    : isDuplicateError
                                                        ? "#fdecea" // Light red for duplicate errors
                                                        : "transparent", // No specific color for valid cells
                                            color: isDefaultAssigned
                                                ? "#1b5e20" // Dark green for default values
                                                : isDuplicateError
                                                    ? "#d32f2f" // Bright red for duplicates
                                                    : "inherit",
                                            fontWeight: isDefaultAssigned || isCellCorrected || isDuplicateError
                                                ? "bold"
                                                : "normal",
                                            cursor: isInvalid ? "pointer" : "default", // Allow editing invalid cells
                                        }}
                                    >
                                        <Tooltip
                                            title={
                                                isDefaultAssigned
                                                    ? "This value was automatically assigned as a default."
                                                    : isCellCorrected
                                                        ? `Field "${header}" was auto-corrected.`
                                                        : isDuplicateError
                                                            ? `Duplicate value detected in "${header}". Please ensure values are unique in this column.`
                                                            : errorMessage || ""
                                            }
                                            arrow
                                            disableHoverListener={!isDefaultAssigned && !isCellCorrected && !isInvalid}
                                        >
                                            {isInvalid ? (
                                                <TextField
                                                    value={editingValue !== undefined ? editingValue : cellValue}
                                                    onChange={(e) =>
                                                        setEditingCell({
                                                            ...editingCell,
                                                            [`${rowIndex}-${colIndex}`]: e.target.value,
                                                        })
                                                    }
                                                    onBlur={() => handleCellBlur(rowIndex, colIndex)}
                                                    error={isInvalid}
                                                    fullWidth
                                                    variant="outlined"
                                                    size="small"
                                                />
                                            ) : (
                                                cellValue
                                            )}
                                        </Tooltip>
                                    </TableCell>
                                );
                            })}
                        </TableRow>
                    ))}
                </TableBody>
            </Table>

            <Dialog
                open={showUpdateDialog}
                onClose={handleCancelUpdate}
                aria-labelledby="alert-dialog-title"
                aria-describedby="alert-dialog-description"
            >
                <DialogTitle id="alert-dialog-title">Replace Dewars</DialogTitle>
                <DialogContent>
                    <DialogContentText id="alert-dialog-description">
                        The following dewars already exist: {dewarsToReplace.map(dewar => dewar.dewar_name).join(', ')}. Would you like to replace them?
                    </DialogContentText>
                </DialogContent>
                <DialogActions>
                    <Button onClick={handleCancelUpdate} color="primary">
                        Cancel
                    </Button>
                    <Button onClick={handleConfirmUpdate} color="primary" autoFocus>
                        Replace
                    </Button>
                </DialogActions>
            </Dialog>

            <Dialog
                open={errorDialogOpen}
                onClose={() => setErrorDialogOpen(false)}
                aria-labelledby="error-dialog-title"
                aria-describedby="error-dialog-description"
            >
                <DialogTitle>Error Creating/Updating Dewars</DialogTitle>
                <DialogContent>
                    <DialogContentText>
                        The following errors occurred while processing dewars:
                        <ul>
                            {errorMessages.map((message, index) => (
                                <li key={index}>{message}</li>
                            ))}
                        </ul>
                    </DialogContentText>
                </DialogContent>
                <DialogActions>
                    <Button onClick={() => setErrorDialogOpen(false)} color="primary">
                        Close
                    </Button>
                </DialogActions>
            </Dialog>

        </TableContainer>

    );
};

export default SpreadsheetTable;