// ##################################################
// Fetching Files related
// Shared pure helpers live in scraper-utils.js (loaded before this file)
// ##################################################

// Helper function to get rental amount from invoice grid
function getRentalFromInvoiceGrid(basicRental) {
    const baseId = "ctl00_Content_Radgrid_Invoice_ctl00__";
    let rowIndex = 0;
    const basicRentalFloat = parseFloat(basicRental) || 0;
    
    while (true) {
        const rowElement = document.getElementById(baseId + rowIndex);
        if (!rowElement) {
            // No more rows found, return basic rental as fallback
            return basicRental;
        }
        
        try {
            // Assuming the rental amount is in a specific column (adjust index as needed)
            // You may need to adjust this based on your actual table structure
            const rentalCell = rowElement.children[4]; // Adjust column index as needed
            if (!rentalCell) {
                rowIndex++;
                continue;
            }
            
            const rentalText = rentalCell.textContent.trim();
            const rentalAmount = parseFloat(rentalText) || 0;
            
            if (rentalAmount > basicRentalFloat) {
                // Current row rental is more than basic, move to next row
                rowIndex++;
                continue;
            } else if (rentalAmount === basicRentalFloat) {
                // Same as basic rental, use basic rental
                return basicRental;
            } else {
                // Less than basic rental, use this value
                return rentalText;
            }
            
        } catch (error) {
            console.error(`Error processing invoice row ${rowIndex}:`, error);
            rowIndex++;
            continue;
        }
    }
}

// Main digger function
function diggerSingle() {
    const PREFIX = "ctl00_Content_";
    
    // DOM element mapping with efficient retrieval
    const elements = {
        order: utils.getElementById(`${PREFIX}lblBasic_Order`),
        vacc: utils.getElementById(`${PREFIX}txtBasic_VirtualAcc`),
        custName: utils.getElementById(`${PREFIX}txtBasic_CustName`),
        aging: utils.getElementById(`${PREFIX}txtTotalMthAging`),
        currentOSAmt: utils.getElementById(`${PREFIX}txtTotalCurrentOSAmt`),
        outstandingTotal: utils.getElementById(`${PREFIX}txtTotalOutstanding`),
        targetOSAmt: utils.getElementById(`${PREFIX}txtTargetOSAmt`),
        targetCollAmt: utils.getElementById(`${PREFIX}txtTargetCollAmt`),
        product: utils.getElementById(`${PREFIX}txtBasic_Product`),
        emergencyContact: utils.getElementById(`${PREFIX}txtEC_ContactName`),
        emergencyRelation: utils.getElementById(`${PREFIX}txtEC_Relation`),
        billingEmail: utils.getElementById(`${PREFIX}txtCC_BiillingEmail`),
        custIC: utils.getElementById(`${PREFIX}txtBasic_CustIC`),
        rental: utils.getElementById(`${PREFIX}txtBasic_Rental`),
        lastService: utils.getElementById(`${PREFIX}txtBasic_LastService`),
        installDate: utils.getElementById(`${PREFIX}txtBasic_InstDate`),
        contractEnd: utils.getElementById(`${PREFIX}txtContractEndDate`),
        rentPeriod: utils.getElementById(`${PREFIX}txtBasic_RentPeriod`),
        unbillAging: utils.getElementById(`${PREFIX}txtUnbillMthAging`),
        unbillAmt: utils.getElementById(`${PREFIX}txtUnbillAmt`),
        rentalScheme: utils.getElementById(`${PREFIX}txtBasic_RentalScheme`),
        billAddress: utils.getElementById(`${PREFIX}txtBasic_BillAddress`),
        instAddress: utils.getElementById(`${PREFIX}txtBasic_InstAddress`),
        staffcode: utils.getElementById(`${PREFIX}txtMemberCodeName`)
    };

    // Extract raw values with null checks
    const rawValues = {
        saleid: elements.order.innerText,
        vacc: elements.vacc.value,
        name: elements.custName.value,
        aging: elements.aging.value,
        oscurrent: elements.currentOSAmt.value,
        ostotal: elements.outstandingTotal.value,
        ostarget: elements.targetOSAmt.value,
        ospaid: elements.targetCollAmt.value,
        product: elements.product.value,
        emergencyname: elements.emergencyContact.value,
        emergencyrelation: elements.emergencyRelation.value,
        email: elements.billingEmail.value,
        nric: elements.custIC.value,
        monthly: elements.rental.value,
        lastservice: elements.lastService.value,
        installdate: elements.installDate.value,
        contractend: elements.contractEnd.value,
        obligation: elements.rentPeriod.value,
        unbillaging: elements.unbillAging.value,
        unbilltotal: elements.unbillAmt.value,
        scheme: elements.rentalScheme.value,
        addrmail: elements.billAddress.value,
        addrinst: elements.instAddress.value,
        staff: elements.staffcode.value
    };

    // Product type checking
    const isHealthProduct = (product) => {
        const healthProducts = new Set(['VITA', 'SERIES', 'BESPOKE', 'BEDFRAME']);
        return [...healthProducts].some(term => product.includes(term));
    };

    // Helper function to check if only the first element exists
    const isFirstElementOnly = () => {
        let baseId = "ctl00_Content_Radgrid_Payment_ctl00__";
        let firstElementExists = !!document.getElementById(baseId + "0"); // Check if the first element exists
        let otherElementsExist = false;

        // Loop through potential elements starting from index 1
        for (let i = 1; i < 100; i++) { // Assuming a reasonable limit (e.g., 100 rows)
            if (document.getElementById(baseId + i)) {
                otherElementsExist = true;
                break; // Exit early if any other element exists
            }
        }

        return firstElementExists && !otherElementsExist;
    };

    // Helper function to extract the Ref Date of the first payment history entry
    const getFirstPaymentRefDate = () => {
        try {
            // Select the first row of the payment history table
            const firstPaymentRow = document.getElementById("ctl00_Content_Radgrid_Payment_ctl00__0");
            if (!firstPaymentRow) {
                console.error("First payment row not found.");
                return null;
            }

            // Navigate to the Ref Date column using children method
            const refDateCell = firstPaymentRow.children[8]; // Adjust the index based on the actual DOM structure
            if (!refDateCell) {
                console.error("Ref Date cell not found in the first payment row.");
                return null;
            }

            // Extract and trim the Ref Date value
            const refDate = refDateCell.textContent.trim();
            if (!refDate) {
                console.error("Ref Date is empty or invalid.");
                return null;
            }

            return refDate; // Return the Ref Date as-is (assumed to be in DD/MM/YYYY format)
        } catch (error) {
            console.error("Error extracting Ref Date:", error);
            return null;
        }
    };

    // Helper function to calculate the difference in months between two dates
    const getLastPaymentDateDiff = () => {
        const firstPaymentDate = getFirstPaymentRefDate();
        if (!firstPaymentDate) return null;

        const parsedPaymentDate = utils.parseDate(firstPaymentDate);
        if (!parsedPaymentDate) return null;

        return utils.getMonthDiff(new Date(), parsedPaymentDate);
    };

    // Get the appropriate rental amount
    const basicRental = rawValues.monthly; // Keep as string for comparison
    const finalRental = getRentalFromInvoiceGrid(basicRental);

    // Construct optimized data object
    const data = {
        created: Date.now(),
        updated: "",
        visit: false,
        health: isHealthProduct(rawValues.product),
        fraud: isFirstElementOnly(), // Set fraud based on the existence check
        staff: elements.staffcode && elements.staffcode.value && elements.staffcode.value.includes("Active") ? true : false,
        saleid: rawValues.saleid.split(" ")[0],
        name: rawValues.name.includes("(BLACKLISTED)") 
            ? rawValues.name.replace("(BLACKLISTED) ", "") 
            : rawValues.name,
        nric: rawValues.nric,
        vacc: rawValues.vacc,
        blacklist: rawValues.name.includes("(BLACKLISTED)"),
        area: utils.processAddr(rawValues.addrinst),
        obligation: rawValues.obligation,
        scheme: rawValues.scheme,
        rental: parseFloat(finalRental) || 0, // Parse to float only when assigning to data
        ref: '',
        noemg: false,
        aging: {
            raw: UserProfile.team === "alpha" ? rawValues.aging : "5",
            flat: UserProfile.team === "alpha" ? (parseInt(rawValues.aging) || 0) : 5,
        },
        unbill: {
            aging: utils.parseAmount(rawValues.unbillaging),
            total: utils.parseAmount(rawValues.unbilltotal)
        },
        os: {
            current: utils.parseAmount(rawValues.oscurrent),
            total: utils.parseAmount(rawValues.ostotal),
            target: utils.parseAmount(rawValues.ostarget),
            paid: utils.parseAmount(rawValues.ospaid),
            payment: getFirstPaymentRefDate() // Add Ref Date of the first payment here
        },
        addr: {
            mail: rawValues.addrmail,
            inst: rawValues.addrinst,
        },
        product: rawValues.product,
        emergency: {
            name: rawValues.emergencyname,
            relation: rawValues.emergencyrelation,
        },
        email: rawValues.email.toLowerCase(),
        
        contact: diggerDigContact(),
        notes: [],
        date: {
            lastservice: rawValues.lastservice,
            install: rawValues.installdate,
            end: rawValues.contractend, // Ensure this is in DD/MM/YYYY format
            ptp: "",
            due: "",
            update: "",
        },
        broken: [],
        stage: {
            val: 1,
            ts: "",
        },
        float: 0.0,
        label: null, // user-assigned via Quick.setLabel; not scraped from the page
        cancel: (() => {
            const lastServiceDate = utils.parseDate(rawValues.lastservice);
            const lastPaymentDate = utils.parseDate(getFirstPaymentRefDate());

            if (!lastServiceDate || !lastPaymentDate) return false;

            const lastServiceDiff = utils.getMonthDiff(new Date(), lastServiceDate);
            const lastPaymentDiff = utils.getMonthDiff(new Date(), lastPaymentDate);

            return lastServiceDiff > 5 && lastPaymentDiff > 5;
        })()
    };

    return data;
}

/**
 * Lightweight payment data extractor for deep updates
 * Only extracts: os.paid, os.total, os.payment
 */
function diggerSinglePayment() {
    console.log('[diggerSinglePayment] Extracting payment data only...');

    // Helper function to extract the Ref Date of the first payment history entry
    const getFirstPaymentRefDate = () => {
        try {
            // Select the first row of the payment history table
            const firstPaymentRow = document.getElementById("ctl00_Content_Radgrid_Payment_ctl00__0");
            if (!firstPaymentRow) {
                console.error("First payment row not found.");
                return null;
            }

            // Navigate to the Ref Date column using children method
            const refDateCell = firstPaymentRow.children[8]; // Ref Date column
            if (!refDateCell) {
                console.error("Ref Date cell not found in the first payment row.");
                return null;
            }

            // Extract and trim the Ref Date value
            const refDate = refDateCell.textContent.trim();
            if (!refDate) {
                console.error("Ref Date is empty or invalid.");
                return null;
            }

            return refDate;
        } catch (error) {
            console.error("Error extracting Ref Date:", error);
            return null;
        }
    };

    // Get raw values
    const rawValues = {
        ospaid: document.getElementById("ctl00_Content_Label_PaidAmount")?.innerText || "0",
        ostotal: document.getElementById("ctl00_Content_Label_OSTotal")?.innerText || "0",
    };

    // Extract only payment-related fields
    const paymentData = {
        os: {
            paid: utils.parseAmount(rawValues.ospaid),
            total: utils.parseAmount(rawValues.ostotal),
            payment: getFirstPaymentRefDate()
        }
    };

    console.log('[diggerSinglePayment] Payment data extracted:', paymentData);
    return paymentData;
}


// Cache DOM prefix
const CONTACT_TABLE_ID = "ctl00_Content_RadGrid_Contact_ctl00";

/**
 * Main contact digger function
 * @returns {Object} Object containing default index and contact list
 */
function diggerDigContact() {
    const table = document.getElementById(CONTACT_TABLE_ID);
    if (!table || !table.rows || table.rows.length <= 1) {
        return { default: 0, list: [] };
    }

    // Use Array.from for better performance with map
    const contacts = Array.from(table.rows)
        .slice(1) // Skip header row
        .reduce((validContacts, row) => {
            const detail = row.cells[0]?.innerText;
            const contact = processContactDetail(detail);
            
            if (contact) {
                validContacts.push(contact);
            }
            
            return validContacts;
        }, []);

    return {
        default: 0,
        list: contacts
    };
}


// Constants for DOM IDs
const DOM_IDS = {
    TOTAL_TARGET: "ctl00_Content_txtTotalTarget",
    AGING_2: "ctl00_Content_txtAging2",
    AGING_3: "ctl00_Content_txtAging3",
    AGING_4: "ctl00_Content_txtAging4",
    AGING_5_UNBILL1: "ctl00_Content_txtAging5_Unbill1",
    AGING_5_UNBILL2: "ctl00_Content_txtAging5_Unbill2",
    SEARCH_BTN: "ctl00_Content_btnConfirmSearch_input",
    AGING_DROPDOWN: "ctl00_Content_cmbMthAging_DropDown",
    SALES_NO: "ctl00_Content_txtSalesNo",
    VAST_CHECKBOX: "vast"
};

/**
 * Get file count by aging and process data.
 */
