src_utils_CSVParser.js

import Papa from 'papaparse';
import contact from '../pages/ContactDesignation/contact';

const IDENTIFIER_LINE_2 = "### THESE TWO LINES SERVES AS IDENTIFIER. DO NOT DELETE ###";

/**
 * Enum for different identifiers fr different pages
 * @readonly
 * @enum {string}
 */
export const Identifiers = Object.freeze({
    TEST_PLANNING:           "### THIS CSV IS INTENDED TO BE USED AT WIRECRACKER.COM FOR TEST PLANNING ###",
    LOCALIZATION:            "### THIS CSV IS INTENDED TO BE USED AT WIRECRACKER.COM FOR LOCALIZATION ###",
    DESIGNATION:             "### THIS CSV IS INTENDED TO BE USED AT WIRECRACKER.COM FOR DESIGNATION ###",
    STIMULATION:             "### THIS CSV IS INTENDED TO BE USED AT WIRECRACKER.COM FOR CCEPS / SEIZURE RECREATION PLANNING ###",
    STIMULATION_FUNCTION:    "### THIS CSV IS INTENDED TO BE USED AT WIRECRACKER.COM FOR FUNCTIONAL MAPPING PLANNING ###",
    STIMULATION_RECREATION:   "### THIS CSV IS INTENDED TO BE USED AT WIRECRACKER.COM FOR SEIZURE RECREATION PLANNING ###",
    STIMULATION_CCEP:         "### THIS CSV IS INTENDED TO BE USED AT WIRECRACKER.COM FOR CCEPS PLANNING ###",
    RESECTION:               "### THIS CSV IS INTENDED TO BE USED AT WIRECRACKER.COM FOR RESECTION ###",
});

/**
 * Parses a CSV file and returns the parsed data.
 *
 * @param {File} file - The CSV file to be parsed.
 * @param {boolean} coordinates - Whether the file is a coordinates file.
 * @param {Function} showError - Function to display error messages.
 * @returns {Promise<{ identifier: string, data: Object }>} A promise that resolves with the identifier and parsed CSV data.
 */
