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
                          }) => {
    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 initialNewDewarState = {
        number_of_pucks: 0,
        number_of_samples: 0,
        ready_date: null,
        shipping_date: null,
        arrival_date: null,
        returning_date: null,
        qrcode: 'N/A',
        contact_person_id: selectedShipment?.contact_person?.id,
        return_address_id: selectedShipment?.return_address?.id,
        dewar_name: '',
        tracking_number: 'UNKNOWN',
        status: 'In preparation',
        pucks: [] // Ensure 'pucks' array exists
    };

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

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

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

    const errorMap = generateErrorMap(localErrors);

    useEffect(() => {
        const initialNonEditableCells = new Set();
        raw_data.forEach((row, rowIndex) => {
            headers.forEach((_, colIndex) => {
                const key = `${row.row_num}-${headers[colIndex]}`;
                if (!errorMap.has(key)) {
                    initialNonEditableCells.add(`${rowIndex}-${colIndex}`);
                }
            });
        });
        setNonEditableCells(initialNonEditableCells);
    }, [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;

        if (!currentRow.data) {
            currentRow.data = [];
        }

        currentRow.data[colIndex] = newValue;

        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.is_valid !== undefined) {
                if (response.is_valid) {
                    const updatedErrors = localErrors.filter(
                        (error) => !(error.row === currentRow.row_num && error.cell === colIndex)
                    );
                    setLocalErrors(updatedErrors);
                    setNonEditableCells((prev) => new Set([...prev, `${rowIndex}-${colIndex}`]));
                } else {
                    const updatedErrors = [
                        ...localErrors,
                        { row: currentRow.row_num, cell: colIndex, message: response.message || 'Invalid value.' }
                    ];
                    setLocalErrors(updatedErrors);
                }
            }
            setRawData(updatedRawData);
        } catch (error) {
            console.error('Validation failed:', error);
        }
    };

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

    const allCellsValid = () => nonEditableCells.size === raw_data.length * headers.length;

    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) => {
        if (!selectedShipment) return null;

        try {
            const shipDewars = await ShipmentsService.getDewarsByShipmentIdShipmentsShipmentIdDewarsGet(selectedShipment.id);
            return shipDewars.find((d) => d.dewar_name === dewarName);
        } catch (error) {
            console.error('Failed to fetch existing dewars:', error);
            return null;
        }
    };

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

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

        const dewarNameIdx = fieldToCol['dewarname'];
        const puckNameIdx = fieldToCol['puckname'];
        const puckTypeIdx = fieldToCol['pucktype'];
        const sampleNameIdx = fieldToCol['crystalname'];
        const samplePositionIdx = fieldToCol['positioninpuck'];

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

            if (!row.data) {
                console.error('Row data is missing');
                continue;
            }

            const dewarName = typeof row.data[dewarNameIdx] === 'string' ? row.data[dewarNameIdx].trim() : null;
            const puckName = row.data[puckNameIdx] !== undefined && row.data[puckNameIdx] !== null ? String(row.data[puckNameIdx]).trim() : null;
            const puckType = typeof row.data[puckTypeIdx] === 'string' ? row.data[puckTypeIdx] : 'Unipuck';
            const sampleName = typeof row.data[sampleNameIdx] === 'string' ? row.data[sampleNameIdx].trim() : null;
            const samplePosition = row.data[samplePositionIdx] !== undefined && row.data[samplePositionIdx] !== null ? Number(row.data[samplePositionIdx]) : null;

            if (dewarName && puckName) {
                let dewar;
                if (!dewars.has(dewarName)) {
                    dewar = {
                        ...initialNewDewarState,
                        dewar_name: dewarName,
                        contact_person_id: contactPerson.id,
                        return_address_id: returnAddress.id,
                        pucks: []
                    };
                    dewars.set(dewarName, dewar);
                    puckPositionMap.set(dewarName, new Map());

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

                let puckPositions = puckPositionMap.get(dewarName);
                if (!puckPositions.has(puckName)) {
                    puckPositions.set(puckName, puckPositions.size + 1);
                }
                const puckPosition = puckPositions.get(puckName);

                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);
                }

                const sample = {
                    sample_name: sampleName,
                    position: samplePosition,
                    results: null // Placeholder for results field
                };

                if (isNaN(sample.position)) {
                    console.error(`Invalid sample position for sample ${sample.sample_name} in puck ${puckName}`);
                } else {
                    puck.samples.push(sample);
                }
            } else {
                if (!dewarName) {
                    console.error(`Dewar name is missing in row ${rowIndex}`);
                }
                if (!puckName) {
                    console.error(`Puck name is missing in row ${rowIndex}`);
                }
            }
        }

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

        // Save dewars array for later use in handleConfirmUpdate
        setDewarsToCreate(dewars);

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

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

        try {
            for (const dewar of dewarsToReplace) {
                await DewarsService.deleteDewarDewarsDewarIdDelete(dewar.id);
            }
            const dewarsArray = Array.from(dewarsToCreate.values());
            await handleDewarCreation(dewarsArray);
            console.log('Dewars replaced successfully');
        } catch (error) {
            console.error('Error replacing dewar', error);
            if (error instanceof ApiError && error.body) {
                console.error('Validation errors:', error.body.detail);
            } else {
                console.error('Unexpected error:', error);
            }
        }
        setShowUpdateDialog(false);
        setDewarsToReplace([]);
        setDewarsToCreate(new Map());
    };

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

    const handleDewarCreation = async (dewarsArray) => {
        for (const dewar of dewarsArray) {
            try {
                if (!dewar.pucks || dewar.pucks.length === 0) {
                    console.error(`Dewar ${dewar.dewar_name} does not have any pucks.`);
                    continue;
                }

                const createdDewar = await DewarsService.createDewarDewarsPost(dewar);

                if (createdDewar && selectedShipment) {
                    await ShipmentsService.addDewarToShipmentShipmentsShipmentIdAddDewarPost(
                        selectedShipment.id,
                        createdDewar.id
                    );
                    console.log(`Dewar ${createdDewar.dewar_name} with ID ${createdDewar.id} created and added to the shipment.`);
                }
            } catch (error) {
                console.error('Error adding dewar', error);
                if (error instanceof ApiError && error.body) {
                    console.error('Validation errors:', error.body.detail);
                } else {
                    console.error('Unexpected error:', error);
                }
            }
        }
    };

    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_person,
                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}>
            <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>
                    {raw_data.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 cellValue = (row.data && row.data[colIndex]) || "";
                                const editingValue = editingCell[`${rowIndex}-${colIndex}`];
                                const isReadonly = !isInvalid;

                                return (
                                    <TableCell key={colIndex} align="center">
                                        <Tooltip title={errorMessage || ""} arrow disableHoverListener={!isInvalid}>
                                            {isInvalid ? (
                                                <TextField
                                                    value={editingValue !== undefined ? editingValue : cellValue}
                                                    onChange={(e) => setEditingCell({ ...editingCell, [`${rowIndex}-${colIndex}`]: e.target.value })}
                                                    onKeyDown={(e) => {
                                                        if (e.key === "Enter") {
                                                            handleCellEdit(rowIndex, colIndex);
                                                        }
                                                    }}
                                                    onBlur={() => handleCellBlur(rowIndex, colIndex)}
                                                    error={isInvalid}
                                                    fullWidth
                                                    variant="outlined"
                                                    size="small"
                                                    disabled={isReadonly}
                                                />
                                            ) : (
                                                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>
        </TableContainer>
    );
};

export default SpreadsheetTable;