function getFileCountByAging() {
    // Cache DOM elements to reduce reflow and improve performance
    const elements = {
        totalTarget: getDOMElement(DOM_IDS.TOTAL_TARGET),
        aging2: getDOMElement(DOM_IDS.AGING_2),
        aging3: getDOMElement(DOM_IDS.AGING_3),
        aging4: getDOMElement(DOM_IDS.AGING_4),
        aging5Unbill1: getDOMElement(DOM_IDS.AGING_5_UNBILL1),
        aging5Unbill2: getDOMElement(DOM_IDS.AGING_5_UNBILL2)
    };

    // Don't get total here - will get from .rgInfoPart at listing page
    const data = [];

    // Process based on team type
    if (UserProfile.team === "alpha") {
        const agingCounts = {
            2: parseNumberFromHTML(elements.aging2),
            3: parseNumberFromHTML(elements.aging3),
            4: parseNumberFromHTML(elements.aging4)
        };

        // Add only non-zero counts to data array
        Object.entries(agingCounts).forEach(([aging, count]) => {
            if (count > 0) {
                data.push({ aging: parseInt(aging), total: count, done: false });
            }
        });

        if (data.length === 0) {
            RunBlocker.hide();
            Snackbar.show("This CA is not ALPHA...");
            return;
        }
    } else {
        // For bravo team, we'll get total from listing page
        data.push({ aging: 5, total: 0, done: false });
    }

    // Update collected information
    const info = mydb.info;
    const collected = info.collected;

    if (UserProfile.team === "alpha") {
        collected.a2 = parseCollectedAmount(elements.aging2);
        collected.a3 = parseCollectedAmount(elements.aging3);
        collected.a4 = parseCollectedAmount(elements.aging4);
    } else {
        collected.a5a = parseCollectedAmount(elements.aging5Unbill1);
        collected.a5b = parseCollectedAmount(elements.aging5Unbill2);
    }

    collected.aoa = parseCollectedAmount(elements.totalTarget);

    // Efficiently update database
    mydb.info = { ...info, collected };
    mydb.updated = new Date();
    mydb.persist();

    // Set memory and redirect - total will be fetched from .rgInfoPart at listing page
    Memory.put({
        page: "listing",
        action: "fetchAllSid",
        meta: data,
        toCreate: []
    });

    location.href = Router.getPageUrl("listing");
}

// Optimized fetch function
/**
 * Fetch all SIDs by aging.
 */
function fetchAllSidByAging() {
    const elements = {
        searchBtn: getDOMElement(DOM_IDS.SEARCH_BTN),
        agingDropdown: getDOMElement(DOM_IDS.AGING_DROPDOWN)
    };

    if (!elements.searchBtn || !elements.agingDropdown) {
        console.error("Required DOM elements not found");
        RunBlocker.hide();
        return;
    }

    const getAgingButton = (index) => {
        try {
            return elements.agingDropdown.children[0]?.children[1]?.children[index]?.children[0] || null;
        } catch (err) {
            console.error(`Failed to get aging button at index ${index}:`, err);
            return null;
        }
    };

    const meta = Memory.fetch("meta");
    if (!meta || !meta[0]) {
        console.error("No meta data available");
        RunBlocker.hide();
        return;
    }

    const agingTarget = meta[0].aging;

    const agingButtonMap = {
        2: 0,
        3: 1,
        4: 2,
        5: 3
    };

    const buttonIndex = agingButtonMap[agingTarget];
    const targetButton = getAgingButton(buttonIndex);

    if (!targetButton) {
        console.error("Target aging button not found");
        RunBlocker.hide();
        return;
    }

    agingClickHandler(targetButton, elements.searchBtn);
}

function agingClickHandler(agingBtn, searchBtn) {
    const meta = Memory.fetch("meta");
    const total = Memory.fetch("total");
    const toCreate = Memory.fetch("toCreate");

    if (!meta || !meta[0]) {
        console.error("Invalid memory data");
        RunBlocker.hide();
        return;
    }

    const firstMeta = meta[0];

    if (firstMeta.done) {
        handleDoneState(agingBtn, { meta, total, toCreate });
    } else {
        handlePendingState(agingBtn, searchBtn, firstMeta);
    }
}

function handleDoneState(agingBtn, memoryData) {
    // Create a shallow copy of meta and remove the first item
    const newMeta = memoryData.meta.slice(1); // Use slice for immutability

    // Trigger the aging button click
    agingBtn.click();

    // Check if there are no more items in meta
    if (newMeta.length === 0) {
        ProgBar.show("finish fetching files id...", 100, 100);

        // Update memory and trigger mass saving operations
        Memory.put({
            page: "listing",
            action: "mso",
            meta: memoryData.toCreate,
            total: memoryData.total
        });
        massSavingOps();
        return;
    }

    // Update memory and fetch the next set of SIDs
    Memory.put({
        page: "listing",
        action: "fetchAllSid",
        meta: newMeta,
        total: memoryData.total,
        toCreate: memoryData.toCreate
    });
    fetchAllSidByAging();
}

function handlePendingState(agingBtn, searchBtn, metaItem) {
    agingBtn.click(); // Always click aging button

        searchBtn.click(); // Only click search for 2, 3, 4
    
    beginWaitByAgingLoad(metaItem.total, metaItem.aging);
}

// Optimized mass saving operations
/**
 * Perform mass saving operations.
 */
async function massSavingOps() {
    try {
        // Fetch meta data from memory
        const meta = Memory.fetch("meta");

        // Validate meta data
        if (!meta || !Array.isArray(meta)) {
            throw new Error("Invalid meta data");
        }

        // Check if meta array is empty or first item is undefined
        if (meta.length === 0 || !meta[0]) {
            console.log("All items processed. Initiating finalization...");
            await finalizeOperations();
            return;
        }

        // Cache required DOM elements once
        const salesInput = getDOMElement(DOM_IDS.SALES_NO);
        const searchBtn = getDOMElement(DOM_IDS.SEARCH_BTN);

        // Validate DOM elements
        if (!salesInput || !searchBtn) {
            throw new Error("Required DOM elements not found");
        }

        // Check if meta[0] exists before destructuring
        if (!meta[0]) {
            console.log("meta[0] is undefined. All items processed. Initiating finalization...");
            await finalizeOperations();
            return;
        }

        // Process the first item in meta
        const { saleid } = meta[0];
        salesInput.value = saleid; // Set the sales input value
        searchBtn.click();         // Trigger the search button click

        // Wait for the operation to complete
        await msowait(saleid);
    } catch (error) {
        console.error("Mass saving operations failed:", error);

        // Check if error is due to empty meta (processing complete)
        const meta = Memory.fetch("meta");
        if (!meta || meta.length === 0 || !meta[0]) {
            console.log("Processing complete due to empty meta. Initiating finalization...");
            await finalizeOperations();
        } else {
            // Genuine failure mid-run — release the click blocker so the page is usable
            RunBlocker.hide();
        }
    }
}

async function finalizeOperations() {
    try {
        console.log("initiating finalization...");

        // Set toolbar state
        sessionStorage.setItem("toolbarshow", "false");

        // Update progress bar
        ProgBar.show("finish saving files...", 100, 100);

        // Handle checkbox
        const vastCheckbox = getDOMElement(DOM_IDS.VAST_CHECKBOX);
        if (vastCheckbox) {
            vastCheckbox.checked = false;
        }

        // Auto-save initial data to Firebase
        console.log("☁️ Saving initial data to Firebase...");
        try {
            await FirebaseFirestore.saveData(true);
            console.log("✅ Initial data saved to Firebase");

            // Show success alert
            alert("✅ Files completed save! Initial data synced to cloud ☁️");
        } catch (firebaseError) {
            console.error("Firebase save failed:", firebaseError);
            // Still show success for local save, but warn about cloud sync
            alert("✅ Files saved locally!\n⚠️ Cloud sync failed - please click 'Save Data' manually to sync.");
        }

        // After user clicks OK, clear memory
        Memory.clear();

        // fea Sync+Remark (gufcba with new files): start remark phase after sync
        // (self-gating: syncremark mode or a preserved remarkQueueConfig from Modal C)
        if (startRemarkQueuePhase()) {
            return; // remark phase handles navigation from here
        }

        // Clear fetch-mode flags
        localStorage.removeItem("fetchMode");
        localStorage.removeItem("remarkConfig");
        localStorage.removeItem("remarkQueueConfig");
        localStorage.removeItem("glConfig");

        // Close window if opened from another window (e.g., after gfcba with diggerSingle)
        if (window.opener && !window.opener.closed) {
            window.close();
            return;
        }

        // Otherwise redirect to home page
        location.href = Router.getPageUrl("home");

        console.log("finalization completed successfully!");

    } catch (error) {
        console.error("Finalization failed:", error);
    }
}

// Cache DOM selectors for better performance
const DOM_SELECTORS = {
    SALES_GRID: "ctl00_Content_RadGrid_Sales_ctl00",
    PAGE_SIZE: {
        COMBO: "ctl00_Content_RadGrid_Sales_ctl00_ctl03_ctl01_PageSizeComboBox",
        ARROW: "ctl00_Content_RadGrid_Sales_ctl00_ctl03_ctl01_PageSizeComboBox_Arrow",
        DROPDOWN: "ctl00_Content_RadGrid_Sales_ctl00_ctl03_ctl01_PageSizeComboBox_DropDown"
    }
};


async function msowait(target) {
    const CLICKED_KEY = `msowait_clicked_${target}`; // ✅ Moved to top
    localStorage.setItem(CLICKED_KEY, "false");

    try {
        // Helper function to check if the target exists in the table
        const checkTableForTarget = (table, targetId) => {
            if (!table?.rows?.[1]?.cells) return null;

            const link = table.rows[1].cells[0]?.getElementsByTagName("input")[0];
            const cid = table.rows[1].cells[2]?.innerText.trim();
            return cid === targetId ? link : null;
        };

        // Check if this target has already been clicked
        if (localStorage.getItem(CLICKED_KEY) === "true") {
            console.log(`Already clicked for target: ${target}`);
            return;
        }

        // Polling function to wait for the target
        const pollForTarget = async (targetId, maxAttempts = 100, delayMs = 200) => {
            let attempts = 0;

            while (attempts < maxAttempts) {
                const table = document.getElementById(DOM_SELECTORS.SALES_GRID);
                const link = checkTableForTarget(table, targetId);

                if (link) {
                    // Update memory and trigger actions when the target is found
                    Memory.put({
                        page: "single",
                        action: "save",
                        meta: Memory.fetch("meta"),
                        total: Memory.fetch("total")
                    });
                    sessionStorage.setItem("toolbarshow", JSON.stringify(true));
                    
                    // Mark as clicked before click to avoid race condition
                    localStorage.setItem(CLICKED_KEY, "true");

                    await delay(50); // Small delay before clicking the link
                    link.click();
                    return; // Exit the loop once the target is handled
                }

                await delay(delayMs); // Wait before retrying
                attempts++;
            }

            throw new Error(`Timeout waiting for target ${targetId}`);
        };

        // Start polling for the target
        await pollForTarget(target);
    } catch (error) {
        console.error("Error in msowait:", error.message);
        // Ensure toolbar stays visible even on error
        sessionStorage.setItem("toolbarshow", JSON.stringify(true));
    }
}

// Optimized beginWaitByAgingLoad function
async function beginWaitByAgingLoad(filesum, aging) {
    const maxAttempts = 1000; // Prevent infinite loops
    let attempts = 0;
    const totalFiles = Memory.fetch('total'); // Cache static value

    while (attempts++ < maxAttempts) {
        try {
            // DOM element queries (potential CPU hotspot)
            const cfsumElement = document.querySelector(".rgInfoPart strong");
            const salesGrid = document.getElementById(DOM_SELECTORS.SALES_GRID);
            
            // Element validation
            if (!cfsumElement || !salesGrid) {
                await delay(50);
                continue;
            }

            // State checks
            const currentSum = parseInt(cfsumElement.innerText);
            const currentElements = salesGrid.children[2]?.childElementCount || 0;

            // Conditional flow optimization
            if (currentSum !== filesum) {
                await delay(50);
                continue;
            }

            // Page size handling
            const pageSizeComboBox = document.getElementById(DOM_SELECTORS.PAGE_SIZE.COMBO);
            const comboBoxValue = parseInt(pageSizeComboBox?.children[0]?.children[0]?.value) || 0;

            if (comboBoxValue < 50) {
                const pageSizeArrow = document.getElementById(DOM_SELECTORS.PAGE_SIZE.ARROW);
                const pageSizeDropDown = document.getElementById(DOM_SELECTORS.PAGE_SIZE.DROPDOWN);
                
                if (pageSizeArrow && pageSizeDropDown) {
                    pageSizeArrow.click();
                    await delay(50);
                    pageSizeDropDown.children[0].children[0].children[2].click();
                    await delay(50);
                    continue;
                }
            }

            // Elements count check
            if (currentElements < 11 && currentElements < currentSum) {
                await delay(50);
                continue;
            }

            // Conflict check
            const cid = salesGrid.children[2]?.children[0]?.cells[2]?.innerText;
            const toCreate = Memory.fetch('toCreate') || [];
            if (toCreate.some(record => record.saleid === cid)) {
                await delay(50);
                continue;
            }

            // Exit condition met
            break;
        } catch (error) {
            console.error("Optimized function error:", error.message);
            await delay(50);
        }
    }

    // Final execution
    const toCreate = Memory.fetch('toCreate') || [];
    ProgBar.show('fetching files id...', toCreate.length, totalFiles);
    syncProcedure(null, null, aging);
}

// Optimized syncProcedure function
async function syncProcedure(toCreate = null, page = null, aging = null) {
    try {
        // Get aging from memory if not provided
        if (aging === null) {
            const meta = Memory.fetch('meta');
            aging = meta && meta[0] ? meta[0].aging : null;
        }

        const elements = {
            rgInfoPart: document.getElementsByClassName("rgInfoPart")[0],
            rgCurrentPage: document.getElementsByClassName("rgCurrentPage")[0],
            table: document.getElementById(DOM_SELECTORS.SALES_GRID),
            nextPage: document.getElementsByClassName("rgPageNext")[0],
            firstPage: document.getElementsByClassName("rgPageFirst")[0]
        };

        if (!elements.rgInfoPart || !elements.table) {
            throw new Error("Required elements not found");
        }

        const mpage = parseInt(elements.rgInfoPart.getElementsByTagName("strong")[1].innerText);
        const cpage = parseInt(elements.rgCurrentPage.innerText);
        const totalitem = parseInt(elements.rgInfoPart.getElementsByTagName("strong")[0].innerText);

        // Update Memory with actual total from .rgInfoPart if not set yet
        if (!Memory.fetch('total')) {
            const currentMemory = Memory.fetch();
            Memory.put({
                ...currentMemory,
                total: totalitem
            });
        }

        // Initialize or use existing toCreate array
        let records = toCreate || [];
        
        // Check if we need to wait for page change
        if (page !== null && cpage !== page) {
            await delay(50);
            return syncProcedure(records, page, aging);
        }

        // Create Set for efficient lookups
        const existingIds = new Set(records.map(val => val.saleid));
        
        // Process current page
        Array.from(elements.table.rows).slice(1).forEach(row => {
            const saleid = row.cells[2].innerText;
            if (!existingIds.has(saleid)) {
                // Reason column (cell 12): blank (&nbsp;) = not yet remarked in CCMS.
                // gfcba remark modes skip files that already have a Reason.
                const reasonText = row.cells[12] ? (row.cells[12].innerText || "").trim() : "";
                records.push({ aging, saleid, reasonBlank: reasonText === "" });
                ProgBar.show(null, records.length, Memory.fetch("total"));
            }
        });

        // Check if we've collected all items
        if (records.length === totalitem) {
            mymemory.data.meta[0].done = true;
            mymemory.data.toCreate = Memory.fetch("toCreate") === undefined 
                ? records 
                : [...Memory.fetch("toCreate"), ...records];
            mymemory.persist();
            return fetchAllSidByAging();
        }

        // Handle pagination
        if (cpage < mpage) {
            elements.nextPage.click();
            await delay(50);
            return syncProcedure(records, cpage + 1, aging);
        } else {
            elements.firstPage.click();
            await delay(50);
            return syncProcedure(records, 1, aging);
        }
        
    } catch (error) {
        console.error("Error in syncProcedure:", error.message);
        await delay(100);
        return syncProcedure(toCreate, page, aging);
    }
}