export function parseCSVFile(file, coordinates = false, showError = null) {
    return new Promise((resolve, reject) => {
        if (!file) {
            const errorMsg = "No file provided.";
            if (showError) showError(errorMsg);
            reject(new Error(errorMsg));
            return;
        }

        const reader = new FileReader();

        reader.onload = function (e) {
            const fileContent = e.target.result;
            const lines = fileContent.split(/\r?\n/);

            if (!coordinates && (lines.length < 2 ||
                (
                    !lines[0].trim().includes(Identifiers.TEST_PLANNING) &&
                    !lines[0].trim().includes(Identifiers.LOCALIZATION) &&
                    !lines[0].trim().includes(Identifiers.DESIGNATION) &&
                    !lines[0].trim().includes(Identifiers.STIMULATION) &&
                    !lines[0].trim().includes(Identifiers.STIMULATION_FUNCTION) &&
                    !lines[0].trim().includes(Identifiers.STIMULATION_RECREATION) &&
                    !lines[0].trim().includes(Identifiers.STIMULATION_CCEP) &&
                    !lines[0].trim().includes(Identifiers.RESECTION)
                ) || !lines[1].trim().includes(IDENTIFIER_LINE_2) )) {
                const errorMsg = "Invalid file. The first line must be the correct identifier.";
                if (showError) showError(errorMsg);
                reject(new Error(errorMsg));
                return;
            }

            let identifier;
            let csvWithoutIdentifier;
            let metadata = {
                patientId: '',
                creationDate: null,
                modifiedDate: null,
                fileId: null
            };

            if (!coordinates) { 
                identifier = lines[0].trim();
                // Extract metadata from the CSV header
                for (let i = 2; i < 6; i++) {
                    if (lines[i].startsWith('PatientID:')) {
                        metadata.patientId = lines[i].split('PatientID:')[1].trim();
                    } else if (lines[i].startsWith('CreatedDate:')) {
                        metadata.creationDate = lines[i].split('CreatedDate:')[1].trim();
                    } else if (lines[i].startsWith('ModifiedDate:')) {
                        metadata.modifiedDate = lines[i].split('ModifiedDate:')[1].trim();
                    } else if (lines[i].startsWith('FileID:')) {
                        metadata.fileId = lines[i].split('FileID:')[1].trim();
                    }
                }
                // Parse CSV content excluding the identifier and metadata lines
                csvWithoutIdentifier = lines.slice(6).join("\n");
            } else {
                identifier = "coordinates";
                csvWithoutIdentifier = lines.join("\n");
            }

            if (identifier.includes(Identifiers.LOCALIZATION)) {
                resolve({ identifier, data: parseLocalization(csvWithoutIdentifier), metadata });
                return;
            }
            else if (identifier.includes(Identifiers.DESIGNATION) || identifier.includes(Identifiers.RESECTION)) {
                // First parse as localization to get the original structure
                const localizationData = parseLocalization(csvWithoutIdentifier);
                // Then parse as designation for the current state
                const designationData = parseDesignation(csvWithoutIdentifier);
                resolve({ 
                    identifier, 
                    data: {
                        originalData: localizationData,
                        data: designationData
                    },
                    metadata
                });
                return;
            }
            else if (identifier.includes(Identifiers.STIMULATION) || identifier.includes(Identifiers.STIMULATION_FUNCTION) || identifier.includes(Identifiers.STIMULATION_RECREATION) || identifier.includes(Identifiers.STIMULATION_CCEP)) {
                const stimulationData = parseStimulation(csvWithoutIdentifier);
                resolve({identifier, data: stimulationData, metadata});
                return;
            }
            else if (identifier.includes(Identifiers.TEST_PLANNING)) {
                resolve({ identifier, data: parseTests(csvWithoutIdentifier), metadata });
                return;
            }

            Papa.parse(csvWithoutIdentifier, {
                header: true,
                comments: "#",
                skipEmptyLines: true,
                dynamicTyping: true, // Ensures correct data types for numbers
                complete: function (results) {
                    resolve({ identifier, data: results.data, metadata });
                },
                error: function (err) {
                    if (showError) showError("Parsing error: " + err.message);
                    reject(new Error("Parsing error: " + err.message));
                }
            });
        };

        reader.onerror = function () {
            if (showError) showError("Error reading file.");
            reject(new Error("Error reading file."));
        };

        reader.readAsText(file);
    });
}

/**
 * Parses localization CSV data into a nested dictionary format.
 *
 * @param {Object[]} data - Parsed CSV data from PapaParse
 * @returns {Object} A nested dictionary with the format { Label: { ContactNumber: {"electrodeDescription": "Left Entorhinal", "contactDescription": "Left Entorhinal", "associatedLocation": "GM"}, ... }, ... }
 */
function parseLocalization(csvData) {
    const parsedData = {};
    const rows = Papa.parse(csvData, { header: true, skipEmptyLines: true }).data;
    
    rows.forEach(row => {
        const label = row.Label.trim();
        const contactNumber = row.ContactNumber.trim();
        const electrodeDescription = row.ElectrodeDescription.trim();
        const contactDescription = row.ContactDescription.trim();
        const associatedLocation = row.AssociatedLocation.trim();
        const electrodeType = row.Type ? row.Type.trim() : 'DIXI'; // Default to DIXI if not specified
        
        if (!parsedData[label]) {
            parsedData[label] = { 
                'description': electrodeDescription,
                'type': electrodeType // Store the electrode type
            };
        }
        parsedData[label][contactNumber] = {
            contactDescription,
            associatedLocation
        };
    });
    
    return parsedData;
}

/**
 * Parses designation CSV data into a data structure format.
 *
 * @param {Object[]} data - Parsed CSV data from PapaParse
 * @returns {Object} A data structure with the format [{ label: 'A'', contacts: [contact, contact, ...] }, ... ]
 */
function parseDesignation(csvData) {
    const parsedData = {};
    const rows = Papa.parse(csvData, { header: true, skipEmptyLines: true }).data;
    
    // First pass: Group by electrode label and collect contacts
    rows.forEach(row => {
        const label = row.Label.trim();
        const contactNumber = parseInt(row.ContactNumber);
        let associatedLocation = row.AssociatedLocation.trim();
        const contactDescription = row.ContactDescription.trim();
        const electrodeDescription = row.ElectrodeDescription.trim();
        const mark = parseInt(row.Mark) || 0; // Default to 0 if not specified
        const surgeonMark = parseInt(row.SurgeonMark) === 1; // Convert to boolean from int (0 or 1)
        const electrodeType = row.Type ? row.Type.trim() : 'DIXI'; // Default to DIXI if not specified
        
        // Process associated location based on GM presence
        if (associatedLocation === 'GM') {
            associatedLocation = contactDescription;
        } else if (associatedLocation === 'GM/WM') {
            associatedLocation = `${contactDescription}/WM`;
        } else if (associatedLocation === 'GM/GM') {
            const [desc1, desc2] = contactDescription.split('+');
            associatedLocation = `${desc1}/${desc2}`;
        }
        // For other cases (like WM), keep the original associatedLocation
        
        if (!parsedData[label]) {
            parsedData[label] = {
                label: label,
                contacts: [],
                type: electrodeType // Store the electrode type
            };
        }
        
        const contactObj = {
            ...(new contact(associatedLocation, mark, surgeonMark)),
            id: `${label}${contactNumber}`,
            index: contactNumber,
            __electrodeDescription__: electrodeDescription,
            __contactDescription__: contactDescription,
        };
        
        // Add to contacts array at the correct index (contactNumber - 1)
        // Ensure the array is large enough
        while (parsedData[label].contacts.length < contactNumber) {
            parsedData[label].contacts.push(null);
        }
        parsedData[label].contacts[contactNumber - 1] = contactObj;
    });
    
    // Convert to array format matching demo data
    return Object.values(parsedData).map(electrode => ({
        label: electrode.label,
        contacts: electrode.contacts.filter(contact => contact !== null), // Remove any null entries
        type: electrode.type // Include the electrode type in the output
    }));
}

/**
 * Parses stimulation CSV data into a data structure format.
 *
 * @param {Object[]} data - Parsed CSV data from PapaParse
 * @returns {Object} A data structure with the format [{ label: 'A'', contacts: [contact, contact, ...] }, ... ]
 */
function parseStimulation(csvData) {
    const parsedData = {};
    const rows = Papa.parse(csvData, { header: true, skipEmptyLines: true }).data;
    const planOrder = [];

    // First pass: Group by electrode label and collect contacts
    rows.forEach(row => {
        const label = row.Label.trim();
        const contactNumber = parseInt(row.ContactNumber);
        let associatedLocation = row.AssociatedLocation.trim();
        const contactDescription = row.ContactDescription.trim();
        const mark = parseInt(row.Mark) || 0; // Default to 0 if not specified)
        const surgeonMark = row.SurgeonMark.trim() === "true"; // Convert to boolean
        const pair = parseInt(row.Pair);
        const isPlanning = row.IsPlanning.trim() === "true";
        const electrodeDescription = row.ElectrodeDescription.trim();
        const frequency = parseFloat(row.Frequency) || 105; // TODO : ask what default value should be
        const duration = parseFloat(row.Duration) || 3.0;
        const current = parseFloat(row.Current) || 2.445;
        const order = parseInt(row.PlanOrder) || -1;

        // Process associated location based on GM presence
        if (associatedLocation === 'GM') {
            associatedLocation = contactDescription;
        } else if (associatedLocation === 'GM/WM') {
            associatedLocation = `${contactDescription}/WM`;
        } else if (associatedLocation === 'GM/GM') {
            const [desc1, desc2] = contactDescription.split('+');
            associatedLocation = `${desc1}/${desc2}`;
        }
        // For other cases (like WM), keep the original associatedLocation

        if (!parsedData[label]) {
            parsedData[label] = {
                label: label,
                contacts: []
            };
        }

        const contactObj = {
            ...(new contact(associatedLocation, mark, surgeonMark)),
            __electrodeDescription__: electrodeDescription,
            __contactDescription__: contactDescription,
            id: label + contactNumber,
            index: contactNumber,
            pair: pair,
            isPlanning: isPlanning,
            duration: duration,
            frequency: frequency,
            current: current,
            order: order
        };

        if (isPlanning && order >= 0) {
            planOrder[order] = contactObj.id;
        }

        // Add to contacts array at the correct index (contactNumber - 1)
        // Ensure the array is large enough
        while (parsedData[label].contacts.length < contactNumber) {
            parsedData[label].contacts.push(null);
        }
        parsedData[label].contacts[contactNumber - 1] = contactObj;
    });

    // Convert to array format matching demo data
    return {
        data: Object.values(parsedData).map(electrode => ({
            label: electrode.label,
            contacts: electrode.contacts.filter(contact => contact !== null) // Remove any null entries
        })),
        planOrder: planOrder.filter(Boolean) // Remove any null/undefined entries
    };
}