async function updateProcedure(toUpdate = null, page = null, count = null, removed = 0) {
    if (!Router.isCurrentPage("listing")) {
        Memory.put({
            page: "listing",
            action: "bup",
            meta: { toUpdate, page, count, removed },
        });
        location.href = Router.getPageUrl("listing");
        return;
    }

    if (!check50({ toUpdate, page, count, removed })) return;

    let currentPage = parseInt(document.querySelector(".rgCurrentPage").innerText);
    const maxPage = parseInt(document.querySelector(".rgInfoPart strong:nth-child(2)").innerText);

    if (!toUpdate) {
        toUpdate = new Set(CMFile.all({ verb: "only", key: "saleid" }));
    } else if (page !== null && currentPage !== page) {
        await delay(50);
        return updateProcedure(toUpdate, page, count, removed);
    }

    if (toUpdate.size === removed) {
        toUpdate.forEach(saleid => {
            const data = CMFile.give(saleid);
            data.removed = true;
            CMFile.update(saleid, data);
        });

        sessionStorage.setItem("toolbarshow", JSON.stringify(false));
        Snackbar.show(`Finish update, ${count} files updated, ${removed} file(s) removed..`);
        Memory.clear();
        console.log("updateproc");

        // fea Sync+Remark: after sync, open N files and remark them one by one
        if (startRemarkQueuePhase()) {
            return; // remark phase handles navigation from here
        }

        await delay(250);
        location.reload();
        return;
    }

    const table = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00");
    for (let i = 1; i < table.rows.length; i++) {
        const saleid = table.rows[i].cells[2].innerText;

        if (toUpdate.size > 0 && toUpdate.has(saleid)) {
            const record = CMFile.give(saleid);
            const paid = parseFloat(table.rows[i].cells[9].innerText);
            const total = parseFloat(table.rows[i].cells[6].innerText) + parseFloat(table.rows[i].cells[8].innerText);

            if (record.os.paid !== paid) {
                record.os.paid = paid;
                record.os.total = total;

                if (record.float) {
                    const t = record.float + record.os.paid;
                    if (t <= 0) {
                        record.float = 0;
                    }
                }

                CMFile.update(saleid, record);
                count++;
                console.log(`${saleid} UPDATED!`);
            }

            toUpdate.delete(saleid);
            const remainingFiles = CMFile.all().length - toUpdate.size;
            ProgBar.show(`${remainingFiles} files updated...`, remainingFiles, CMFile.all().length);
            console.log(`${toUpdate.size} files left`);
        }
    }

    if (toUpdate.size > 0) {
        if (currentPage < maxPage) {
            document.querySelector(".rgPageNext").click();
            await delay(50);
            return updateProcedure(toUpdate, ++currentPage, count, removed);
        } else {
            document.querySelector(".rgPageFirst").click();
            await delay(50);
            return updateProcedure(toUpdate, 1, count, removed);
        }
    } else {
        mydb.updated = new Date();
        mydb.persist();
        Snackbar.show(`${count} files updated`);
        Memory.clear();
        await delay(50);
        location.reload();
    }
}

function check50(opt) {
	let cpage, mpage;
	mpage = parseInt(document.getElementsByClassName("rgInfoPart")[0].getElementsByTagName("strong")[1].innerText);
	cpage = parseInt(document.getElementsByClassName("rgCurrentPage")[0].innerText);
	let clen = parseInt(
		document.getElementById("ctl00_Content_RadGrid_Sales_ctl00_ctl03_ctl01_PageSizeComboBox").children[0].children[0]
			.value,
	);
	if (clen < 50) {
		document.getElementById("ctl00_Content_RadGrid_Sales_ctl00_ctl03_ctl01_PageSizeComboBox_Arrow").click();
		document
			.getElementById("ctl00_Content_RadGrid_Sales_ctl00_ctl03_ctl01_PageSizeComboBox_DropDown")
			.children[0].children[0].children[2].click();
		setTimeout(function () {
			updateProcedure(opt.toUpdate, opt.page, opt.count, opt.removed);
		}, 50);
		return false;
	} else {
		x = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00");

		if (x.children[2].childElementCount > 10) {
			return true;
		} else if (cpage === mpage) {
			console.log("Its max page to go for update");
			return true;
		}
		setTimeout(function () {
			updateProcedure(opt.toUpdate, opt.page, opt.count, opt.removed);
		}, 50);
		return false;
	}
}

function gfcbau() {
	// Fresh gufcba run: reset the remark-queue listing order — fasbau re-scrapes
	// it from the unfiltered main list when Sync+Remark is pending.
	localStorage.removeItem("listingOrder");
	sessionStorage.removeItem("listingOrderScraped");

	let total;
	try {
		// Attempt to parse the total value
		total = parseInt(
			document.getElementById("ctl00_Content_txtTotalTarget").innerHTML.split(" <br> ")[1].split(" : ")[1],
		);
	} catch (err) {
		// Show an alert if the element or value is not available
		RunBlocker.hide();
		alert("Cuckoo system is not yet updated");
		Memory.clear();
		return; // Exit the function early
	}
	let data = [];
	let info = mydb.info;
	let collected = info.collected;

	if (UserProfile.team === "alpha") {
		let aging2Element = document.getElementById("ctl00_Content_txtAging2");
		let aging3Element = document.getElementById("ctl00_Content_txtAging3");
		let aging4Element = document.getElementById("ctl00_Content_txtAging4");

		try {
			let a2 = parseInt(aging2Element.innerHTML.split(" <br> ")[1].split(" : ")[1]);
			let a3 = parseInt(aging3Element.innerHTML.split(" <br> ")[1].split(" : ")[1]);
			let a4 = parseInt(aging4Element.innerHTML.split(" <br> ")[1].split(" : ")[1]);

			if (a2 > 0) data.push({ aging: 2, total: a2, done: false });
			if (a3 > 0) data.push({ aging: 3, total: a3, done: false });
			if (a4 > 0) data.push({ aging: 4, total: a4, done: false });

			collected.a2 = parseFloat(
				aging2Element.innerHTML
					.split(" <br> ")[0]
					.split(" : ")[1]
					.split(" / ")[0]
					.replace(" RM ", "")
					.replace(",", ""),
			);
			collected.a3 = parseFloat(
				aging3Element.innerHTML
					.split(" <br> ")[0]
					.split(" : ")[1]
					.split(" / ")[0]
					.replace(" RM ", "")
					.replace(",", ""),
			);
			collected.a4 = parseFloat(
				aging4Element.innerHTML
					.split(" <br> ")[0]
					.split(" : ")[1]
					.split(" / ")[0]
					.replace(" RM ", "")
					.replace(",", ""),
			);
		} catch (err) {
			console.log(err.message);
			RunBlocker.hide();
			Snackbar.show("This CA is not ALPHA...");
			return;
		}
	} else {
		// For Bravo team - DON'T set total yet, will get from .rgInfoPart at listing page
		let aging5Element = document.getElementById("ctl00_Content_txtAging5");

		try {
			// Don't use total here for Bravo - set to 0 as placeholder
			data.push({ aging: 5, total: 0, done: false });

			collected.a5a = parseFloat(
				aging5Element.innerHTML
					.split(" <br> ")[0]
					.split(" : ")[1]
					.split(" / ")[0]
					.replace(" RM ", "")
					.replace(",", ""),
			);
			collected.a5b = parseFloat(
				aging5Element.innerHTML
					.split(" <br> ")[0]
					.split(" : ")[1]
					.split(" / ")[0]
					.replace(" RM ", "")
					.replace(",", ""),
			);
		} catch (err) {
			//
		}
	}

	collected.aoa = parseFloat(
		document
			.getElementById("ctl00_Content_txtTotalTarget")
			.innerHTML.split(" <br> ")[0]
			.split(" : ")[1]
			.split(" / ")[0]
			.replace("RM ", "")
			.replace(",", ""),
	);
	info.collected = collected;
	mydb.info = info;
	mydb.updated = new Date();
	mydb.persist();

	Memory.put({
		page: "listing",
		action: "fetchAllSidUpdate",
		meta: data,
		total: UserProfile.team === "alpha" ? parseInt(total) : 0, // Set 0 for Bravo, will update from .rgInfoPart
		toUpdate: [],
	});

	location.href = Router.getPageUrl("listing");
}

function gdfcbau() {
	// Deep update - gets aging counts, then opens files one by one
	// Don't get total here - will get from .rgInfoPart at listing page to avoid mismatch/stuck loop
	let data = [];
	let info = mydb.info;
	let collected = info.collected;

	if (UserProfile.team === "alpha") {
		let aging2Element = document.getElementById("ctl00_Content_txtAging2");
		let aging3Element = document.getElementById("ctl00_Content_txtAging3");
		let aging4Element = document.getElementById("ctl00_Content_txtAging4");

		try {
			let a2 = parseInt(aging2Element.innerHTML.split(" <br> ")[1].split(" : ")[1]);
			let a3 = parseInt(aging3Element.innerHTML.split(" <br> ")[1].split(" : ")[1]);
			let a4 = parseInt(aging4Element.innerHTML.split(" <br> ")[1].split(" : ")[1]);

			if (a2 > 0) data.push({ aging: 2, total: a2, done: false });
			if (a3 > 0) data.push({ aging: 3, total: a3, done: false });
			if (a4 > 0) data.push({ aging: 4, total: a4, done: false });

			collected.a2 = parseFloat(
				aging2Element.innerHTML
					.split(" <br> ")[0]
					.split(" : ")[1]
					.split(" / ")[0]
					.replace(" RM ", "")
					.replace(",", ""),
			);
			collected.a3 = parseFloat(
				aging3Element.innerHTML
					.split(" <br> ")[0]
					.split(" : ")[1]
					.split(" / ")[0]
					.replace(" RM ", "")
					.replace(",", ""),
			);
			collected.a4 = parseFloat(
				aging4Element.innerHTML
					.split(" <br> ")[0]
					.split(" : ")[1]
					.split(" / ")[0]
					.replace(" RM ", "")
					.replace(",", ""),
			);
		} catch (err) {
			console.log(err.message);
			RunBlocker.hide();
			Snackbar.show("This CA is not ALPHA...");
			return;
		}
	} else {
		let aging5Element = document.getElementById("ctl00_Content_txtAging5");

		try {
			// For bravo team, we'll get total from listing page
			data.push({ aging: 5, total: 0, done: false });

			collected.a5a = parseFloat(
				aging5Element.innerHTML
					.split(" <br> ")[0]
					.split(" : ")[1]
					.split(" / ")[0]
					.replace(" RM ", "")
					.replace(",", ""),
			);
			collected.a5b = parseFloat(
				aging5Element.innerHTML
					.split(" <br> ")[0]
					.split(" : ")[1]
					.split(" / ")[0]
					.replace(" RM ", "")
					.replace(",", ""),
			);
		} catch (err) {
			//
		}
	}

	collected.aoa = parseFloat(
		document
			.getElementById("ctl00_Content_txtTotalTarget")
			.innerHTML.split(" <br> ")[0]
			.split(" : ")[1]
			.split(" / ")[0]
			.replace("RM ", "")
			.replace(",", ""),
	);
	info.collected = collected;
	mydb.info = info;
	mydb.updated = new Date();
	mydb.persist();

	// Don't set total here - will be fetched from .rgInfoPart at listing page
	Memory.put({
		page: "listing",
		action: "fetchAllSidDeepUpdate",
		meta: data,
		toUpdate: [],
	});

	location.href = Router.getPageUrl("listing");
}

function generateLettersBatch() {
	// Get total from .rgInfoPart (single source of truth)
	const rgInfoPart = document.getElementsByClassName("rgInfoPart")[0];
	if (!rgInfoPart) {
		alert("Collection list not loaded yet. Please wait for the page to load.");
		Memory.clear();
		return;
	}

	const totalFromRgInfo = parseInt(rgInfoPart.getElementsByTagName("strong")[0].innerText);

	// Get the sales grid table (same as used in fetch operations)
	const salesGrid = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00");

	if (!salesGrid) {
		alert("Sales table not found. Please make sure you're on the collection list page.");
		Memory.clear();
		return;
	}

	// Extract sale IDs from all rows
	const saleIds = [];
	for (let i = 1; i < salesGrid.rows.length; i++) {
		const saleIdCell = salesGrid.rows[i].cells[2];
		const linkCell = salesGrid.rows[i].cells[0]?.getElementsByTagName("a")[0];

		if (saleIdCell && linkCell) {
			const saleid = saleIdCell.innerText.trim();
			saleIds.push({
				saleid: saleid,
				link: linkCell,
				done: false
			});
		}
	}

	if (saleIds.length === 0) {
		alert("No sale IDs found in collection list.");
		Memory.clear();
		return;
	}

	console.log(`Found ${saleIds.length} files, total from .rgInfoPart: ${totalFromRgInfo}`);

	// Store in memory for processing
	Memory.put({
		page: "single",
		action: "downloadLetter",
		meta: saleIds,
		currentIndex: 0,
		total: totalFromRgInfo
	});

	// Click first link to start the process
	ProgBar.show(`Generating letters... (0/${saleIds.length})`, 0, saleIds.length);
	saleIds[0].link.click();
}

function downloadLetterFromPage() {
	const memData = Memory.fetch();

	if (!memData || memData.action !== "downloadLetter") {
		return;
	}

	const currentIndex = memData.currentIndex || 0;
	const saleIds = memData.meta;
	const total = memData.total;

	// Update progress
	ProgBar.show(`Generating letters... (${currentIndex + 1}/${total})`, currentIndex + 1, total);

	// Trigger the letter download
	(async () => {
		try {
			const formData = new FormData();

			// Add required form fields
			formData.append('__EVENTTARGET', 'ctl00$Content$btnGenerateLetter');
			formData.append('__EVENTARGUMENT', '');

			const viewState = document.querySelector('input[name="__VIEWSTATE"]');
			if (viewState) {
				formData.append('__VIEWSTATE', viewState.value);
			}

			const viewStateGenerator = document.querySelector('input[name="__VIEWSTATEGENERATOR"]');
			if (viewStateGenerator) {
				formData.append('__VIEWSTATEGENERATOR', viewStateGenerator.value);
			}

			const eventValidation = document.querySelector('input[name="__EVENTVALIDATION"]');
			if (eventValidation) {
				formData.append('__EVENTVALIDATION', eventValidation.value);
			}

			// Add other hidden inputs
			const existingForm = document.querySelector('form');
			if (existingForm) {
				const inputs = existingForm.querySelectorAll('input[type="hidden"]:not([name="__EVENTTARGET"]):not([name="__EVENTARGUMENT"])');
				inputs.forEach(input => {
					if (!['__VIEWSTATE', '__VIEWSTATEGENERATOR', '__EVENTVALIDATION'].includes(input.name)) {
						formData.append(input.name, input.value);
					}
				});
			}

			// Make the request
			const response = await fetch(window.location.href, {
				method: 'POST',
				body: formData
			});

			// Get filename from response headers
			const contentDisposition = response.headers.get('content-disposition');
			let filename = `letter_${saleIds[currentIndex].saleid}.pdf`;
			if (contentDisposition) {
				const filenameMatch = contentDisposition.match(/filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/);
				if (filenameMatch && filenameMatch[1]) {
					filename = filenameMatch[1].replace(/['"]/g, '');
				}
			}

			const blob = await response.blob();

			// Create download link and click it automatically
			const url = window.URL.createObjectURL(blob);
			const a = document.createElement('a');
			a.href = url;
			a.download = filename;
			a.style.display = 'none';
			document.body.appendChild(a);
			a.click();
			document.body.removeChild(a);
			window.URL.revokeObjectURL(url);

			console.log('File downloaded:', filename);

			// Mark current as done and move to next
			saleIds[currentIndex].done = true;
			const nextIndex = currentIndex + 1;

			if (nextIndex < total) {
				// Update memory with next index
				Memory.put({
					page: "single",
					action: "downloadLetter",
					meta: saleIds,
					currentIndex: nextIndex,
					total: total
				});

				// Go back to collection list
				setTimeout(() => {
					window.history.back();
				}, 1000);
			} else {
				// All done
				ProgBar.show("Finished generating all letters!", total, total);
				Memory.clear();
				setTimeout(() => {
					window.history.back();
				}, 2000);
			}
		} catch (error) {
			console.error('Error downloading letter:', error);
			alert('Error generating letter. Process will continue with next file.');

			// Continue with next file even on error
			const nextIndex = currentIndex + 1;
			if (nextIndex < total) {
				Memory.put({
					page: "single",
					action: "downloadLetter",
					meta: saleIds,
					currentIndex: nextIndex,
					total: total
				});
				setTimeout(() => {
					window.history.back();
				}, 1000);
			} else {
				Memory.clear();
			}
		}
	})();
}

function fasbauDeep() {
	let schBtn = document.getElementById("ctl00_Content_btnConfirmSearch_input");
	let ag2Btn, ag3Btn, ag4Btn, ag5Btn;
	if (UserProfile.team === "alpha") {
		ag2Btn = document.getElementById("ctl00_Content_cmbMthAging_DropDown").children[0].children[1].children[0]
			.children[0];
		ag3Btn = document.getElementById("ctl00_Content_cmbMthAging_DropDown").children[0].children[1].children[1]
			.children[0];
		ag4Btn = document.getElementById("ctl00_Content_cmbMthAging_DropDown").children[0].children[1].children[2]
			.children[0];
	} else {
		ag5Btn = document.getElementById("ctl00_Content_cmbMthAging_DropDown").children[0].children[1].children[3]
			.children[0];
	}

	let agtarget = Memory.fetch("meta")[0].aging;
	switch (agtarget) {
		case 2:
			var abtn = ag2Btn;
			break;
		case 3:
			var abtn = ag3Btn;
			break;
		case 4:
			var abtn = ag4Btn;
			break;
		default:
			var abtn = ag5Btn;
			break;
	}

	if (Memory.fetch("meta")[0].done) {
		const newmeta = Memory.fetch("meta").filter((item) => item.done === false);

		if (newmeta.length === 0) {
			ProgBar.show("finish fetching files id...", 100, 100);
			Memory.put({
				page: "single",
				action: "deepUpdate",
				meta: Memory.fetch("toUpdate"),
				currentIndex: 0,
				total: Memory.fetch("total"),
			});

			// Click first file to start deep update
			const salesGrid = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00");
			const firstLink = salesGrid.rows[1].cells[0].getElementsByTagName("a")[0];
			firstLink.click();
			sessionStorage.setItem("toolbarshow", JSON.stringify(false));

			return;
		}
		Memory.put({
			page: "listing",
			action: "fetchAllSidDeepUpdate",
			meta: newmeta,
			total: Memory.fetch("total"),
			toUpdate: Memory.fetch("toUpdate"),
		});
		fasbauDeep();
	} else {
		abtn.click();
		sbtn.click();
		beginWaitByAgingLoadDeep(Memory.fetch("meta")[0].total, Memory.fetch("meta")[0].aging);
	}

	function beginWaitByAgingLoadDeep(filesum, aging) {
		let cfsumElement = document.getElementsByClassName("rgInfoPart")[0].getElementsByTagName("strong")[0];
		let pageSizeComboBox = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00_ctl03_ctl01_PageSizeComboBox");
		let pageSizeArrow = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00_ctl03_ctl01_PageSizeComboBox_Arrow");
		let pageSizeDropDown = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00_ctl03_ctl01_PageSizeComboBox_DropDown");
		let salesGrid = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00");

		// Get total from .rgInfoPart (single source of truth)
		const totalFromRgInfo = parseInt(cfsumElement.innerText);

		// Update Memory with actual total from .rgInfoPart
		const currentMemory = Memory.fetch();
		Memory.put({
			...currentMemory,
			total: totalFromRgInfo
		});

		let cfsum = totalFromRgInfo;
		let clen = parseInt(pageSizeComboBox.children[0].children[0].value);
		if (clen < 50) {
			pageSizeArrow.click();
			pageSizeDropDown.children[0].children[0].children[2].click();
			console.log(parseInt(pageSizeComboBox.children[0].children[0].value));
			setTimeout(function() {
				beginWaitByAgingLoadDeep(filesum, aging);
			}, 50);
			return;
		}

		let currentElements = salesGrid.children[2].childElementCount;
		if (currentElements < 11 && currentElements < cfsum) {
			setTimeout(function() {
				beginWaitByAgingLoadDeep(filesum, aging);
			}, 50);
			return;
		}

		let cid = salesGrid.children[2].children[0].cells[2].innerText;

		if (Memory.fetch('toUpdate').length > 0) {
			if (Memory.fetch('toUpdate').some(record => record['saleid'] === cid)) {
				setTimeout(function() {
					beginWaitByAgingLoadDeep(filesum, aging);
				}, 50);
				return;
			}
		}

		ProgBar.show('fetching files for deep update...', Memory.fetch('toUpdate').length, totalFromRgInfo);
		deepUpdateProc(null, null, aging);
	}
}

function deepUpdateProc(toUpdate = null, page = null, aging = null) {
    const rgInfoPart = document.getElementsByClassName("rgInfoPart")[0];
    const rgCurrentPage = document.getElementsByClassName("rgCurrentPage")[0];
    const table = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00");
    const nextPageButton = document.getElementsByClassName("rgPageNext")[0];
    const firstPageButton = document.getElementsByClassName("rgPageFirst")[0];

    const totalPages = parseInt(rgInfoPart.getElementsByTagName("strong")[1].innerText);
    let currentPage = parseInt(rgCurrentPage.innerText);
    const totalRows = parseInt(rgInfoPart.getElementsByTagName("strong")[0].innerText);

    // Update Memory with actual total from .rgInfoPart if not set yet
    if (!Memory.fetch('total')) {
        const currentMemory = Memory.fetch();
        Memory.put({
            ...currentMemory,
            total: totalRows
        });
    }

    // Initialize `toUpdate` if not provided
    if (!toUpdate) {
        toUpdate = [];
    }

    // Handle pagination logic
    if (page !== null && currentPage !== page) {
        setTimeout(() => {
            console.log("Waiting for next page...");
            deepUpdateProc(toUpdate, page, aging);
        }, 50);
        return;
    }

    // Use a Set for efficient lookups
    const toUpdateSet = new Set(toUpdate.map((val) => val.saleid));

    // Cache table rows and length
    const tableRows = table.rows;
    const tableLength = tableRows.length;

    for (let i = 1; i < tableLength; i++) {
        const row = tableRows[i];
        const saleid = row.cells[2].innerText;

        // Skip rows already in `toUpdate`
        if (toUpdateSet.has(saleid)) {
            continue;
        }

        // Get link to file for deep update
        const link = row.cells[0].getElementsByTagName("a")[0];

        // Add minimal data - will be fetched from file
        toUpdate.push({
            aging: aging,
            saleid: saleid,
            link: link
        });

        // Update progress bar
        ProgBar.show(null, toUpdate.length, Memory.fetch("total"));
    }

    // Check if all rows have been processed
    if (toUpdate.length === totalRows) {
        mymemory.data.meta[0].done = true;
        mymemory.data.toUpdate = Memory.fetch("toUpdate")?.concat(toUpdate) || toUpdate;
        mymemory.persist();
        fasbauDeep();
        return;
    }

    // Pagination logic
    if (currentPage < totalPages) {
        nextPageButton.click();
        setTimeout(() => {
            deepUpdateProc(toUpdate, currentPage + 1, aging);
        }, 50);
    } else {
        firstPageButton.click();
        setTimeout(() => {
            deepUpdateProc(toUpdate, 1, aging);
        }, 50);
    }
}

// Scrape the UNFILTERED main collection list (mixed agings) top-to-bottom and
// stash the saleid order for the Sync+Remark queue (listingOrder). Runs once per
// gufcba run, before the aging-filtered sync begins. Only collects as many
// saleids as the remark quota needs (sets page size 50 first, so one page
// usually covers it) — no full-grid paging. Leaves the grid on page 1.
async function scrapeMainListingOrder() {
	try {
		// How many saleids the remark queue actually needs
		let quota = 0;
		try {
			quota = JSON.parse(localStorage.getItem("remarkConfig") || "{}").quota || 0;
		} catch (e) { /* ignore */ }
		const target = quota > 0 ? quota : 50; // fallback: one full page

		const salesInput = document.getElementById("ctl00_Content_txtSalesNo");
		const searchBtn = document.getElementById("ctl00_Content_btnConfirmSearch_input");
		if (salesInput) salesInput.value = ""; // no filter = full mixed list
		if (searchBtn) searchBtn.click();
		await delay(800); // let the grid refresh (AJAX)

		// Page size 50 → one page usually covers the whole quota
		const combo = document.getElementById(DOM_SELECTORS.PAGE_SIZE.COMBO);
		const comboValue = parseInt(combo?.children[0]?.children[0]?.value) || 0;
		if (comboValue < 50) {
			const arrow = document.getElementById(DOM_SELECTORS.PAGE_SIZE.ARROW);
			const dropdown = document.getElementById(DOM_SELECTORS.PAGE_SIZE.DROPDOWN);
			if (arrow && dropdown) {
				arrow.click();
				await delay(50);
				dropdown.children[0].children[0].children[2].click(); // "50" item
				await delay(800); // grid reloads with 50 rows, back on page 1
			}
		}

		const saleids = [];
		const seen = new Set();
		for (let guard = 0; guard < 20 && saleids.length < target; guard++) {
			const table = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00");
			if (table && table.rows) {
				for (let i = 1; i < table.rows.length && saleids.length < target; i++) {
					const cell = table.rows[i].cells[2];
					const sid = cell ? cell.innerText.trim() : "";
					if (sid && !seen.has(sid)) {
						seen.add(sid);
						saleids.push(sid);
					}
				}
			}
			if (saleids.length >= target) break;

			const rgInfo = document.getElementsByClassName("rgInfoPart")[0];
			const curEl = document.getElementsByClassName("rgCurrentPage")[0];
			const nextBtn = document.getElementsByClassName("rgPageNext")[0];
			if (!rgInfo || !curEl || !nextBtn) break;
			const totalPages = parseInt(rgInfo.getElementsByTagName("strong")[1].innerText);
			const currentPage = parseInt(curEl.innerText);
			if (!totalPages || currentPage >= totalPages) break;

			nextBtn.click();
			await delay(600); // AJAX page swap
		}

		localStorage.setItem("listingOrder", JSON.stringify(saleids));
		console.log(`[ListingOrder] Main list order scraped (${saleids.length} needed ${target}):`, saleids);

		// Back to page 1 if we paged, so the aging sync starts from a clean grid
		const curElAfter = document.getElementsByClassName("rgCurrentPage")[0];
		if (curElAfter && parseInt(curElAfter.innerText) > 1) {
			const firstBtn = document.getElementsByClassName("rgPageFirst")[0];
			if (firstBtn) {
				firstBtn.click();
				await delay(500);
			}
		}
	} catch (e) {
		console.error("[ListingOrder] Scrape failed — remark queue will fall back to DB order:", e);
	}
}

function fasbau() {
	console.log("🟢 fasbau() started");

	// Sync+Remark only: before the aging 2→3→4 sync begins, scrape the UNFILTERED
	// main list once (direct search, mixed agings) — the remark queue follows
	// THIS order, not the per-aging sync order. One-shot per gufcba run.
	if (localStorage.getItem("fetchMode") === "syncremark" && !sessionStorage.getItem("listingOrderScraped")) {
		sessionStorage.setItem("listingOrderScraped", "1");
		scrapeMainListingOrder().then(() => fasbau());
		return;
	}

	let schBtn = document.getElementById("ctl00_Content_btnConfirmSearch_input");
	let ag2Btn, ag3Btn, ag4Btn, ag5Btn;
	
	if (UserProfile.team === "alpha") {
		ag2Btn = document.getElementById("ctl00_Content_cmbMthAging_DropDown").children[0].children[1].children[0]
			.children[0];
		ag3Btn = document.getElementById("ctl00_Content_cmbMthAging_DropDown").children[0].children[1].children[1]
			.children[0];
		ag4Btn = document.getElementById("ctl00_Content_cmbMthAging_DropDown").children[0].children[1].children[2]
			.children[0];
	} else {
		ag5Btn = document.getElementById("ctl00_Content_cmbMthAging_DropDown").children[0].children[1].children[3]
			.children[0];
	}

	let agtarget = Memory.fetch("meta")[0].aging;
	console.log("🎯 Target aging:", agtarget);
	
	switch (agtarget) {
		case 2:
			agingClickHandler(ag2Btn, schBtn);
			break;
		case 3:
			agingClickHandler(ag3Btn, schBtn);
			break;
		case 4:
			agingClickHandler(ag4Btn, schBtn);
			break;
		case 5:
			agingClickHandler(ag5Btn, schBtn);
			break;
		default:
			console.log("❌ Invalid aging target");
			RunBlocker.hide();
			return;
		}

	function agingClickHandler(abtn, sbtn) {
		console.log("🔵 agingClickHandler() called");
		console.log("📋 meta[0].done:", Memory.fetch("meta")[0].done);
		
		if (Memory.fetch("meta")[0].done) {
			let newmeta = Memory.fetch("meta");
			newmeta.shift();
			abtn.click();
			if (newmeta.length === 0) {
				ProgBar.show("finish fetching files data...", 100, 100);
				Memory.put({
					page: "listing",
					action: "muo",
					meta: Memory.fetch("toUpdate"),
					total: Memory.fetch("total"),
				});
				massUpdateOps();
				sessionStorage.setItem("toolbarshow", JSON.stringify(false));
				return;
			}
			Memory.put({
				page: "listing",
				action: "fetchAllSidUpdate",
				meta: newmeta,
				total: Memory.fetch("total"),
				toUpdate: Memory.fetch("toUpdate"),
			});
			fasbau();
		} else {
			console.log("🔘 Aging button click");
			abtn.click();
			console.log("🔍 Search button click");
			sbtn.click();
			console.log("⏳ Starting beginWaitByAgingLoadU with filesum:", Memory.fetch("meta")[0].total, "aging:", Memory.fetch("meta")[0].aging);
			beginWaitByAgingLoadU(Memory.fetch("meta")[0].total, Memory.fetch("meta")[0].aging);
		}
	}

	function beginWaitByAgingLoadU(filesum, aging) {
		console.log("⏱️ beginWaitByAgingLoadU() - filesum:", filesum, "aging:", aging);
		
		let cfsumElement = document.getElementsByClassName("rgInfoPart")[0]?.getElementsByTagName("strong")[0];
		let pageSizeComboBox = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00_ctl03_ctl01_PageSizeComboBox");
		let pageSizeArrow = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00_ctl03_ctl01_PageSizeComboBox_Arrow");
		let pageSizeDropDown = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00_ctl03_ctl01_PageSizeComboBox_DropDown");
		let salesGrid = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00");

		// Wait for elements to exist
		if (!cfsumElement || !salesGrid) {
			console.log("⚠️ Waiting for elements - cfsumElement:", !!cfsumElement, "salesGrid:", !!salesGrid);
			setTimeout(function() {
				beginWaitByAgingLoadU(filesum, aging);
			}, 50);
			return;
		}

		let cfsum = parseInt(cfsumElement.innerText);
		console.log("📊 Current sum:", cfsum, "Expected:", filesum);
		
		// **FIX: Update Memory with actual total from .rgInfoPart if filesum is 0**
		if (filesum === 0 && cfsum > 0) {
			console.log("🔄 Updating filesum from 0 to actual:", cfsum);
			const currentMemory = Memory.fetch();
			currentMemory.total = cfsum;
			currentMemory.meta[0].total = cfsum;
			Memory.put(currentMemory);
			filesum = cfsum; // Update local variable
		}
		
		if (cfsum !== filesum) {
			console.log("⚠️ Sum mismatch - waiting...");
			setTimeout(function() {
				beginWaitByAgingLoadU(filesum, aging);
			}, 50);
			return;
		}

		// Rest of the code remains the same...
		let clen = parseInt(pageSizeComboBox?.children[0]?.children[0]?.value || 0);
		console.log("📏 Page size:", clen);
		
		if (clen < 50) {
			console.log("📏 Changing page size to 50");
			pageSizeArrow?.click();
			pageSizeDropDown?.children[0]?.children[0]?.children[2]?.click();
			setTimeout(function() {
				beginWaitByAgingLoadU(filesum, aging);
			}, 50);
			return;
		}

		let currentElements = salesGrid.children[2]?.childElementCount || 0;
		console.log("📦 Current elements:", currentElements, "Expected min: 11 or", cfsum);
		
		if (currentElements < 11 && currentElements < cfsum) {
			console.log("⚠️ Not enough elements loaded - waiting...");
			setTimeout(function() {
				beginWaitByAgingLoadU(filesum, aging);
			}, 50);
			return;
		}

		const firstRow = salesGrid.children[2]?.children[0];
		console.log("🔍 First row exists:", !!firstRow);
		
		if (!firstRow) {
			console.log("⚠️ No first row - waiting...");
			setTimeout(function() {
				beginWaitByAgingLoadU(filesum, aging);
			}, 50);
			return;
		}

		console.log("🔍 First row classes:", firstRow.className);
		
		if (firstRow.classList.contains('rgNoRecords')) {
			console.log("⚠️ 'No records' placeholder detected - waiting...");
			setTimeout(function() {
				beginWaitByAgingLoadU(filesum, aging);
			}, 50);
			return;
		}

		if (!firstRow.cells || firstRow.cells.length < 3 || !firstRow.cells[2]) {
			console.log("⚠️ Cells not ready - waiting...");
			setTimeout(function() {
				beginWaitByAgingLoadU(filesum, aging);
			}, 50);
			return;
		}

		let cid = firstRow.cells[2].innerText;
		console.log("✅ Got sale ID:", cid);

		if (Memory.fetch('toUpdate')?.length > 0) {
			const hasConflict = Memory.fetch('toUpdate').some(record => record['saleid'] === cid);
			if (hasConflict) {
				console.log("⚠️ Conflict detected - waiting...");
				setTimeout(function() {
					beginWaitByAgingLoadU(filesum, aging);
				}, 50);
				return;
			}
		}

		console.log("✅ All checks passed - proceeding to updateProc");
		ProgBar.show('fetching latest files...', Memory.fetch('toUpdate').length, Memory.fetch('total'));
		updateProc(null, null, aging);
	}
}

function updateProc(toUpdate = null, page = null, aging = null) {
    // Cache frequently accessed DOM elements outside the loop
    const rgInfoPart = document.getElementsByClassName("rgInfoPart")[0];
    const rgCurrentPage = document.getElementsByClassName("rgCurrentPage")[0];
    const table = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00");
    const nextPageButton = document.getElementsByClassName("rgPageNext")[0];
    const firstPageButton = document.getElementsByClassName("rgPageFirst")[0];

    // Parse total pages and current page once
    const totalPages = parseInt(rgInfoPart.getElementsByTagName("strong")[1].innerText);
    let currentPage = parseInt(rgCurrentPage.innerText);
    const totalRows = parseInt(rgInfoPart.getElementsByTagName("strong")[0].innerText);

    // Update Memory with actual total from .rgInfoPart if not set yet
    if (!Memory.fetch('total')) {
        const currentMemory = Memory.fetch();
        Memory.put({
            ...currentMemory,
            total: totalRows
        });
    }

    // Initialize `toUpdate` if not provided
    if (!toUpdate) {
        toUpdate = [];
    }

    // Handle pagination logic
    if (page !== null && currentPage !== page) {
        setTimeout(() => {
            console.log("Waiting for next page...");
            updateProc(toUpdate, page, aging);
        }, 50);
        return;
    }

    // Use a Set for efficient lookups
    const toUpdateSet = new Set(toUpdate.map((val) => val.saleid));
    const saleidsToUpdate = [];
    const dataToUpdate = [];

    // Cache table rows and length to avoid repeated DOM access
    const tableRows = table.rows;
    const tableLength = tableRows.length;

    for (let i = 1; i < tableLength; i++) {
        const row = tableRows[i];
        const saleid = row.cells[2].innerText;

        // Skip rows already in `toUpdate`
        if (toUpdateSet.has(saleid)) {
            continue;
        }

        // Extract PTP date if applicable
        let ptpDate = "";
        if (row.cells[12].innerText === "PTP") {
            const dateParts = row.cells[14].innerText.split("/");
            ptpDate = `${dateParts[2]}-${dateParts[1]}-${dateParts[0]}`;
        }

        // Reason column (cell 12): blank (&nbsp;) = not yet remarked in CCMS.
        // Carried into CMFile so the Sync+Remark queue can skip remarked files.
        const reasonText = row.cells[12] ? (row.cells[12].innerText || "").trim() : "";
        const reasonBlank = reasonText === "";

        // Calculate paid and total OS values
        const paid = parseFloat(row.cells[9].innerText);
        const totalOS = parseFloat(row.cells[6].innerText) + parseFloat(row.cells[8].innerText);

        // Add data to `toUpdate` based on checkbox state
        if (tdsyncccms.checked) {
            toUpdate.push({
                aging: aging,
                saleid: saleid,
                reasonBlank: reasonBlank,
                date: { ptp: ptpDate },
                os: { paid: paid, total: totalOS },
            });
        } else {
            toUpdate.push({
                aging: aging,
                saleid: saleid,
                reasonBlank: reasonBlank,
                os: { paid: paid, total: totalOS },
            });
        }

        saleidsToUpdate.push(saleid);

        // Update progress bar
        ProgBar.show(null, toUpdate.length, Memory.fetch("total"));
    }

    // Check if all rows have been processed
    if (toUpdate.length === totalRows) {
        mymemory.data.meta[0].done = true;
        mymemory.data.toUpdate = Memory.fetch("toUpdate")?.concat(toUpdate) || toUpdate;
        mymemory.persist();
        fasbau();
        return;
    }

    // Pagination logic
    if (currentPage < totalPages) {
        nextPageButton.click();
        setTimeout(() => {
            console.log("Moving to next page...");
            updateProc(toUpdate, currentPage + 1, aging);
        }, 50);
    } else {
        // Return to the first page and restart
        firstPageButton.click();
        setTimeout(() => {
            console.log("Returning to first page...");
            updateProc(toUpdate, 1, aging);
        }, 50);
    }
}

function dataFileSave(file) {
    const reader = new FileReader();
    reader.onload = async function (event) {
        const fileData = event.target.result;
        let rows = [];
        if (file.name.endsWith(".csv")) {
            // Handle CSV files
            rows = fileData.split("\n").map(row => row.trim().split(","));
        } else if (file.name.endsWith(".xls") || file.name.endsWith(".xlsx")) {
            // Lazy load XLSX library when needed
            await window.LazyLoader.loadXLSX();

            // Handle XLS/XLSX files using SheetJS
            const workbook = XLSX.read(fileData, { type: "binary" });
            const sheetName = workbook.SheetNames[0]; // Get the first sheet
            rows = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName], { header: 1 }); // Convert sheet to array of rows
        } else {
            console.error("Unsupported file format.");
            return;
        }
        
        // Extract headers
        const headers = rows[0];
        
        // Define required headers and find their indices
        const requiredHeaders = ["SalesNo", "TargetAging", "UnbillAging"];
        const headerIndices = {};
        
        // Check if required headers exist and get their indices
        for (const header of requiredHeaders) {
            const index = headers.indexOf(header);
            if (index === -1) {
                console.error(`Missing required header: ${header}`);
                return;
            }
            headerIndices[header] = index;
        }
        
        // Function to determine aging category
        function determineAging(targetAging, unbillAging) {
            const target = parseFloat(targetAging);
            const unbill = parseFloat(unbillAging);
            
            if (target >= 2 && target < 3) return 2;
            if (target >= 3 && target < 4) return 3;
            if (target >= 4 && target < 5) return 4;
            if (target >= 5)  return 5;
            
            
            // Default fallback
            return 2;
        }
        
        // Process all records and add aging info
        const toCreate = [];
        const agingStats = { 2: 0, 3: 0, 4: 0, "5a": 0, "5b": 0 };
        
        // Process each row
        for (let i = 1; i < rows.length; i++) {
            const row = rows[i];
            if (!row || !row[headerIndices.SalesNo]) continue;
            
            // Get data from the row
            const sid = String(row[headerIndices.SalesNo]).trim();
            const targetAging = row[headerIndices.TargetAging];
            const unbillAging = row[headerIndices.UnbillAging];
            
            // Validate data
            if (!sid || targetAging === undefined || unbillAging === undefined) {
                console.warn(`Skipping row ${i}: missing required data`);
                continue;
            }
            
            // Determine aging category
            const agingCategory = determineAging(targetAging, unbillAging);
            
            // Add to records array with aging info
            toCreate.push({ 
                saleid: sid,
                aging: agingCategory,
                targetAging: targetAging,
                unbillAging: unbillAging 
            });
            
            // Update stats
            agingStats[agingCategory]++;
        }
        
        // Log aging distribution
        console.log("Aging distribution from Excel:");
        Object.keys(agingStats).forEach(aging => {
            if (agingStats[aging] > 0) {
                console.log(`Aging ${aging}: ${agingStats[aging]} records`);
            }
        });
        
        // Store data in memory for direct mass saving operations
        Memory.put({
            page: "listing",
            action: "mso", // Direct to mass saving operations
            meta: toCreate, // Store all records in meta for processing
            total: toCreate.length,
            agingStats: agingStats // Store stats for reference
        });
        
        console.log("File data processed and stored in memory for direct saving.");
        console.log("Total records to save:", toCreate.length);
        console.log("Bypassing getFileCountByAging() and fetchAllSidByAging() - going directly to massSavingOps()");
        
        // Ask the user to confirm before proceeding
        const userConfirmed = confirm(`Found ${toCreate.length} records with aging data processed. Do you want to continue saving directly? Click OK to continue or Cancel to abort.`);
        
        if (userConfirmed) {
            // Navigate to listing page and run mass saving operations directly
            if (Router.isCurrentPage("listing")) {
                massSavingOps(); // Bypass everything and go directly to mass saving operations
            } else {
                // Store a flag to run massSavingOps after page loads
                Memory.put({
                    page: "listing",
                    action: "mso",
                    meta: toCreate,
                    total: toCreate.length,
                    agingStats: agingStats,
                    runDirectly: true // Flag to run massSavingOps directly after page load
                });
                location.href = Router.getPageUrl("listing");
            }
        } else {
            console.log("Save operation canceled by the user.");
            Memory.clear();
            alert("Save operation canceled. No changes were made.");
        }
    };
    
    reader.onerror = function () {
        console.error("Error reading file:", reader.error);
    };
    
    // Read the file as text (for CSV) or binary (for XLS/XLSX)
    if (file.name.endsWith(".csv")) {
        reader.readAsText(file);
    } else {
        reader.readAsBinaryString(file);
    }
}