/**
 * Parses stimulation CSV data into a data structure format.
 *
 * @param {Object[]} data - Parsed CSV data from PapaParse
 * @returns {Object} A data structure with the format [{ label: 'A'', contacts: [contact, contact, ...] }, ... ]
 */
function parseTests(csvData) {
    const parsedData = {};
    const tests = {};
    const rows = Papa.parse(csvData, { header: true, skipEmptyLines: true }).data;
    let hasAnyTests = false;

    // First pass: Group by electrode label and collect contacts
    rows.forEach(row => {
        const label = row.Label.trim();
        const contactNumber = parseInt(row.ContactNumber);
        let associatedLocation = row.AssociatedLocation.trim();
        const contactDescription = row.ContactDescription.trim();
        const mark = parseInt(row.Mark) || 0; // Default to 0 if not specified
        const surgeonMark = row.SurgeonMark.trim() === "true"; // Convert to boolean
        const pair = parseInt(row.Pair);
        const electrodeDescription = row.ElectrodeDescription.trim();
        const frequency = parseFloat(row.Frequency) || 105;
        const duration = parseFloat(row.Duration) || 3.0;
        const current = parseFloat(row.Current) || 2.445;
        const testID = row.TestID.trim() || "No test";
        const isPlanning = row.IsPlanning ? row.IsPlanning.trim() === "true" : true; // Default to true if not specified

        // Process associated location based on GM presence
        if (associatedLocation === 'GM') {
            associatedLocation = contactDescription;
        } else if (associatedLocation === 'GM/WM') {
            associatedLocation = `${contactDescription}/WM`;
        } else if (associatedLocation === 'GM/GM') {
            const [desc1, desc2] = contactDescription.split('+');
            associatedLocation = `${desc1}/${desc2}`;
        }

        if (!parsedData[label]) {
            parsedData[label] = {
                label: label,
                contacts: []
            };
        }

        const contactObj = {
            ...(new contact(associatedLocation, mark, surgeonMark)),
            __electrodeDescription__: electrodeDescription,
            __contactDescription__: contactDescription,
            id: label + contactNumber,
            electrodeLabel: label,
            index: contactNumber,
            pair: pair,
            duration: duration,
            frequency: frequency,
            current: current,
            isPlanning: isPlanning
        };

        // Add to contacts array at the correct index (contactNumber - 1)
        // Ensure the array is large enough
        while (parsedData[label].contacts.length < contactNumber) {
            parsedData[label].contacts.push(null);
        }
        parsedData[label].contacts[contactNumber - 1] = contactObj;

        // Add test ID to the contact's test list
        if (testID !== "No test") {
            hasAnyTests = true;
            if (!tests[contactObj.id]) {
                tests[contactObj.id] = [];
            }
            tests[contactObj.id].push({id: parseInt(testID)});
        }
    });

    // Convert to array format matching demo data
    return {
        contacts: Object.values(parsedData).map(electrode => ({
            label: electrode.label,
            contacts: electrode.contacts.filter(contact => contact !== null) // Remove any null entries
        })),
        tests: hasAnyTests ? tests : {}
    };
}

/**
 * Saves a CSV file from data and downloads it or returns the data.
 * 
 * @param {string} identifier - The identifier for the first line.
 * @param {Object} data - The data to be saved.
 * @param {string} patientId - The patient ID for the first line.
 * @param {string} createdDate - The created date for the first line.
 * @param {string} modifiedDate - The modified date for the first line.
 * @param {boolean} download - Whether to download the file or return the data.
 * @returns {Object|void} The parsed data if download is false, otherwise void.
 */