function DataFileUpdate(file) {
    const reader = new FileReader();

    reader.onload = async function (event) {
        const fileData = event.target.result;

        let rows = [];
        if (file.name.endsWith(".csv")) {
            // Handle CSV files
            rows = fileData.split("\n").map(row => row.trim().split(","));
        } else if (file.name.endsWith(".xls") || file.name.endsWith(".xlsx")) {
            // Lazy load XLSX library when needed
            await window.LazyLoader.loadXLSX();

            // Handle XLS/XLSX files using SheetJS
            const workbook = XLSX.read(fileData, { type: "binary" });
            const sheetName = workbook.SheetNames[0]; // Get the first sheet
            rows = XLSX.utils.sheet_to_json(workbook.Sheets[sheetName], { header: 1 }); // Convert sheet to array of rows
        } else {
            console.error("Unsupported file format.");
            return;
        }

        // Extract headers
        const headers = rows[0];
        
        // Define required headers and find their indices
        const requiredHeaders = ["SalesNo", "TotalTarget", "TotalPaid"];
        const headerIndices = {};
        
        // Check if required headers exist and get their indices
        for (const header of requiredHeaders) {
            const index = headers.indexOf(header);
            if (index === -1) {
                console.error(`Missing required header: ${header}`);
                return;
            }
            headerIndices[header] = index;
        }
        
        // Also get PTPDate index if tdsyncccms is checked
        if (tdsyncccms.checked) {
            const ptpDateIndex = headers.indexOf("PTPDate");
            if (ptpDateIndex !== -1) {
                headerIndices.PTPDate = ptpDateIndex;
            }
        }

        const meta = [];
        for (let i = 1; i < rows.length; i++) {
            const row = rows[i];
            if (!row || !row[headerIndices.SalesNo]) continue;

            // Get values using only the indices we need
            const saleid = String(row[headerIndices.SalesNo]).trim();
            const totalTarget = parseFloat(row[headerIndices.TotalTarget]);
            const totalPaid = parseFloat(row[headerIndices.TotalPaid]);
            const totalOS = totalTarget + totalPaid;

            // Validate data
            if (!saleid || isNaN(totalOS) || isNaN(totalPaid)) {
                continue;
            }

            // Format the record as expected by massUpdateOps
            const record = {
                saleid: saleid,
                os: {
                    paid: totalPaid,
                    total: totalOS,
                },
            };

            // Add PTP date if tdsyncccms.checked is true and PTPDate exists
            if (tdsyncccms.checked && headerIndices.PTPDate !== undefined) {
                record.date = {
                    ptp: row[headerIndices.PTPDate] ? String(row[headerIndices.PTPDate]).trim() : null,
                };
            }

            meta.push(record);
        }

        // Store data in memory
        Memory.put({
            page: "listing",
            action: "muo", // Directly prepare for mass update
            meta: meta, // Store all records in meta
            total: meta.length, // Total number of records
        });

        console.log("File data processed and stored in memory.");
        console.log("Memory content:", Memory.fetch());

        // Ask the user to confirm before proceeding
        const userConfirmed = confirm("Do you want to continue updating data via the file? Click OK to continue or Cancel to abort.");
        if (userConfirmed) {
            massUpdateOps();
        } else {
            console.log("Update canceled by the user.");
            Memory.clear();
            alert("Update canceled. No changes were made.");
        }
    };

    reader.onerror = function () {
        console.error("Error reading file:", reader.error);
    };

    // Read the file as text (for CSV) or binary (for XLS/XLSX)
    if (file.name.endsWith(".csv")) {
        reader.readAsText(file);
    } else {
        reader.readAsBinaryString(file);
    }
}

function continueDeepUpdate() {
	const memData = Memory.fetch();
	if (!memData || memData.action !== "continueDeepUpdate") return;

	const currentIndex = memData.currentIndex || 0;
	const allFiles = memData.meta;
	const total = memData.total;

	if (currentIndex >= total) {
		// This shouldn't happen, but handle it
		Memory.clear();
		location.href = Router.getPageUrl("home");
		return;
	}

	const currentFile = allFiles[currentIndex];

	// Update Memory to deepUpdate action before clicking file
	Memory.put({
		page: "single",
		action: "deepUpdate",
		meta: allFiles,
		currentIndex: currentIndex,
		total: total
	});

	// Find and click the link for current file
	const salesGrid = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00");
	if (!salesGrid) {
		console.error("Sales grid not found");
		Memory.clear();
		return;
	}

	// Find the row with matching saleid
	for (let i = 1; i < salesGrid.rows.length; i++) {
		const row = salesGrid.rows[i];
		const saleid = row.cells[2].innerText;
		if (saleid === currentFile.saleid) {
			const link = row.cells[0].getElementsByTagName("a")[0];
			if (link) {
				link.click();
				return;
			}
		}
	}

	// If we couldn't find the file, skip to next
	console.warn(`Could not find file ${currentFile.saleid}, skipping...`);
	const nextIndex = currentIndex + 1;
	if (nextIndex < total) {
		Memory.put({
			page: "listing",
			action: "continueDeepUpdate",
			meta: allFiles,
			currentIndex: nextIndex,
			total: total
		});
		setTimeout(() => {
			location.reload();
		}, 100);
	} else {
		Memory.clear();
		location.href = Router.getPageUrl("home");
	}
}

function massDeepUpdateOps() {
	const memData = Memory.fetch();
	if (!memData || memData.action !== "deepUpdate") return;

	const currentIndex = memData.currentIndex || 0;
	const allFiles = memData.meta;
	const total = memData.total;

	if (currentIndex >= total) {
		// All done
		ProgBar.show("Deep update completed!", total, total);
		Snackbar.show("Deep update completed!");
		Memory.clear();
		setTimeout(() => {
			location.href = Router.getPageUrl("home");
		}, 2000);
		return;
	}

	const currentFile = allFiles[currentIndex];
	const saleid = currentFile.saleid;

	ProgBar.show(`Deep updating file ${currentIndex + 1}/${total}...`, currentIndex + 1, total);

	// Check if file exists in database
	const existing = CMFile.all({ verb: "only", key: "saleid" });
	const fileExists = existing.includes(saleid);

	if (fileExists) {
		// File exists - update only payment fields using diggerSinglePayment()
		const paymentData = diggerSinglePayment();
		if (paymentData) {
			let rec = CMFile.give(saleid);
			rec.os.paid = paymentData.os.paid;
			rec.os.total = paymentData.os.total;
			rec.os.payment = paymentData.os.payment;
			rec.date.update = new Date();
			CMFile.update(saleid, rec);
			console.log(`[massDeepUpdateOps] Updated payment data for ${saleid}`);
		}
	} else {
		// New file - fetch complete data using diggerSingle()
		const completeData = diggerSingle();
		if (completeData) {
			CMFile.create(completeData);
			console.log(`[massDeepUpdateOps] Created new file ${saleid}`);
		}
	}

	// Move to next file
	const nextIndex = currentIndex + 1;
	if (nextIndex < total) {
		Memory.put({
			page: "listing",
			action: "continueDeepUpdate",
			meta: allFiles,
			currentIndex: nextIndex,
			total: total
		});

		// Go back to listing page
		setTimeout(() => {
			window.history.back();
		}, 500);
	} else {
		// Done with all files - remove files not in list
		const saleidsInList = new Set(allFiles.map(f => f.saleid));
		const existingFiles = CMFile.all({ verb: "only", key: "saleid" });

		existingFiles.forEach((saleid) => {
			if (!saleidsInList.has(saleid)) {
				let record = CMFile.give(saleid);
				record.removed = true;
				CMFile.update(saleid, record);
				console.log(`[massDeepUpdateOps] Removed ${saleid} - not in current list`);
			}
		});

		ProgBar.show("Deep update completed!", total, total);
		Snackbar.show(`Deep update completed! ${total} files processed.`);
		Memory.clear();
		setTimeout(() => {
			location.href = Router.getPageUrl("home");
		}, 2000);
	}
}

// fea only: when sync finds files that are in the listing but not fetched yet
// (e.g. leftovers from an interrupted fetch), ask what to do with them.
// Modal B cancelled → loops back to Modal C. Skip/cancel → finish sync without fetching.
async function promptAddsFetchMode(adds, updatedCount) {
    // If user picked "Sync + Remark (existing)" in Modal A, preserve its config for the
    // post-fetch remark queue — Modal C will overwrite fetchMode/remarkConfig for the adds.
    if (localStorage.getItem("fetchMode") === "syncremark") {
        try {
            const queueCfg = JSON.parse(localStorage.getItem("remarkConfig") || "null");
            if (queueCfg) {
                queueCfg.exclude = adds.map((r) => r.saleid); // adds handled by Modal C, don't re-remark
                localStorage.setItem("remarkQueueConfig", JSON.stringify(queueCfg));
            }
        } catch (e) {
            console.error("[MassUpdate] Failed to preserve syncremark config:", e);
        }
    }

    while (true) {
        const choice = await customConfirm3({
            title: "Fail baru ditemui",
            message: `${adds.length} fail belum di-fetch. Apa tindakan?`,
            options: [
                { label: "Fetch + GL + Remark", value: "full", styleProps: { background: "#66a771", color: "white" } },
                { label: "Fetch + GL sahaja", value: "gl", styleProps: { background: "#4a90d9", color: "white" } },
                { label: "Fetch + Remark sahaja", value: "remark", styleProps: { background: "#f5a623", color: "white" } },
                { label: "Fetch sahaja", value: "only", styleProps: { background: "#6c757d", color: "white" } },
                { label: "Jangan fetch", value: "skip", styleProps: { background: "#1f2937", color: "#e5e7eb" } },
            ],
        });
        console.log("[ModalC] choice:", choice);

        // Skip / backdrop-cancel: leave new files unfetched, finish like the no-adds path
        if (!choice || choice === "skip" || choice === "cancel") {
            Snackbar.show(`${updatedCount} files updated, ${adds.length} new file(s) skipped`);

            // Remark queue for existing files may still be pending (Sync + Remark in Modal A)
            if (startRemarkQueuePhase()) return;

            setTimeout(() => {
                Memory.clear();
                localStorage.removeItem("fetchMode");
                localStorage.removeItem("remarkConfig");
                localStorage.removeItem("remarkQueueConfig");
                localStorage.removeItem("glConfig");
                location.href = Router.getPageUrl("home");
            }, 3000);
            return;
        }

        // Remark modes → Modal B (no quota: remark applies to all new files)
        if (choice === "full" || choice === "remark") {
            const cfg = await customRemarkConfig({
                showQuota: false,
                title: "Fetch + Remark",
                message: "Sahkan remark untuk semua fail baru:",
            });
            if (!cfg) continue; // Modal B cancelled → back to Modal C
            localStorage.setItem("remarkConfig", JSON.stringify(cfg));
        } else {
            localStorage.removeItem("remarkConfig");
        }

        // GL modes → Modal GL (GL1 / 2nd GL BM/Eng / both)
        if (choice === "full" || choice === "gl") {
            const glCfg = await customGLConfig();
            if (!glCfg) continue; // Modal GL cancelled → back to Modal C
            localStorage.setItem("glConfig", JSON.stringify(glCfg));
        } else {
            localStorage.removeItem("glConfig");
        }

        localStorage.setItem("fetchMode", choice); // "full" | "gl" | "remark" | "only"
        console.log("[ModalC] starting fetch:", choice, "| glConfig:", localStorage.getItem("glConfig"), "| remarkConfig:", localStorage.getItem("remarkConfig"));
        Memory.put({
            page: "listing",
            action: "mso",
            meta: adds,
            total: adds.length,
        });
        massSavingOps();
        return;
    }
}

// klng only: same post-sync ask as fea Modal C, but WITHOUT remark options.
// Sync runs first (klng gufcba has no up-front modal); if new files are found,
// ask what to do with them. "Fetch + GL" → Modal GL (GL1 / 2nd GL BM/Eng / both).
// Modal GL cancelled → loops back here. Skip/cancel → finish sync without fetching.
async function promptAddsFetchModeKlng(adds, updatedCount) {
    while (true) {
        const choice = await customConfirm3({
            title: "Fail baru ditemui",
            message: `${adds.length} fail belum di-fetch. Apa tindakan?`,
            options: [
                { label: "Fetch + GL", value: "gl", styleProps: { background: "#66a771", color: "white" } },
                { label: "Fetch sahaja", value: "only", styleProps: { background: "#6c757d", color: "white" } },
                { label: "Jangan fetch", value: "skip", styleProps: { background: "#1f2937", color: "#e5e7eb" } },
            ],
        });
        console.log("[ModalC-klng] choice:", choice);

        // Skip / backdrop-cancel: leave new files unfetched, finish like the no-adds path
        if (!choice || choice === "skip" || choice === "cancel") {
            Snackbar.show(`${updatedCount} files updated, ${adds.length} new file(s) skipped`);
            setTimeout(() => {
                Memory.clear();
                localStorage.removeItem("fetchMode");
                localStorage.removeItem("remarkConfig");
                localStorage.removeItem("glConfig");
                location.href = Router.getPageUrl("home");
            }, 3000);
            return;
        }

        // "Fetch + GL" → Modal GL (GL1 / 2nd GL BM/Eng / both)
        if (choice === "gl") {
            const glCfg = await customGLConfig();
            if (!glCfg) continue; // Modal GL cancelled → back to Modal C
            localStorage.setItem("glConfig", JSON.stringify(glCfg));
        } else {
            localStorage.removeItem("glConfig");
        }

        localStorage.setItem("fetchMode", choice); // "gl" | "only"
        console.log("[ModalC-klng] starting fetch:", choice, "| glConfig:", localStorage.getItem("glConfig"));
        Memory.put({
            page: "listing",
            action: "mso",
            meta: adds,
            total: adds.length,
        });
        massSavingOps();
        return;
    }
}