export function saveCSVFile(identifier, data, patientId = '', createdDate = null, modifiedDate = null, download = true, fileId = null) {
    const currentDate = new Date().toISOString();
    const finalPatientId = patientId || data.patientId || '';
    const finalCreatedDate = createdDate || currentDate;
    const finalModifiedDate = modifiedDate || currentDate;
    const finalFileId = fileId || data.fileId || '';

    let csvContent = `${identifier}\n${IDENTIFIER_LINE_2}\n`;
    csvContent += `PatientID:${finalPatientId}\n`;
    csvContent += `CreatedDate:${finalCreatedDate}\n`;
    csvContent += `ModifiedDate:${finalModifiedDate}\n`;
    csvContent += `FileID:${finalFileId}\n`;
    let returnData = [];
    
    if (identifier === Identifiers.LOCALIZATION) {
        const headers = ["Label", "ContactNumber", "ElectrodeDescription", "ContactDescription", "AssociatedLocation", "Mark", "SurgeonMark", "Type"];
        csvContent += headers.join(",") + "\n";
        
        Object.entries(data).forEach(([label, contacts]) => {
            const electrodeDescription = contacts.description;
            const electrodeType = contacts.type || 'DIXI'; // Default to DIXI if not specified
            Object.entries(contacts).forEach(([contactNumber, contactData]) => {
                // Skip the 'description' and 'type' keys, as they're not contacts
                if (contactNumber === 'description' || contactNumber === 'type') return;

                const {
                    contactDescription,
                    associatedLocation
                } = contactData;

                const row = [label, contactNumber, electrodeDescription, contactDescription, associatedLocation, 0, 0, electrodeType];
                csvContent += row.join(",") + "\n";
                
                if (!download) {
                    returnData.push({
                        Label: label,
                        ContactNumber: parseInt(contactNumber),
                        ElectrodeDescription: electrodeDescription,
                        AssociatedLocation: associatedLocation,
                        ContactDescription: contactDescription,
                        Mark: 0,
                        SurgeonMark: 0,
                        Type: electrodeType
                    });
                }
            });
        });
    }
    
    if (download) {
        const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
        const link = document.createElement("a");
        link.href = URL.createObjectURL(blob);
        link.download = "anatomy_" + new Date().toISOString().split('T')[0] + ".csv";
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    } else {
        switch (identifier) {
            case Identifiers.DESIGNATION: return parseDesignation(Papa.unparse(returnData));
            case Identifiers.STIMULATION: return parseStimulation(Papa.unparse(returnData));
        }
        return parseDesignation(Papa.unparse(returnData));
    }
}

/**
 * Saves a CSV file from data and downloads it or returns the data.
 * 
 * @param {Object[]} designationData - The data to be saved.
 * @param {Object[]} localizationData - The localization data to be used.
 * @param {string} patientId - The patient ID for the first line.
 * @param {string} createdDate - The created date for the first line.
 * @param {string} modifiedDate - The modified date for the first line.
 * @param {boolean} download - Whether to download the file or return the data.
 * @param {string} type - The type of designation data.
 * @returns {string} The CSV content.
 */