function massUpdateOps() {
	let count = 0;
	let adds = [];
	let news = Memory.fetch("meta");

    console.log("Memory meta:", Memory.fetch("meta"));

	// Get ALL saleids including removed files
	let existing = new Set(mydb.records.map(r => r.saleid));

	// Cache the length of news for performance
	let newsLength = news.length;

	news.forEach((record, idx) => {
		ProgBar.show(`updating ${idx + 1} / ${newsLength} files...`, idx + 1, newsLength);

		if (existing.has(record.saleid)) {
			let change = false;
			let rec = CMFile.give(record.saleid);

			// If file was previously removed, un-remove it
			if (rec.removed) {
				rec.removed = false;
				change = true;
			}

			// Keep the reasonBlank flag fresh (gfcba remark skip), but persist it
			// SILENTLY — internal tracking metadata, not a user-visible "update".
			let metaOnly = false;
			if (record.reasonBlank !== undefined && rec.reasonBlank !== record.reasonBlank) {
				rec.reasonBlank = record.reasonBlank;
				metaOnly = true;
			}

			// Guard NaN: an empty/non-numeric Paid cell parses to NaN, and
			// NaN !== x is ALWAYS true — without the guard every sync would
			// count (and store) a bogus change forever.
			const newPaid = record.os ? record.os.paid : NaN;
			if (!Number.isNaN(newPaid) && rec.os.paid !== newPaid) {
				rec.os.paid = record.os.paid;
				rec.os.total = record.os.total;
				let t = rec.float + rec.os.paid;
				if (t <= 0) {
					rec.float = 0;
				}
				rec.date.update = new Date();
				change = true;
			}

			if (tdsyncccms.checked && record.date) {
				// Normalize null/undefined vs "" — gfcba stores null, the sync
				// scrape uses ""; treat both as "no PTP date".
				const newPtp = record.date.ptp || "";
				if ((rec.date.ptp || "") !== newPtp) {
					rec.date.ptp = record.date.ptp;
					rec.date.update = new Date();
					change = true;
				}
			}

			if (change || metaOnly) {
				CMFile.update(rec.saleid, rec);
				if (change) count++;
			}
		} else {
			adds.push(record);
		}

		existing.delete(record.saleid);
	});

	// Mark files no longer in table as removed
	existing.forEach((val) => {
		let record = CMFile.give(val);
		if (record && !record.removed) {
			record.removed = true;
			CMFile.update(val, record);
		}
	});

	if (adds.length) {
		console.log(`[MassUpdate] ${adds.length} new file(s) detected:`, adds.map((r) => r.saleid));
		// fea & klng: ask user what to do with the new files (Modal C, post-sync) —
		// also covers resume after an interrupted fetch (leftover files show up as adds).
		// fea Modal C has remark options; klng Modal C is GL-only (klng has no remark).
		// other teams: keep current auto-fetch behavior.
		if (localStorage.getItem("fea") === "true" && typeof customConfirm3 === "function") {
			// Sync+Remark pending? Remark the queued files FIRST — Modal C for the
			// new files is shown only after the queue drains (finishRemarkPhase
			// reads pendingAddsAfterRemark and calls promptAddsFetchMode then).
			localStorage.setItem("pendingAddsAfterRemark", JSON.stringify({ adds, updatedCount: count }));
			if (startRemarkQueuePhase()) {
				console.log("[MassUpdate] Remark phase pending — remarking first; Modal C for", adds.length, "new file(s) deferred until queue drains");
				return;
			}
			localStorage.removeItem("pendingAddsAfterRemark");
			console.log("[MassUpdate] fea — showing Modal C");
			promptAddsFetchMode(adds, count);
		} else if (localStorage.getItem("klng") === "true" && typeof customConfirm3 === "function") {
			console.log("[MassUpdate] klng — showing Modal C (GL only, no remark)");
			promptAddsFetchModeKlng(adds, count);
		} else {
			console.log("[MassUpdate] non-fea/klng — auto-fetch adds");
			Memory.put({
				page: "listing",
				action: "mso",
				meta: adds,
				total: adds.length,
			});
			massSavingOps();
		}
	} else {
        console.log("trigger save");
        Snackbar.show(`${count} files updated...`);

        // fea Sync+Remark: after sync, open N files and remark them one by one
        if (startRemarkQueuePhase()) {
            return; // remark phase handles navigation from here
        }

        setTimeout(() => {
            Memory.clear();
            localStorage.removeItem("fetchMode");
            localStorage.removeItem("remarkConfig");
            localStorage.removeItem("remarkQueueConfig");
            localStorage.removeItem("glConfig");
            location.href = Router.getPageUrl("home");
        }, 3000); // 3 seconds delay before clearing memory and redirecting
    }
}

// Export function for use in app-utils.js
window.getFileCountByAging = getFileCountByAging;

window.onload = function () {
    

    if (DataProcessor.process()) {
        handleSaveButtonClick();
    handleAlertDismissal();
        calculateCorrectPercentage();
    }
};

function hideLoginUser() {
    let lblLoginUser = document.getElementById("lblLoginUser");
    if (lblLoginUser) {
        lblLoginUser.style.display = "none"; // Hides the element
    }

    let lblTotalTarget = document.getElementById("Content_lblbar_totaltarget");
    if (lblTotalTarget) {
        lblTotalTarget.textContent = "100%"; // If it's a label, span, or div
        lblTotalTarget.value = "100%"; // If it's an input field
    }
}



function handleSaveButtonClick() {
	const saveClicked = sessionStorage.getItem("saveClicked") === "true";
	// remarkAction is set by BOTH automated remark paths (mass-save 'save' case and
	// 'handleremark' queue case) right before handleRemarkClick, and is only cleared
	// once the success message is seen. Unlike saveClicked — which is consumed on the
	// first post-save load — it survives any extra postbacks/redirects, so it serves
	// as the durable "an automated save is in flight on this tab" signal.
	const remarkAction = sessionStorage.getItem("remarkAction") === "true";
	console.log("[HandleSaveButtonClick] Called on load. saveClicked:", saveClicked, "| remarkAction:", remarkAction, "| window.opener:", !!window.opener, "| history.length:", history.length);

	if (saveClicked) {
		sessionStorage.removeItem("saveClicked");
		if (isSuccessAlertMessage()) {
			console.log("[HandleSaveButtonClick] Success alert detected on load, closing tab");
			sessionStorage.removeItem("remarkAction");
			notifyRemarkQueueNext(); // no-op outside the gufcba remark queue
			setTimeout(() => (window.closeMassSaveTab || window.close)(), 250);
		} else {
			// Alert not a success yet (or fills in after load) - keep watching
			console.log("[HandleSaveButtonClick] Save alert not a success message yet, observing for success...");
			observeAlertMessage();
		}
	} else if (remarkAction && isSuccessAlertMessage()) {
		// Post-save reload where saveClicked was already consumed by an earlier load
		// (extra postback/redirect): success is confirmed, so finish what that load
		// started — notify the queue and close. Without this branch the tab stays
		// open forever: nothing else re-arms the close on later loads.
		console.log("[HandleSaveButtonClick] saveClicked already consumed but remarkAction set + success visible — closing tab (recovered stuck tab)");
		sessionStorage.removeItem("remarkAction");
		notifyRemarkQueueNext();
		setTimeout(() => (window.closeMassSaveTab || window.close)(), 250);
	}
}

function handleAlertDismissal() {
	if (sessionStorage.getItem("alertDismissed") === "true") {
		console.log("Alert was dismissed and page was reloaded!");
		sessionStorage.removeItem("alertDismissed");
	}
}

function calculateCorrectPercentage() {
	const pageRouter = new Router();
	if (Router.isCurrentPage("single")) {
		sessionStorage.setItem("toolbarshow", JSON.stringify(true));
	}
	if (Router.isCurrentPage("home")) {
		handleModalDisplay();
		updateHomePageElements();
	}
}

function handleModalDisplay() {
	const modalShown = localStorage.getItem("modalShown");
	if (modalShown === "false") {
		console.log("Modal was not shown before, showing it now");
		makeDeviceModal();
		const deviceModal = document.getElementById("deviceModal");
		if (deviceModal) {
			deviceModal.style.display = "block";
			console.log("Device modal should be visible now");
		} else {
			console.error("Device modal not found");
		}
	}
}

function updateHomePageElements() {
	const allRecords = CMFile.all();
	const elements = [
		"ctl00_Content_txtTotalTarget",
		"ctl00_Content_txtAging2",
		"ctl00_Content_txtAging3",
		"ctl00_Content_txtAging4",
	];
	const agingTargets = [null, 2, 3, 4];

	elements.forEach((elementId, index) => {
		const records = index === 0 ? allRecords : CMFile.aging(agingTargets[index], allRecords);
		const npRecords = CMFile.filter(CMFilter.NP, records);
		const ppRecords = CMFile.filter(CMFilter.PP, records);
		const comRecords = CMFile.filter(CMFilter.COM, records);

		const element = document.getElementById(elementId);
		if (element) {
			element.innerHTML = `${element.innerHTML}<br><strong><span style="color:grey; display:inline-block;">NP : ${npRecords.length}</span> <span style="color:orange; display:inline-block;">PP : ${ppRecords.length}</span> <span style="color:green; display:inline-block;">C : ${comRecords.length}</span></strong>`;

			const totalTargetText = element.innerText;
			const totalMatch = totalTargetText.match(/Total Collected : RM ([\d,\.]+)/);
			const targetMatch = totalTargetText.match(/Total Target : RM ([\d,\.]+)/);

			if (totalMatch && targetMatch) {
				let total = parseFloat(totalMatch[1].replace(/,/g, ''));
				const target = parseFloat(targetMatch[1].replace(/,/g, ''));

				if (index > 0) {
					total -= records.reduce((sum, record) => (record.os.total < 0 ? sum + Math.abs(record.os.total) : sum), 0);
				}

				const correctPercentage = (total / target) * 100;
				if (UserProfile.team === "alpha") {
					updateProgressBar(index, correctPercentage, elementId, agingTargets);
				}
			} else {
				console.log(`Could not extract total and target from text for ${elementId}`);
			}
		} else {
			console.log(`Element with ID '${elementId}' does not exist.`);
		}
	});
}



function updateProgressBar(index, correctPercentage, elementId, agingTargets) {
	const progressBarContainerId = `Content_prog_${index === 0 ? 'totaltarget' : 'Aging' + agingTargets[index] + 'target'}`;
	const progressBarContainer = document.getElementById(progressBarContainerId);
	if (progressBarContainer) {
		const newProgressBar = createProgressBar(correctPercentage, elementId, index, agingTargets);
		progressBarContainer.appendChild(newProgressBar);
	}
}

function createProgressBar(correctPercentage, elementId, index, agingTargets) {
	const newProgressBar = document.createElement('div');
	newProgressBar.id = `correctedPercentageBar_${elementId === 'ctl00_Content_txtTotalTarget' ? 'TotalTarget' : 'Aging' + agingTargets[index]}`;
	newProgressBar.className = 'progress-bar';
	newProgressBar.style.backgroundColor = '#FFA500';
	newProgressBar.style.borderColor = '#FFA500';
	newProgressBar.style.width = `${correctPercentage.toFixed(1)}%`;

	const newProgressValue = document.createElement('div');
	newProgressValue.className = 'progress-value';
	newProgressValue.innerText = `${correctPercentage.toFixed(1)} %`;

	const updateColor = () => {
		newProgressValue.style.color = window.innerWidth < 768 ? 'black' : '#FFA500';
		newProgressValue.style.borderColor = window.innerWidth < 768 ? 'black' : '#FFA500';
	};

	updateColor();
	window.addEventListener('resize', updateColor);

	newProgressBar.appendChild(newProgressValue);
	return newProgressBar;
}





function selectFollowUpdateDate() {
    const input = document.getElementById("ctl00_Content_dpFollowUpdateDate_dateInput");
    const popupButton = document.getElementById("ctl00_Content_dpFollowUpdateDate_popupButton");

    if (!input || !popupButton) {
        console.error("Required elements are not found.");
        return;
    }

    const currentDate = new Date();
    const targetDate = ("0" + currentDate.getDate()).slice(-2);
    input.value = targetDate;

    const recall = Number(targetDate);

    // Click the popup button to open the calendar
    popupButton.click();

    // Wait for the popup to load
    setTimeout(() => {
        const dateLinks = document.querySelectorAll("a");
        let matchedIndex = -1;
        let count = 0;

        // Find the index of the first occurrence and count the occurrences
        for (let i = 0; i < dateLinks.length; i++) {
            if (dateLinks[i].textContent === targetDate) {
                if (matchedIndex === -1) {
                    // Track the first occurrence
                    matchedIndex = i;
                }
                count++;
                if (recall >= 20 && count === 2) {
                    // For recall >= 20, select the second occurrence if there are exactly two
                    matchedIndex = i;
                    break;
                }
            }
        }

        // Click the appropriate date link based on the recall condition
        if (matchedIndex !== -1) {
            dateLinks[matchedIndex].click();

            // After clicking the date, click the input field
            setTimeout(() => {
                input.click();
            }, 1000); // Adjust the timeout as needed to ensure the date click is processed
        }
    }, 100); // Adjust the timeout duration as needed
}

function beginOpenFile(sid) {
    const table = document.getElementById("ctl00_Content_RadGrid_Sales_ctl00");

    if (!table) {
        console.error("Table not found.");
        return;
    }

    function checkForMatch() {
        const tbody = table.querySelector("tbody");
        if (!tbody) return;

        // Case 1: Check for "No target found"
        const noRecordRow = tbody.querySelector("tr.rgNoRecords");
        if (noRecordRow) {
            const divText = noRecordRow.querySelector("div")?.innerText.trim();
            if (divText === "No target found.") {
                Snackbar.show(`File ${sid} not found...`);
                Memory.clear();
                return;
            }
        }

        // Case 2: Look for specific row by ID
        const matchedRow = table.querySelector("tr[id='ctl00_Content_RadGrid_Sales_ctl00__0']");
        if (matchedRow) {
            const link = matchedRow.cells[0]?.getElementsByTagName("input")[0];
            if (link) {
                Memory.clear();
                link.click();
                console.log(`Matched and clicked file: ${sid}`);
                window.close(); // optional
                return;
            }
        }

        // Still loading — keep checking
        requestAnimationFrame(checkForMatch);
    }

    checkForMatch();
}

async function drop() {
    if (!confirm("Remove all data?")) return;

    // Define the filename based on your existing logic
    const filename = `${UserProfile.userId}_cxdata_${DateUtils.monthId}_${DateUtils.shortYearString}`;
    const collectionName = `cmdb${DateUtils.monthId}_${DateUtils.shortYearString}`;
    
    // Directly remove the item from localStorage
    window.localStorage.removeItem(filename);

    // Remove data from Firestore
    const UserDatabase = firebase.firestore();
    
    // Show initial snackbar message indicating that the removal operation is in progress
    Snackbar.show("Removing data... Please wait.");
    
    try {
        // Delete the document from Firestore
        await UserDatabase.collection(collectionName).doc(filename).delete();
        console.log("Data successfully removed from Firestore!");

        // Clear in-memory data if any
        Memory.clear();
        
        // Notify user of successful removal
        Snackbar.show("Data removed successfully.");
        
        // Reload the page after a short delay
        setTimeout(() => {
            location.reload();
        }, 1500);
    } catch (error) {
        console.error("Error removing document from Firestore: ", error);
        Snackbar.show("Failed to remove data.");
    }
}


		function drop1() {
			if (!confirm("Log Out?")) return;
			sessionStorage.removeItem("togglerPosition");
	        sessionStorage.removeItem("toolbarState");
			// Clear encrypted userId cache (cleanup for any residual cache)
			sessionStorage.removeItem("_encUserId");

			// Directly remove the item from localStorage
			localStorage.removeItem("val");

			// Clear any additional memory if needed
			Memory.clear();
		
			// Reload the page after a delay
			setTimeout(() => {
				location.reload();
			}, 1500);
		
			// Show a Snackbar message
			Snackbar.show("Logged out");
		}

        function clearMem() {
			Memory.clear();
			Snackbar.show("Memory cleared...");
			setTimeout(() => location.reload(), 1000);
		}

        function clickCancelButton() {
            const cancelButton = document.getElementById("ctl00_Content_btnCancelSearch_input");
            if (cancelButton) {
                console.log("Clicking Cancel Search button...");
                cancelButton.click();
            } else {
                console.warn("Cancel button not found.");
            }
        }

        function showLabelModal(sid, currentLabel, triggerElement) {
            // Remove existing modal if any
            const existingModal = document.getElementById('whatsapp-label-modal');
            if (existingModal) {
                existingModal.remove();
            }
            
            // Create modal overlay
            const modalOverlay = document.createElement('div');
            modalOverlay.id = 'whatsapp-label-modal';
            modalOverlay.style.cssText = `
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0, 0, 0, 0.4);
                z-index: 10000;
                display: flex;
                align-items: center;
                justify-content: center;
                animation: fadeIn 0.2s ease;
            `;
            
            // Create modal content
            const modalContent = document.createElement('div');
            modalContent.style.cssText = `
                background: white;
                border-radius: 12px;
                width: 320px;
                max-width: 90vw;
                box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
                overflow: hidden;
                animation: slideUp 0.3s ease;
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            `;
            
            // Modal header
            const header = document.createElement('div');
            header.style.cssText = `
                padding: 20px 20px 16px;
                border-bottom: 1px solid #e1e8ed;
                background: #f8f9fa;
            `;
            header.innerHTML = `
                <h3 style="margin: 0; font-size: 16px; font-weight: 500; color: #111b21;">Select Label</h3>
            `;
            
            // Modal body with label options
            const body = document.createElement('div');
            body.style.cssText = `
                padding: 8px 0;
            `;
            
            // Get labels from localStorage or use defaults with your custom colors
            const getLabels = () => {
                const stored = localStorage.getItem('whatsapp-labels');
                if (stored) {
                    return JSON.parse(stored);
                }
                return [
                    { value: '1', text: 'Label 1', color: '#FF5722' }, // Orange
                    { value: '2', text: 'Label 2', color: '#42A5F5' }, // Light Blue
                    { value: '3', text: 'Label 3', color: '#FFCB00' }, // Yellow
                    { value: '4', text: 'Label 4', color: '#009688' }  // Teal
                ];
            };
            
            // Save labels to localStorage
            const saveLabels = (labels) => {
                localStorage.setItem('whatsapp-labels', JSON.stringify(labels));
            };
            
            // Show edit input for a label
            const showEditInput = (labelOption, label, labels) => {
                const originalContent = labelOption.innerHTML;
                
                labelOption.innerHTML = `
                    <div style="
                        width: 20px;
                        height: 20px;
                        border-radius: 50%;
                        background-color: ${label.color};
                        margin-right: 12px;
                    "></div>
                    <input type="text" value="${label.text}" style="
                        flex: 1;
                        border: 1px solid #25d366;
                        border-radius: 4px;
                        padding: 4px 8px;
                        font-size: 15px;
                        color: #111b21;
                        outline: none;
                        margin-right: 8px;
                    " />
                    <button style="
                        background: #25d366;
                        color: white;
                        border: none;
                        border-radius: 4px;
                        padding: 4px 8px;
                        cursor: pointer;
                        font-size: 12px;
                        margin-right: 4px;
                    ">Save</button>
                    <button style="
                        background: #8696a0;
                        color: white;
                        border: none;
                        border-radius: 4px;
                        padding: 4px 8px;
                        cursor: pointer;
                        font-size: 12px;
                    ">Cancel</button>
                `;
                
                const input = labelOption.querySelector('input');
                const saveBtn = labelOption.querySelector('button');
                const cancelBtn = labelOption.querySelectorAll('button')[1];
                
                input.focus();
                input.select();
                
                const saveEdit = () => {
                    const newText = input.value.trim();
                    if (newText && newText !== label.text) {
                        label.text = newText;
                        saveLabels(labels);
                    }
                    renderLabels();
                };
                
                const cancelEdit = () => {
                    renderLabels();
                };
                
                saveBtn.addEventListener('click', saveEdit);
                cancelBtn.addEventListener('click', cancelEdit);
                
                input.addEventListener('keydown', (e) => {
                    if (e.key === 'Enter') {
                        saveEdit();
                    } else if (e.key === 'Escape') {
                        cancelEdit();
                    }
                });
            };
            
            // Render all labels
            const renderLabels = () => {
                body.innerHTML = '';
                const labels = getLabels();
                
                labels.forEach(label => {
                    const option = document.createElement('div');
                    option.style.cssText = `
                        padding: 12px 20px;
                        cursor: pointer;
                        display: flex;
                        align-items: center;
                        transition: background-color 0.2s ease;
                        border-bottom: 1px solid #f0f2f5;
                    `;
                    
                    const isSelected = currentLabel == label.value;
                    
                    option.innerHTML = `
                        <div style="
                            width: 20px;
                            height: 20px;
                            border-radius: 50%;
                            background-color: ${label.color};
                            margin-right: 12px;
                            display: flex;
                            align-items: center;
                            justify-content: center;
                        ">
                            ${isSelected ? '<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="3"><polyline points="20,6 9,17 4,12"></polyline></svg>' : ''}
                        </div>
                        <span style="
                            font-size: 15px;
                            color: #111b21;
                            font-weight: ${isSelected ? '500' : '400'};
                            flex: 1;
                        ">${label.text}</span>
                        <button class="edit-btn" style="
                            background: transparent;
                            border: none;
                            cursor: pointer;
                            padding: 4px;
                            margin-right: 8px;
                            border-radius: 4px;
                            display: flex;
                            align-items: center;
                            justify-content: center;
                        " title="Edit label">
                            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#8696a0" stroke-width="2">
                                <path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
                                <path d="m18.5 2.5 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
                            </svg>
                        </button>
                        ${isSelected ? '<div><svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#25d366" stroke-width="2"><polyline points="20,6 9,17 4,12"></polyline></svg></div>' : ''}
                    `;
                    
                    const editBtn = option.querySelector('.edit-btn');
                    
                    // Edit button hover effects
                    editBtn.addEventListener('mouseenter', () => {
                        editBtn.style.backgroundColor = '#f5f6f6';
                    });
                    
                    editBtn.addEventListener('mouseleave', () => {
                        editBtn.style.backgroundColor = 'transparent';
                    });
                    
                    // Edit button click
                    editBtn.addEventListener('click', (e) => {
                        e.stopPropagation();
                        showEditInput(option, label, labels);
                    });
                    
                    // Option hover effects
                    option.addEventListener('mouseenter', () => {
                        if (!option.querySelector('input')) {
                            option.style.backgroundColor = '#f5f6f6';
                        }
                    });
                    
                    option.addEventListener('mouseleave', () => {
                        if (!option.querySelector('input')) {
                            option.style.backgroundColor = 'transparent';
                        }
                    });
                    
                    // Option click to select label
                    option.addEventListener('click', (e) => {
                        if (!option.querySelector('input') && !e.target.closest('.edit-btn')) {
                            Quick.setLabel(sid, label.value);
                            updateButtonText(triggerElement, label.text);
                            closeModal();
                        }
                    });
                    
                    body.appendChild(option);
                });
                
                // Clear option
                if (currentLabel) {
                    const clearOption = document.createElement('div');
                    clearOption.style.cssText = `
                        padding: 12px 20px;
                        cursor: pointer;
                        display: flex;
                        align-items: center;
                        transition: background-color 0.2s ease;
                        border-top: 1px solid #e1e8ed;
                        margin-top: 8px;
                    `;
                    
                    clearOption.innerHTML = `
                        <div style="
                            width: 20px;
                            height: 20px;
                            border-radius: 50%;
                            border: 2px solid #8696a0;
                            margin-right: 12px;
                            display: flex;
                            align-items: center;
                            justify-content: center;
                        ">
                            <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#8696a0" stroke-width="2">
                                <line x1="18" y1="6" x2="6" y2="18"></line>
                                <line x1="6" y1="6" x2="18" y2="18"></line>
                            </svg>
                        </div>
                        <span style="font-size: 15px; color: #8696a0;">Clear Label</span>
                    `;
                    
                    clearOption.addEventListener('mouseenter', () => {
                        clearOption.style.backgroundColor = '#f5f6f6';
                    });
                    
                    clearOption.addEventListener('mouseleave', () => {
                        clearOption.style.backgroundColor = 'transparent';
                    });
                    
                    clearOption.addEventListener('click', () => {
                        Quick.clearLabel(sid);
                        updateButtonText(triggerElement, 'Select Label');
                        closeModal();
                    });
                    
                    body.appendChild(clearOption);
                }
            };
            
            // Initial render
            renderLabels();
            
            // Assemble modal
            modalContent.appendChild(header);
            modalContent.appendChild(body);
            modalOverlay.appendChild(modalContent);
            
            // Add to DOM
            document.body.appendChild(modalOverlay);
            
            // Close modal functions
            function closeModal() {
                modalOverlay.style.animation = 'fadeOut 0.2s ease';
                modalContent.style.animation = 'slideDown 0.2s ease';
                setTimeout(() => {
                    if (modalOverlay.parentNode) {
                        modalOverlay.remove();
                    }
                }, 200);
            }
            
            // Close on overlay click
            modalOverlay.addEventListener('click', (e) => {
                if (e.target === modalOverlay) {
                    closeModal();
                }
            });
            
            // Close on Escape key
            const handleEscape = (e) => {
                if (e.key === 'Escape') {
                    closeModal();
                    document.removeEventListener('keydown', handleEscape);
                }
            };
            document.addEventListener('keydown', handleEscape);
            
            // Add CSS animations if not already present
            if (!document.getElementById('whatsapp-modal-styles')) {
                const styleSheet = document.createElement('style');
                styleSheet.id = 'whatsapp-modal-styles';
                styleSheet.textContent = `
                    @keyframes fadeIn {
                        from { opacity: 0; }
                        to { opacity: 1; }
                    }
                    @keyframes fadeOut {
                        from { opacity: 1; }
                        to { opacity: 0; }
                    }
                    @keyframes slideUp {
                        from { transform: translateY(20px); opacity: 0; }
                        to { transform: translateY(0); opacity: 1; }
                    }
                    @keyframes slideDown {
                        from { transform: translateY(0); opacity: 1; }
                        to { transform: translateY(20px); opacity: 0; }
                    }
                `;
                document.head.appendChild(styleSheet);
            }
        }
        
        // Helper function to update button text
        function updateButtonText(buttonElement, newText) {
            const labelTextSpan = buttonElement.querySelector('.label-text');
            if (labelTextSpan) {
                labelTextSpan.textContent = newText;
            }
        }
        
        // Add CSS animations to the page
        if (!document.getElementById('whatsapp-modal-styles')) {
            const style = document.createElement('style');
            style.id = 'whatsapp-modal-styles';
            style.textContent = `
                @keyframes fadeIn {
                    from { opacity: 0; }
                    to { opacity: 1; }
                }
                
                @keyframes fadeOut {
                    from { opacity: 1; }
                    to { opacity: 0; }
                }
                
                @keyframes slideUp {
                    from { 
                        opacity: 0;
                        transform: translateY(20px) scale(0.95);
                    }
                    to { 
                        opacity: 1;
                        transform: translateY(0) scale(1);
                    }
                }
                
                @keyframes slideDown {
                    from { 
                        opacity: 1;
                        transform: translateY(0) scale(1);
                    }
                    to { 
                        opacity: 0;
                        transform: translateY(20px) scale(0.95);
                    }
                }
            `;
            document.head.appendChild(style);
        }