export function saveDesignationCSVFile(designationData, localizationData, patientId = '', createdDate = null, modifiedDate = null, download = true, type = 'designation', fileId = null) {
    const currentDate = new Date().toISOString();
    const finalPatientId = patientId || localizationData.patientId || '';
    const finalCreatedDate = createdDate || currentDate;
    const finalModifiedDate = modifiedDate || currentDate;
    const finalFileId = fileId || designationData.fileId || '';

    let csvContent = `${type === 'resection' ? Identifiers.RESECTION : Identifiers.DESIGNATION}\n${IDENTIFIER_LINE_2}\n`;
    csvContent += `PatientID:${finalPatientId}\n`;
    csvContent += `CreatedDate:${finalCreatedDate}\n`;
    csvContent += `ModifiedDate:${finalModifiedDate}\n`;
    csvContent += `FileID:${finalFileId}\n`;
    const headers = ["Label", "ContactNumber", "ElectrodeDescription", "ContactDescription", "AssociatedLocation", "Mark", "SurgeonMark", "Type"];
    csvContent += headers.join(",") + "\n";

    // Create a map of electrode contacts for quick lookup
    const contactMap = {};
    designationData.forEach(electrode => {
        contactMap[electrode.label] = electrode.contacts;
    });

    // Use localization data structure but include marks from designation
    Object.entries(localizationData).forEach(([label, contacts]) => {
        const electrodeDescription = contacts.description;
        const electrodeType = contacts.type || 'DIXI'; // Default to DIXI if not specified
        const designationContacts = contactMap[label] || [];

        Object.entries(contacts).forEach(([contactNumber, contactData]) => {
            // Skip the 'description' and 'type' keys
            if (contactNumber === 'description' || contactNumber === 'type') return;

            const {
                contactDescription,
                associatedLocation
            } = contactData;

            // Find corresponding designation contact
            const designationContact = designationContacts.find(c => c.index === parseInt(contactNumber));
            const mark = designationContact ? designationContact.mark : 0;
            const surgeonMark = designationContact ? (designationContact.surgeonMark ? 1 : 0) : 0;

            const row = [
                label,
                contactNumber,
                electrodeDescription,
                contactDescription,
                associatedLocation,
                mark,
                surgeonMark,
                electrodeType
            ];
            csvContent += row.join(",") + "\n";
        });
    });

    if (download) {
        const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
        const link = document.createElement("a");
        link.href = URL.createObjectURL(blob);
        link.download = `${type === 'resection' ? 'neurosurgery' : 'epilepsy'}_${new Date().toISOString().split('T')[0]}.csv`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    }

    return csvContent;
}

/**
 * Saves a CSV file from data and downloads it or returns the data.
 *
 * @param {Object[]} stimulationData - The data to be saved.
 * @param {string} planOrder - The plan order for the stimulation.
 * @param {string} type - The type of stimulation.
 * @param {string} patientId - The patient ID for the first line.
 * @param {string} createdDate - The created date for the first line.
 * @param {string} modifiedDate - The modified date for the first line.
 * @param {boolean} download - Whether to download the file or return the data.
 * @returns {string} The CSV content.
 */
export function saveStimulationCSVFile(stimulationData, planOrder, type = 'mapping', patientId = '', createdDate = null, modifiedDate = null, download = true, fileId = null) {
    const currentDate = new Date().toISOString();
    const finalPatientId = patientId || stimulationData.patientId || '';
    const finalCreatedDate = createdDate || currentDate;
    const finalModifiedDate = modifiedDate || currentDate;
    const finalFileId = fileId || stimulationData.fileId || '';

    let csvContent = '';
    switch(type) {
        case 'mapping':
            csvContent = `${Identifiers.STIMULATION_FUNCTION}\n${IDENTIFIER_LINE_2}\n`;
            break;
        case 'recreation':
            csvContent = `${Identifiers.STIMULATION_RECREATION}\n${IDENTIFIER_LINE_2}\n`;
            break;
        case 'ccep':
            csvContent = `${Identifiers.STIMULATION_CCEP}\n${IDENTIFIER_LINE_2}\n`;
            break;
        default:
            throw new Error('Invalid stimulation type');
    }
    
    csvContent += `PatientID:${finalPatientId}\n`;
    csvContent += `CreatedDate:${finalCreatedDate}\n`;
    csvContent += `ModifiedDate:${finalModifiedDate}\n`;
    csvContent += `FileID:${finalFileId}\n`;
    const headers = ["Label", "ContactNumber", "ElectrodeDescription", "ContactDescription", "AssociatedLocation", "Mark", "SurgeonMark", "Pair", "IsPlanning", "Frequency", "Duration", "Current", "PlanOrder", "Type"];
    csvContent += headers.join(",") + "\n";

    // Create a map of electrode contacts for quick lookup
    const contactMap = {};
    stimulationData.forEach(electrode => {
        contactMap[electrode.label] = electrode.contacts;
    });

    // Reconstruct the data
    const output = stimulationData.map(electrode => {
        return electrode.contacts.map(contact => {
            let order = contact.isPlanning ? planOrder.indexOf(contact.id) : -1;
            return [
                electrode.label,
                contact.index,
                contact.__electrodeDescription__,
                contact.__contactDescription__,
                contact.associatedLocation,
                contact.mark,
                contact.surgeonMark,
                contact.pair,
                contact.isPlanning,
                contact.frequency,
                contact.duration,
                contact.current,
                order,
                type
            ].join(",");
        }).join("\n");
    })

    csvContent += output.join("\n");

    if (download) {
        const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
        const link = document.createElement("a");
        link.href = URL.createObjectURL(blob);
        link.download = `stimulation_${type}_${new Date().toISOString().split('T')[0]}.csv`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    }

    return csvContent;
}

/**
 * Saves a CSV file from data and downloads it or returns the data.
 *
 * @param {Object[]} testData - The test data to be saved.
 * @param {Object[]} contacts - Contacts associated with tests.
 * @param {string} patientId - The patient ID for the first line.
 * @param {string} createdDate - The created date for the first line.
 * @param {string} modifiedDate - The modified date for the first line.
 * @param {boolean} download - Whether to download the file or return the data.
 * @returns {string} The CSV content.
 */
export function saveTestCSVFile(testData, contacts, patientId = '', createdDate = null, modifiedDate = null, download = true, fileId = null) {
    const currentDate = new Date().toISOString();
    const finalPatientId = patientId || '';
    const finalCreatedDate = createdDate || currentDate;
    const finalModifiedDate = modifiedDate || currentDate;
    const finalFileId = fileId || testData.fileId || '';

    let csvContent = `${Identifiers.TEST_PLANNING}\n${IDENTIFIER_LINE_2}\n`;
    csvContent += `PatientID:${finalPatientId}\n`;
    csvContent += `CreatedDate:${finalCreatedDate}\n`;
    csvContent += `ModifiedDate:${finalModifiedDate}\n`;
    csvContent += `FileID:${finalFileId}\n`;
    const headers = [
            "Label",
            "ContactNumber",
            "ElectrodeDescription",
            "ContactDescription",
            "AssociatedLocation",
            "Mark",
            "SurgeonMark",
            "Pair",
            "Frequency",
            "Duration",
            "Current",
            "TestID",
            "TestName",
            "IsPlanning"
        ];

    // Create CSV rows
    const rows = contacts.map(electrode => {
        return electrode.contacts.map(contact => {
            const contactTests = testData[contact.id] || [];
            if (contactTests.length === 0) {
                return [
                    electrode.label, // Label
                    contact.index, // ContactNumber
                    contact.__electrodeDescription__, // ElectrodeDescription
                    contact.__contactDescription__, // ContactDescription
                    contact.associatedLocation, // AssociatedLocation
                    contact.mark, // Mark
                    contact.surgeonMark, // SurgeonMark
                    contact.pair, // Pair
                    contact.frequency, // Frequency
                    contact.duration, // Duration
                    contact.current, // Current
                    "No test", // No test
                    "No test name", // No test name
                    contact.isPlanning // IsPlanning
                ].join(",");
            }
            return contactTests.map(test => {
                return [
                    electrode.label, // Label
                    contact.index, // ContactNumber
                    contact.__electrodeDescription__, // ElectrodeDescription
                    contact.__contactDescription__, // ContactDescription
                    contact.associatedLocation, // AssociatedLocation
                    contact.mark, // Mark
                    contact.surgeonMark, // SurgeonMark
                    contact.pair, // Pair
                    contact.frequency, // Frequency
                    contact.duration, // Duration
                    contact.current, // Current
                    test.id, // TestID
                    test.name || "", // TestName
                    contact.isPlanning // IsPlanning
                ].join(",");
            });
        }).join("\n");
    });

    // Combine headers and rows into CSV format
    csvContent += [
        headers.join(','), // Header row
        ...rows // Data rows
    ].join('\n');

    if (download) {
        const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
        const link = document.createElement("a");
        link.href = URL.createObjectURL(blob);
        link.download = "neuropsychology_" + new Date().toISOString().split('T')[0] + ".csv";
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    }

    return csvContent;
}