// maintenance.js
let maintenanceReferenceData = {
    vehicles: [],
    drivers: [],
    users: [],
    workshops: [],
    stage_actions: {}
};
let rfqWorkshopMap = {};
let rfqWorkScopeLineItems = [];


	
let rfqRowsById = {};

function getStageRoleMatrix() {
    return (maintenanceReferenceData && maintenanceReferenceData.stage_role_matrix) || {};
}

$(function () {
    const today = new Date().toISOString().slice(0, 10);
    $('#maintenance_complaint_date').val(today);
    $('#maintenance_complaint_log_date').val(today);

    loadMaintenanceReferenceData().always(function () {
        loadMaintenanceJobs();
    });

    $('#btnNewMaintenanceJob').on('click', function () {
        resetMaintenanceJobForm();
        setMaintenanceJobFormMode('create');
        $('#maintenanceJobModalTitle').text('Log Complaint / Create Job');
        showMaintenanceJobModal();
    });

    $('#saveMaintenanceJob').on('click', saveMaintenanceJob);
    $('#submitMaintenanceAction').on('click', function () {
        submitMaintenanceAction();
    });
    $('#rejectComplaintAction').on('click', function () {
        submitMaintenanceAction('reject_complaint');
    });
    $('#btnRefreshMaintenance').on('click', loadMaintenanceJobs);
    $('#maintenanceStageFilter').on('change', loadMaintenanceJobs);
    $('#maintenanceSearch').on('keyup', loadMaintenanceJobs);

    $('#btnExportCsv, #btnExportXlsx, #btnExportPdf').on('click', function () {
        var format = this.id.replace('btnExport', '').toLowerCase();
        var params = {
            type: 'maintenance',
            format: format,
            stage: $('#maintenanceStageFilter').val(),
            search: $('#maintenanceSearch').val(),
            start_date: $('#maintenanceDateFrom').val(),
            end_date: $('#maintenanceDateTo').val()
        };
        var qs = Object.keys(params).map(function(k) { return encodeURIComponent(k)+'='+encodeURIComponent(params[k]); }).join('&');
        window.open('api/data_export.php?' + qs, '_blank');
    });

    $(document).on('click', '.btn-edit-maintenance-job', function () {
        const jobId = $(this).data('id');
        if (!jobId) {
            alert('Invalid job ID for edit.');
            return;
        }

        $.ajax({
            url: 'api/maintenance.php',
            method: 'GET',
            dataType: 'json',
            data: { action: 'job_details', job_id: jobId },
            success: function (res) {
                if (res.status !== 'success') {
                    alert('Unable to load job details: ' + (res.message || 'Unknown error'));
                    return;
                }
                populateMaintenanceJobForm(res.data);
            },
            error: function (jqXHR, textStatus, errorThrown) {
                alert('Error loading job details: ' + textStatus + ' ' + errorThrown);
                console.error('Job details load failed:', textStatus, errorThrown, jqXHR.responseText);
            }
        });
    });

    $(document).on('click', '.btn-open-maintenance-timeline', function () {
        openMaintenanceTimeline($(this).data('id'));
    });

    $(document).on('click', '.btn-open-maintenance-action', function () {
        const jobId = $(this).data('id');
        const actionCode = $(this).data('action');
        const workshopName = $(this).data('workshop') || '';

        if (!jobId || !actionCode) {
            alert('Invalid maintenance action button');
            return;
        }

        openMaintenanceActionModal(jobId, actionCode, String(workshopName));
    });

    $(document).on('click', '.btn-open-rfq-manager', function () {
        const jobId = $(this).data('id');
        if (!jobId) {
            alert('Invalid maintenance job');
            return;
        }
        openRfqManager(jobId);
    });
    $(document).on('click', '.btn-set-final-workshop', function () {
        const rfqId = $(this).data('rfq-id');
        if (!rfqId) {
            alert('Invalid RFQ selection');
            return;
        }
        selectFinalWorkshopForJob(rfqId);
    });

    // RFQ vendor invite handlers are below (inline in this ready block)
    $('#btnUploadQuote').on('click', uploadQuoteForRfq);
    $('#btnAddQuoteLineItem').on('click', function () {
        addQuoteLineItemRow();
        updateQuoteLineItemsTotal();
    });
    $('#btnUseWorkScopeForQuote').on('click', function () {
        populateQuoteLineItemsFromWorkScope(true);
    });
    $('#addMaintenanceLineItem').on('click', function () {
        addMaintenanceLineItemRow();
    });
    $(document).on('click', '.remove-maintenance-line-item', function () {
        $(this).closest('tr').remove();
        updateMaintenanceLineItemsTotal();
    });
    $(document).on('input', '.maintenance-line-item-cost', updateMaintenanceLineItemsTotal);
    $(document).on('click', '.remove-quote-line-item', function () {
        $(this).closest('tr').remove();
        updateQuoteLineItemsTotal();
    });
    $(document).on('input', '.quote-line-item-description, .quote-line-item-cost', updateQuoteLineItemsTotal);

    $('#maintenance_action_workshop').on('change', function () {
        updateMaintenanceActionWorkshopIndicator();
    });

    $('#quote_rfq_id').on('change', function () {
        updateQuoteWorkshopIndicator();
    });

    // RFQ vendor invite: open modal
    $('#btnAddRfqVendor').on('click', function () {
        const jobId = $('#rfq_manager_job_id').val();
        const sel = document.getElementById('rfq_workshop_select');
        if (!sel) { showAlert('Workshop selector not found', 'danger'); return; }
        const opt = sel.options[sel.selectedIndex];
        if (!opt || !opt.value) { showAlert('Please select a workshop', 'warning'); return; }
        const vendorId = opt.value;
        const vendorEmail = opt.getAttribute('data-email') || '';

        const inviteModal = document.getElementById('rfqInviteModal');
        if (inviteModal) {
            $('#rfq_invite_email').val(vendorEmail);
            $('#rfq_invite_workshop_id').val(vendorId);
            $('#rfq_invite_job_id').val(jobId);
            new bootstrap.Modal(inviteModal).show();
        } else if (vendorEmail) {
            sendRfqInvite(vendorId, vendorEmail, jobId);
        } else {
            const email = prompt('Enter workshop email to invite:');
            if (email) sendRfqInvite(vendorId, email, jobId);
        }
    });

    // Confirm RFQ invite
    $('#btnConfirmRfqInvite').on('click', function () {
        const email = String($('#rfq_invite_email').val()).trim();
        const vendorId = $('#rfq_invite_workshop_id').val();
        const jobId = $('#rfq_invite_job_id').val();
        if (!email) { showAlert('Please provide an email', 'warning'); return; }
        try { bootstrap.Modal.getInstance(document.getElementById('rfqInviteModal')).hide(); } catch (e) {}

        $.post('api/rfq.php?action=invite', {
            workshop_id: vendorId,
            workshop_email: email,
            job_id: jobId
        }, function (res) {
            if (!res || (res.status !== 'success' && res.status !== 'exists')) {
                showAlert((res && res.message) || 'Failed to send invite', 'danger');
                return;
            }
            $.post('api/maintenance.php?action=stage_action', {
                job_id: jobId,
                action_code: 'send_rfq',
                workshop_id: vendorId,
                skip_invite_email: '1'
            }, function (res2) {
                if (!res2 || res2.status !== 'success') {
                    showAlert((res2 && res2.message) || 'RFQ saved but workflow stage failed', 'warning');
                } else {
                    showAlert('RFQ invitation sent and stage updated', 'success');
                }
                loadMaintenanceJobs();
                setTimeout(function () { openRfqManager(jobId); }, 250);
            }, 'json').fail(function () {
                showAlert('RFQ invite sent but failed to update stage', 'warning');
                loadMaintenanceJobs();
            });
        }, 'json').fail(function () {
            showAlert('Network error while sending invitation', 'danger');
        });
    });

    // Resend invite click (delegated)
    $(document).on('click', '.btn-resend-invite', function () {
        const rfqId = $(this).data('rfq-id');
        if (!rfqId) { showAlert('Invalid RFQ id', 'danger'); return; }
        const modalEl = document.getElementById('rfqResendModal');
        if (modalEl) {
            $('#rfq_resend_id').val(rfqId);
            $('#rfq_resend_force').prop('checked', false);
            new bootstrap.Modal(modalEl).show();
        } else {
            resendRfqInvite(rfqId, false);
        }
    });

    // Confirm resend
    $('#btnConfirmRfqResend').on('click', function () {
        const rfqId = $('#rfq_resend_id').val();
        const force = $('#rfq_resend_force').is(':checked');
        try { bootstrap.Modal.getInstance(document.getElementById('rfqResendModal')).hide(); } catch (e) {}
        resendRfqInvite(rfqId, force);
    });

    // Work Order validation: prevent "Work Order Raised" without a work order number
    initWorkOrderValidation();
});

function getRfqPolicy() {
    return (maintenanceReferenceData && maintenanceReferenceData.rfq_policy) || { min_vendors: 3, min_quotes_for_vetting: 2 };
}

function showAlert(message, type) {
    type = type || 'info';
    if (window.jQuery) {
        var $container = $('.rfq-workspace');
        var html = '<div class="alert alert-' + type + ' alert-dismissible" role="alert">' + message + '<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
        if ($container.length) {
            $container.prepend(html);
            setTimeout(function () { $container.find('.alert').fadeOut(300, function () { $(this).remove(); }); }, 4000);
            return;
        }
    }
    alert(message);
}

function initWorkOrderValidation() {
    var stageSel = document.getElementById('maintenance_workflow_stage');
    var workOrderInput = document.getElementById('maintenance_work_order_number');
    if (!stageSel) return;

    function updateWorkOrderOption() {
        var opt = Array.prototype.slice.call(stageSel.options).find(function (o) { return o.value === 'Work Order Raised'; });
        var hasWorkOrder = workOrderInput && workOrderInput.value && workOrderInput.value.trim() !== '';
        if (opt) {
            opt.disabled = !hasWorkOrder;
            if (opt.disabled && stageSel.value === 'Work Order Raised') {
                stageSel.value = (stageSel.options[0] && stageSel.options[0].value) || '';
                try {
                    var html = '<div class="alert alert-warning alert-dismissible fade show" role="alert">Cannot set to "Work Order Raised" without a Work Order number.<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button></div>';
                    var $container = $('.maintenance-hero-body');
                    if ($container.length) {
                        $container.prepend(html);
                        setTimeout(function () { $container.find('.alert').fadeOut(300, function () { $(this).remove(); }); }, 4000);
                    } else {
                        alert('Cannot set to "Work Order Raised" without a Work Order number.');
                    }
                } catch (e) {
                    alert('Cannot set to "Work Order Raised" without a Work Order number.');
                }
            }
        }
    }

    if (workOrderInput) {
        workOrderInput.addEventListener('input', updateWorkOrderOption);
        workOrderInput.addEventListener('change', updateWorkOrderOption);
    }

    var lastWorkOrderVal = workOrderInput ? workOrderInput.value : '';
    var workOrderPoll = null;
    function startWorkOrderPoll() {
        if (!workOrderInput || workOrderPoll) return;
        workOrderPoll = setInterval(function () {
            var v = workOrderInput.value || '';
            if (v !== lastWorkOrderVal) {
                lastWorkOrderVal = v;
                updateWorkOrderOption();
            }
        }, 300);
    }
    function stopWorkOrderPoll() {
        if (workOrderPoll) {
            clearInterval(workOrderPoll);
            workOrderPoll = null;
        }
    }

    $('#maintenanceJobModal').on('shown.bs.modal', startWorkOrderPoll);
    $('#maintenanceJobModal').on('hidden.bs.modal', stopWorkOrderPoll);

    if (stageSel) {
        stageSel.addEventListener('change', function () {
            var selected = stageSel.value;
            var hasWorkOrder = workOrderInput && workOrderInput.value && workOrderInput.value.trim() !== '';
            if (selected === 'Work Order Raised' && !hasWorkOrder) {
                updateWorkOrderOption();
            }
        });
    }

    updateWorkOrderOption();
}

function sendRfqInvite(vendorId, vendorEmail, jobId) {
    $.post('api/rfq.php?action=invite', {
        workshop_id: vendorId,
        workshop_email: vendorEmail,
        job_id: jobId
    }, function (res) {
        if (res && (res.status === 'success' || res.status === 'exists')) {
            showAlert(res.message || 'Invitation sent', 'success');
        } else {
            showAlert((res && res.message) || 'Failed to send invitation', 'danger');
        }
    }, 'json').fail(function () {
        showAlert('Network error while sending invitation', 'danger');
    });
}

function resendRfqInvite(rfqId, force) {
    $.post('api/rfq.php?action=resend', {
        rfq_id: rfqId,
        force: force ? '1' : '0'
    }, function (res) {
        if (res && res.status === 'success') {
            showAlert(res.message || 'Invite resent', 'success');
        } else if (res && res.status === 'throttled') {
            showAlert(res.message || 'Invite recently sent; try again later', 'warning');
        } else {
            showAlert((res && res.message) || 'Failed to resend invite', 'danger');
        }
    }, 'json').fail(function () {
        showAlert('Network error while resending invitation', 'danger');
    });
}

function loadMaintenanceReferenceData() {
    return $.get('api/maintenance.php', { action: 'reference_data' }, function (res) {
        if (res.status !== 'success') {
            alert(res.message || 'Unable to load maintenance references');
            return;
        }

        maintenanceReferenceData = res.data || maintenanceReferenceData;
        // Ensure client-side fallback for new payment-not-made action if backend hasn't provided it yet
        maintenanceReferenceData.stage_actions = maintenanceReferenceData.stage_actions || {};
        if (!maintenanceReferenceData.stage_actions['mark_not_paid']) {
            maintenanceReferenceData.stage_actions['mark_not_paid'] = {
                label: 'Mark Not Paid',
                description: 'Record that payment was not made for this requisition',
                requires_comment: true,
                button_tone: 'danger'
            };
        }
        if (!maintenanceReferenceData.stage_actions['mark_paid']) {
            maintenanceReferenceData.stage_actions['mark_paid'] = {
                label: 'Make Payment',
                description: 'Record that payment was made for this requisition',
                requires_comment: true,
                comment_label: 'Reason',
                button_tone: 'dark'
            };
        }
        populateSelect('#maintenance_vehicle_id', maintenanceReferenceData.vehicles || [], 'Select vehicle');
        populateSelect('#maintenance_driver_id', maintenanceReferenceData.drivers || [], 'Select driver');
        populateSelect('#maintenance_action_workshop', maintenanceReferenceData.workshops || [], 'Select workshop');
        populateSelect('#rfq_workshop_select', maintenanceReferenceData.workshops || [], 'Select workshop');
    }, 'json');
}

function shouldShowEditButton(row) {
    // Only the user who raised the complaint may edit, unless admin.
    if (String(window.CURRENT_USER_ROLE || '').toLowerCase() !== 'admin' && Number(row.created_by) > 0 && Number(row.created_by) !== Number(window.CURRENT_USER_ID)) {
        return false;
    }
    // Hide edit button after a quote has been submitted to prevent modifying an in-flight quote.
    return String(row.workflow_stage || row.status || '').toLowerCase() !== 'quote submitted';
}

function loadMaintenanceJobs() {
    $.get('api/maintenance.php', {
        action: 'jobs',
        status: $('#maintenanceStageFilter').val(),
        search: $('#maintenanceSearch').val(),
        start_date: $('#maintenanceDateFrom').val(),
        end_date: $('#maintenanceDateTo').val()
    }, function (res) {
        if (res.status !== 'success') {
            $('#maintenanceJobsTable').html('<tr><td colspan="8" class="text-center text-danger">Unable to load maintenance jobs.</td></tr>');
            return;
        }

        const rows = res.data || [];
        const summary = res.summary || {};
        $('#maintenanceTotalJobs').text(summary.total_jobs || 0);
        $('#maintenanceOpenJobs').text(summary.open_jobs || 0);
        $('#maintenanceWorkshopJobs').text(summary.in_workshop || 0);
        $('#maintenanceAwaitingApproval').text(summary.awaiting_approval || 0);

        let html = '';
        if (!rows.length) {
            html = '<tr><td colspan="7" class="text-center py-4 text-muted">No maintenance jobs found.</td></tr>';
        } else {
            $.each(rows, function (_, row) {
                html += `
                    <tr>
                        <td>
                            <div class="fw-semibold">${escapeHtml(row.plate_number || '-')}</div>
                            <div class="text-muted small">${escapeHtml(row.vehicle_name || '-')}</div>
                        </td>
                        <td>
                            <div class="fw-semibold">
                                ${escapeHtml(row.complaint_title || row.nature_of_fault || '-')}
                            </div>
                            <div class="text-muted small">${escapeHtml(row.complaint_source || '-')}</div>
                        </td>
                        <td>${escapeHtml(row.workshop || '-')}</td>
                        <td>${renderWorkOrderNumberCell(row)}</td>
                        <td>${renderWorkflowStatusCell(row)}</td>
                        <td>${escapeHtml(formatCurrency(row.approved_amount || row.quote_amount || row.cost_of_repair))}</td>
                        <td>
                            <div class="d-flex gap-1 flex-wrap">
                                ${shouldShowEditButton(row) ? `<button type="button" class="btn btn-sm btn-outline-primary btn-edit-maintenance-job" title="Edit" data-id="${row.id}">
                                    <i class="bi bi-pencil"></i>
                                </button>` : ''}
                                <button type="button" class="btn btn-sm btn-outline-secondary btn-open-maintenance-timeline" title="Timeline" data-id="${row.id}">
                                    <i class="bi bi-clock-history"></i>
                                </button>
                                ${buildActionButtons(row)}
                                ${buildRfqManagerButton(row)}
                                ${buildPdfDropdown(row)}
                            </div>
                        </td>
                    </tr>
                `;
            });
        }

        $('#maintenanceJobsTable').html(html);
    }, 'json');
}

let _submittingSave = false;
let _submittingAction = false;

function saveMaintenanceJob() {
    if (_submittingSave) return;
    const payload = {
        id: $('#maintenance_job_id').val(),
        vehicle_id: $('#maintenance_vehicle_id').val(),
        driver_id: $('#maintenance_driver_id').val(),
        complaint_source: $('#maintenance_complaint_source').val(),
        complaint_title: $('#maintenance_complaint_title').val().trim(),
        complaint_description: $('#maintenance_complaint_description').val().trim(),
        complaint_date: $('#maintenance_complaint_date').val(),
        complaint_log_date: $('#maintenance_complaint_log_date').val(),
        nature_of_fault: $('#maintenance_nature_of_fault').val() // Example completion
    };

    // Read mileage fields and validate rule: last < current < next
    const last = Number($('#maintenance_last_service_mileage_km').val() || '');
    const current = Number($('#maintenance_current_service_mileage_km').val() || '');
    const next = Number($('#maintenance_next_service_mileage_km').val() || '');

    // Ensure values are numbers and not NaN
    if (!Number.isFinite(last) || !Number.isFinite(current) || !Number.isFinite(next)) {
        alert('Please provide numeric values for Last Service, Current, and Next Service mileage.');
        return;
    }

    if (!(last < current && current < next)) {
        alert('Mileage validation failed: require Last Service Mileage < Current Mileage < Next Service Mileage.');
        return;
    }

    // attach mileage values to payload
    payload.last_service_mileage_km = last;
    payload.current_service_mileage_km = current;
    payload.next_service_mileage_km = next;

    _submittingSave = true;
    $('#saveMaintenanceJob').prop('disabled', true);
    $.post('api/maintenance.php?action=save_job', payload, function(res) {
        if (res.status === 'success') {
            $('#maintenanceJobModal').modal('hide');
            loadMaintenanceJobs();
        } else {
            alert('Error: ' + res.message);
        }
    }, 'json').always(function () {
        _submittingSave = false;
        $('#saveMaintenanceJob').prop('disabled', false);
    });
}

function populateMaintenanceJobForm(record) {
    resetMaintenanceJobForm();
    $('#maintenance_job_id').val(record.id || '');
    $('#maintenance_vehicle_id').val(record.vehicle_id || '');
    $('#maintenance_driver_id').val(record.driver_id || '');
    $('#maintenance_complaint_source').val(record.complaint_source || 'Fleet Office');
    $('#maintenance_complaint_title').val(record.complaint_title || '');
    $('#maintenance_complaint_description').val(record.complaint_description || '');
    $('#maintenance_work_order_number').val(record.work_order_number || '');
    $('#maintenance_complaint_date').val(normalizeDateInput(record.complaint_date));
    $('#maintenance_complaint_log_date').val(normalizeDateInput(record.complaint_log_date));
    $('#maintenance_nature_of_fault').val(record.nature_of_fault || '');
    $('#maintenance_current_service_mileage_km').val(record.current_service_mileage_km || '');
    $('#maintenance_last_service_mileage_km').val(record.last_service_mileage_km || '');
    $('#maintenance_next_service_mileage_km').val(record.next_service_mileage_km || '');
    // monthly mileage field removed
    $('#maintenance_comment').val(record.maintenance_comment || '');
    $('#maintenance_workflow_stage').val(record.workflow_stage || 'Complaint Logged');
    $('#maintenance_status').val(record.status || 'Pending');
    setMaintenanceJobFormMode('edit', record);
    $('#maintenanceJobModalTitle').text('Edit Maintenance Job');
    showMaintenanceJobModal();
}

function openMaintenanceActionModal(jobId, actionCode, workshopName = '', prefill = {}) {
    const def = (maintenanceReferenceData.stage_actions || {})[actionCode];
    if (!def) {
        alert('Unknown workflow action');
        return;
    }

    $('#maintenance_action_job_id').val(jobId);
    $('#maintenance_action_code').val(actionCode);
    $('#maintenance_action_amount').val('');
    $('#maintenance_action_comment').val('');
    $('#maintenance_action_workshop').val('');
    resetQuoteVettingPanel();
    resetMaintenanceLineItems();
    $('#maintenance_hod_override').prop('checked', false);
    $('#maintenance_hod_override_reason').val('');
    $('#maintenanceHodOverrideReasonGroup').addClass('d-none');
    $('#maintenanceActionWorkshopSelectedLabel').addClass('d-none').text('Selected workshop: -');
    $('#maintenanceActionModalTitle').text(def.label);
    configureMaintenanceActionModal(def);
    // Apply any prefill values provided by caller
    if (prefill) {
        if (prefill.amount !== undefined && prefill.amount !== null) {
            $('#maintenance_action_amount').val(Number(prefill.amount).toFixed ? Number(prefill.amount).toFixed(2) : prefill.amount);
        }
        if (prefill.comment !== undefined && prefill.comment !== null) {
            $('#maintenance_action_comment').val(prefill.comment);
        }
        if (prefill.workshop_id !== undefined && prefill.workshop_id !== null) {
            $('#maintenance_action_workshop').val(String(prefill.workshop_id));
            updateMaintenanceActionWorkshopIndicator();
        } else if (workshopName) {
            // keep existing behaviour: try to match workshop name text
            const targetText = String(workshopName).toLowerCase();
            $('#maintenance_action_workshop option').each(function () {
                if ($(this).text().trim().toLowerCase() === targetText) {
                    $('#maintenance_action_workshop').val($(this).val());
                }
            });
            updateMaintenanceActionWorkshopIndicator();
        }

        if (Array.isArray(prefill.line_items) && prefill.line_items.length) {
            resetMaintenanceLineItems();
            prefill.line_items.forEach(function (it) {
                addMaintenanceLineItemRow({ description: it.description || '', cost: it.cost !== undefined ? it.cost : (it.quoted_cost || it.price || '') });
            });
            updateMaintenanceLineItemsTotal();
            if (!$('#maintenanceActionLineItems tr').length) addMaintenanceLineItemRow();
        }
    }
    if (isQuoteDecisionAction(actionCode)) {
        loadQuoteVettingContext(jobId, actionCode);
    }
    if (workshopName) {
        const targetText = workshopName.toLowerCase();
        $('#maintenance_action_workshop option').each(function () {
            if ($(this).text().trim().toLowerCase() === targetText) {
                $('#maintenance_action_workshop').val($(this).val());
            }
        });
        updateMaintenanceActionWorkshopIndicator();
    }
    showMaintenanceActionModal();
}

function resetQuoteVettingPanel() {
    $('#maintenanceQuoteVettingPanel').addClass('d-none');
    $('#quoteVettingFinalBadge').removeClass('text-bg-success text-bg-warning').addClass('text-bg-light').text('No final workshop');
    $('#quoteVettingSummary').html('<div class="text-muted small">Loading quote context...</div>');
    $('#quoteVettingTable').html('<tr><td colspan="4" class="text-muted">Loading quotes...</td></tr>');
}

function loadQuoteVettingContext(jobId, actionCode) {
    $('#maintenanceQuoteVettingPanel').removeClass('d-none');
    $.get('api/maintenance.php', { action: 'rfq_list', job_id: jobId }, function (res) {
        if (res.status !== 'success') {
            $('#quoteVettingSummary').html(`<div class="text-danger small">${escapeHtml(res.message || 'Unable to load quote context')}</div>`);
            $('#quoteVettingTable').html('<tr><td colspan="4" class="text-danger">Unable to load quotes.</td></tr>');
            return;
        }

        const rows = (res.data && res.data.rows) ? res.data.rows : [];
        const stats = (res.data && res.data.stats) ? res.data.stats : { vendors: 0, quotes: 0 };
        const finalWorkshop = (res.data && res.data.final_workshop) ? res.data.final_workshop : '-';
        const job = (res.data && res.data.job) ? res.data.job : {};
        const lineItems = (res.data && res.data.line_items) ? res.data.line_items : [];
        renderQuoteVettingContext(rows, stats, finalWorkshop, job, lineItems, actionCode);
    }, 'json').fail(function () {
        $('#quoteVettingSummary').html('<div class="text-danger small">Server error while loading quote context.</div>');
        $('#quoteVettingTable').html('<tr><td colspan="4" class="text-danger">Unable to load quotes.</td></tr>');
    });
}

function renderQuoteVettingContext(rows, stats, finalWorkshop, job, lineItems, actionCode) {
    job = job || {};
    lineItems = Array.isArray(lineItems) ? lineItems : [];
    const vendors = Number(stats && stats.vendors ? stats.vendors : 0);
    const quotes = Number(stats && stats.quotes ? stats.quotes : 0);
    const selectedRow = rows.find(function (row) {
        return Number(row.is_final) === 1;
    });
    const comparableRows = rows
        .filter(function (row) {
            const amount = Number(row.quote_amount);
            return Number.isFinite(amount) && amount > 0;
        })
        .sort(function (a, b) {
            return Number(a.quote_amount) - Number(b.quote_amount);
        });
    const lowestRow = comparableRows.length ? comparableRows[0] : null;
    const vettedSourceRow = selectedRow || lowestRow;
    const lineItemTotal = calculateLineItemsTotal(lineItems);
    const sourceAmount = getQuoteDecisionAmountSource(actionCode, job, vettedSourceRow);
    const hasLineItems = Array.isArray(lineItems) && lineItems.length > 0;
    const finalReady = !!selectedRow && finalWorkshop && finalWorkshop !== '-' && hasLineItems;

    $('#quoteVettingFinalBadge')
        .removeClass('text-bg-light text-bg-warning text-bg-success')
        .addClass(finalReady ? 'text-bg-success' : 'text-bg-warning')
        .text(finalReady
            ? `Final: ${finalWorkshop}`
            : (selectedRow && finalWorkshop && finalWorkshop !== '-'
                ? 'Add quote line items before raising job order'
                : 'Select final workshop first'));


    if (sourceAmount !== null && !$('#maintenance_action_amount').val()) {
        $('#maintenance_action_amount').val(sourceAmount.toFixed(2));
    }

    const policy = getRfqPolicy();
    const minV = policy.min_vendors || 3;
    const minQ = policy.min_quotes_for_vetting || 2;
    $('#quoteVettingSummary').html(`
        <div class="quote-vetting-grid">
            <div class="quote-vetting-stat ${lineItems.length ? 'is-ready' : ''}">
                <span>Work Scope</span>
                <strong>${escapeHtml(String(lineItems.length))} item${lineItems.length === 1 ? '' : 's'}</strong>
                <small>${lineItems.length ? `Estimated ${escapeHtml(formatCurrency(lineItemTotal))}` : 'No line items'}</small>
            </div>
            <div class="quote-vetting-stat ${vendors >= minV ? 'is-ready' : ''}">
                <span>Vendor Coverage</span>
                <strong>${escapeHtml(String(vendors))}/${minV}</strong>
                <small>${vendors >= minV ? 'Ready' : 'Needs vendors'}</small>
            </div>
            <div class="quote-vetting-stat ${quotes >= minQ ? 'is-ready' : ''}">
                <span>Comparable Quotes</span>
                <strong>${escapeHtml(String(quotes))}/${minQ}</strong>
                <small>${quotes >= minQ ? 'Ready' : 'Needs quotes'}</small>
            </div>
            <div class="quote-vetting-stat ${lowestRow ? 'is-ready' : ''}">
                <span>Lowest Quote</span>
                <strong>${lowestRow ? escapeHtml(formatCurrency(lowestRow.quote_amount)) : '-'}</strong>
                <small>${lowestRow ? escapeHtml(lowestRow.workshop_name || 'Workshop') : 'No quote yet'}</small>
            </div>
        </div>
    `);
    // Render per-RFQ rows for vetting table
    if (!rows.length) {
        $('#quoteVettingTable').html('<tr><td colspan="4" class="text-muted">No RFQ vendors have been added yet.</td></tr>');
        return;
    }

    const html = rows.map(function (row) {
        const amount = Number(row.quote_amount);
        const hasQuote = Number.isFinite(amount) && amount > 0;
        const isSelected = Number(row.is_final) === 1;
        const quoteLabel = hasQuote ? formatCurrency(amount) : 'No quote';
        const statusLabel = hasQuote ? 'Submitted' : 'Awaiting';

        return `
            <tr class="${isSelected ? 'table-success' : ''}">
                <td>${escapeHtml(row.workshop_name || '-')}</td>
                <td>${escapeHtml(quoteLabel)}</td>
                <td><span class="badge bg-light text-dark border">${escapeHtml(statusLabel)}</span></td>
                <td>${isSelected ? '<span class="badge bg-success">Final workshop</span>' : '-'}</td>
            </tr>
        `;
    }).join('');

    $('#quoteVettingTable').html(html);
}

function isQuoteDecisionAction(actionCode) {
    return ['vet_quote', 'raise_job_order', 'approve_job_order'].includes(String(actionCode || ''));
}

function getQuoteDecisionAmountSource(actionCode, job, selectedQuoteRow) {
    const selectedQuoteAmount = selectedQuoteRow ? Number(selectedQuoteRow.quote_amount) : NaN;
    const vettedAmount = Number(job && job.vetted_amount);
    const quoteAmount = Number(job && job.quote_amount);

    if (actionCode === 'raise_job_order' || actionCode === 'approve_job_order') {
        if (Number.isFinite(vettedAmount) && vettedAmount > 0) return vettedAmount;
        if (Number.isFinite(selectedQuoteAmount) && selectedQuoteAmount > 0) return selectedQuoteAmount;
        if (Number.isFinite(quoteAmount) && quoteAmount > 0) return quoteAmount;
        return null;
    }

    if (Number.isFinite(selectedQuoteAmount) && selectedQuoteAmount > 0) return selectedQuoteAmount;
    if (Number.isFinite(quoteAmount) && quoteAmount > 0) return quoteAmount;
    return null;
}

function configureMaintenanceActionModal(def) {
    $('#maintenanceActionHelp').text(def.description || 'Apply the next workflow step for this job.');

    const actionCode = $('#maintenance_action_code').val();
    const isComplaintApproval = actionCode === 'maintenance_review';
    const isQuoteVetting = actionCode === 'vet_quote';
    const isQuoteDecision = isQuoteDecisionAction(actionCode);
    const amountGroup = $('#maintenanceActionAmountGroup');
    const amountLabel = $('#maintenanceActionAmountLabel');
    const amountHelp = $('#maintenanceActionAmountHelp');
    const commentLabel = $('#maintenanceActionCommentLabel');
    const commentHelp = $('#maintenanceActionCommentHelp');
    const commentField = $('#maintenance_action_comment');
    const amountField = $('#maintenance_action_amount');
    const workshopGroup = $('#maintenanceActionWorkshopGroup');
    const workshopLabel = $('#maintenanceActionWorkshopLabel');
    const workshopHelp = $('#maintenanceActionWorkshopHelp');
    const workshopField = $('#maintenance_action_workshop');
    const workshopSelectedLabel = $('#maintenanceActionWorkshopSelectedLabel');
    const lineItemsGroup = $('#maintenanceActionLineItemsGroup');
    const overrideSection = $('#maintenanceHodOverrideSection');
    const overrideReasonGroup = $('#maintenanceHodOverrideReasonGroup');
    const overrideCheckbox = $('#maintenance_hod_override');
    const submitButton = $('#submitMaintenanceAction');
    const rejectButton = $('#rejectComplaintAction');
    const currentRole = String(window.CURRENT_USER_ROLE || '').toLowerCase();
    const canOverride = currentRole === 'admin' || currentRole === 'hod';

    if (def.requires_amount) {
        amountGroup.show();
        amountLabel.text(def.amount_label || 'Amount');
        amountHelp.text(def.amount_help || 'Enter amount for this stage.');
        amountField.prop('required', true);
    } else {
        amountGroup.hide();
        amountField.prop('required', false);
    }

    if (def.requires_comment) {
        commentLabel.text(def.comment_label || (isComplaintApproval ? 'Maintenance Office Comment' : 'Comment'));
        commentHelp.text(def.comment_help || 'Add context for reviewers, workshop, or internal control.');
        commentField.prop('required', true);
    } else {
        commentLabel.text(def.comment_label || (isComplaintApproval ? 'Maintenance Office Comment' : 'Comment'));
        commentHelp.text(def.comment_help || 'Add context for reviewers, workshop, or internal control.');
        commentField.prop('required', false);
    }

    if (def.requires_workshop) {
        workshopGroup.removeClass('d-none');
        workshopLabel.text(def.workshop_label || 'Workshop');
        workshopHelp.text(def.workshop_help || 'Select workshop for this action.');
        workshopField.prop('required', true);
        workshopSelectedLabel.removeClass('d-none');
        updateMaintenanceActionWorkshopIndicator();
    } else {
        workshopGroup.addClass('d-none');
        workshopField.prop('required', false);
        workshopField.val('');
        workshopSelectedLabel.addClass('d-none').text('Selected workshop: -');
    }

    if (def.requires_line_items) {
        lineItemsGroup.removeClass('d-none');
        if (!$('#maintenanceActionLineItems tr').length) {
            addMaintenanceLineItemRow();
        }
    } else {
        lineItemsGroup.addClass('d-none');
        resetMaintenanceLineItems();
    }

    if (def.allow_hod_override && canOverride) {
        overrideSection.removeClass('d-none');
    } else {
        overrideSection.addClass('d-none');
        overrideReasonGroup.addClass('d-none');
        overrideCheckbox.prop('checked', false);
        $('#maintenance_hod_override_reason').val('');
    }

    overrideCheckbox.off('change').on('change', function () {
        if (this.checked) {
            overrideReasonGroup.removeClass('d-none');
        } else {
            overrideReasonGroup.addClass('d-none');
            $('#maintenance_hod_override_reason').val('');
        }
    });

    if (isComplaintApproval) {
        setActionSubmitButtonTone(submitButton, 'Approve', 'success');
        rejectButton.removeClass('d-none');
    } else if (isQuoteDecision) {
        setActionSubmitButtonTone(submitButton, isQuoteVetting ? 'Vet Selected Quote' : 'Approve Final Amount', isQuoteVetting ? 'primary' : 'success');
        rejectButton.addClass('d-none');
    } else {
        setActionSubmitButtonTone(submitButton, 'Apply Action', 'primary');
        rejectButton.addClass('d-none');
    }
}

function setActionSubmitButtonTone(button, label, tone) {
    button
        .text(label)
        .removeClass('btn-primary btn-secondary btn-success btn-danger btn-warning btn-info btn-dark')
        .addClass(`btn-${tone}`);
}

function submitMaintenanceAction(actionCodeOverride) {
    if (_submittingAction) return;
    const actionCode = actionCodeOverride || $('#maintenance_action_code').val();
    const payload = {
        job_id: $('#maintenance_action_job_id').val(),
        action_code: actionCode,
        amount: $('#maintenance_action_amount').val(),
        comment: $('#maintenance_action_comment').val().trim(),
        workshop_id: $('#maintenance_action_workshop').val(),
        hod_override: $('#maintenance_hod_override').is(':checked') ? '1' : '0',
        hod_override_reason: $('#maintenance_hod_override_reason').val().trim()
    };

    if (!payload.job_id || !payload.action_code) {
        alert('Invalid workflow action');
        return;
    }
    const def = (maintenanceReferenceData.stage_actions || {})[payload.action_code] || {};
    if (def.requires_workshop && !payload.workshop_id) {
        alert('Workshop is required for this action');
        return;
    }
    if (def.requires_amount && !def.requires_line_items) {
        const amount = Number(payload.amount);
        if (!Number.isFinite(amount) || amount <= 0) {
            alert((def.amount_label || 'Amount') + ' must be greater than 0');
            return;
        }
    }
    if (def.requires_comment && !payload.comment) {
        alert((def.comment_label || 'Comment') + ' is required');
        return;
    }
    if (def.requires_line_items) {
        const lineItemResult = collectMaintenanceLineItems();
        if (lineItemResult.error) {
            alert(lineItemResult.error);
            return;
        }
        payload.line_items = JSON.stringify(lineItemResult.items);
        payload.amount = lineItemResult.total.toFixed(2);
    }
    if (payload.hod_override === '1' && !payload.hod_override_reason) {
        alert('HOD override reason is required');
        return;
    }

    // Intercept Send RFQ to prompt for/confirm the workshop email (prefilled from DB)
    if (String(payload.action_code).toLowerCase() === 'send_rfq') {
        const workshopId = $('#maintenance_action_workshop').val();
        if (!workshopId) {
            alert('Select a workshop before sending RFQ');
            return;
        }
        // find workshop in reference data
        const workshop = (maintenanceReferenceData.workshops || []).find(function (w) { return String(w.id) === String(workshopId); }) || null;
        const email = (workshop && workshop.email) ? workshop.email : '';
        const name = (workshop && (workshop.label || workshop.name)) ? (workshop.label || workshop.name) : '';

        // Prefill invite modal
        const inviteModalEl = document.getElementById('rfqInviteModal');
            if (inviteModalEl) {
                document.getElementById('rfq_invite_email').value = email;
                document.getElementById('rfq_invite_workshop_id').value = workshopId;
                document.getElementById('rfq_invite_job_id').value = payload.job_id;
                try { document.getElementById('rfq_invite_workshop_name').textContent = name || ''; } catch (e) {}
                const modal = new bootstrap.Modal(inviteModalEl);
                modal.show();
                return; // wait for user to confirm invite before applying stage action
            }
        }

    _submittingAction = true;
    $('#submitMaintenanceAction, #rejectComplaintAction').prop('disabled', true);
    $.ajax({
        url: 'api/maintenance.php?action=stage_action',
        method: 'POST',
        dataType: 'json',
        data: payload,
        success: function (res) {
            if (res.status !== 'success') {
                alert(res.message || 'Unable to apply maintenance action');
                return;
            }

            hideMaintenanceActionModal();
            loadMaintenanceJobs();
            // If we just sent RFQs, open the RFQ manager so user can add/upload quotes immediately
            if (String(actionCode).toLowerCase() === 'send_rfq') {
                // small timeout to allow job list refresh to complete
                setTimeout(function () { openRfqManager(payload.job_id); }, 250);
            }
            // If we just raised a work order, highlight the job row and show confirmation
            if (String(actionCode).toLowerCase() === 'raise_work_order') {
                setTimeout(function () {
                    highlightJob(payload.job_id);
                    if (typeof showAlert === 'function') showAlert('Work order created and sent to the workshop.', 'success');
                }, 500);
            }
        },
        error: function () {
            alert('Server error while applying maintenance action');
        },
        complete: function () {
            _submittingAction = false;
            $('#submitMaintenanceAction, #rejectComplaintAction').prop('disabled', false);
        }
    });
}

function highlightJob(jobId) {
    if (!jobId) return;
    // find an element with data-id matching the job and highlight its row
    var $el = $(`[data-id="${jobId}"]`).first().closest('tr');
    if (!$el || !$el.length) return;
    $el.css('transition', 'background-color 0.5s ease');
    $el.css('background-color', '#fff3cd');
    setTimeout(function () { $el.css('background-color', ''); }, 2500);
}

function resetMaintenanceLineItems() {
    $('#maintenanceActionLineItems').empty();
    updateMaintenanceLineItemsTotal();
}

function addMaintenanceLineItemRow(item = {}) {
    const description = item.description || '';
    const cost = item.cost !== undefined && item.cost !== null ? item.cost : '';
    $('#maintenanceActionLineItems').append(`
        <tr>
            <td>
                <input type="text" class="form-control form-control-sm maintenance-line-item-description" value="${escapeHtml(description)}" placeholder="Describe work to be done">
            </td>
            <td>
                <input type="number" step="0.01" min="0" class="form-control form-control-sm maintenance-line-item-cost" value="${escapeHtml(String(cost))}" placeholder="0.00">
            </td>
            <td class="text-end">
                <button type="button" class="btn btn-sm btn-outline-danger remove-maintenance-line-item">x</button>
            </td>
        </tr>
    `);
    updateMaintenanceLineItemsTotal();
}

function collectMaintenanceLineItems() {
    const items = [];
    let total = 0;
    let error = '';

    $('#maintenanceActionLineItems tr').each(function () {
        const description = $(this).find('.maintenance-line-item-description').val().trim();
        const costRaw = $(this).find('.maintenance-line-item-cost').val();
        const cost = Number(costRaw);

        if (!description && !costRaw) {
            return;
        }
        if (!description) {
            error = 'Each line item must include work to be done';
            return false;
        }
        if (!Number.isFinite(cost) || cost <= 0) {
            error = 'Each line item must include a cost greater than 0';
            return false;
        }

        total += cost;
        items.push({ description, cost });
    });

    if (!error && !items.length) {
        error = 'Add at least one work line item before raising the work order';
    }

    return { items, total, error };
}

function updateMaintenanceLineItemsTotal() {
    let total = 0;
    $('#maintenanceActionLineItems .maintenance-line-item-cost').each(function () {
        const value = Number($(this).val());
        if (Number.isFinite(value) && value > 0) {
            total += value;
        }
    });
    $('#maintenanceLineItemsTotal').text(formatCurrency(total));
}

function openMaintenanceTimeline(jobId) {
    const canvasElement = document.getElementById('maintenanceTimelineCanvas');
    if (!canvasElement) {
        alert('Timeline panel not found.');
        return;
    }

    const offcanvas = new bootstrap.Offcanvas(canvasElement);
    offcanvas.show();
    $('#maintenanceTimelineBody').html('<div class="text-muted">Loading timeline...</div>');

    $.ajax({
        url: 'api/maintenance.php',
        method: 'GET',
        dataType: 'json',
        data: { action: 'timeline', job_id: jobId },
        success: function (res) {
            if (res.status !== 'success') {
                $('#maintenanceTimelineBody').html('<div class="text-danger">Unable to load timeline: ' + (res.message || 'Unknown error') + '</div>');
                return;
            }

            const rows = res.data || [];
            let html = '';

            if (!rows.length) {
                html = '<div class="text-muted">No timeline actions yet.</div>';
            } else {
                $.each(rows, function (_, row) {
                    html += `
                        <div class="border rounded-3 p-3 mb-3">
                            <div class="fw-semibold">${escapeHtml(row.action_label || '-')}</div>
                            <div class="small text-muted mb-2">${escapeHtml(formatDateTime(row.acted_at))} by ${escapeHtml(row.acted_by_name || 'System')}</div>
                            <div class="small">Stage: ${escapeHtml(row.stage_from || '-')} -> ${escapeHtml(row.stage_to || '-')}</div>
                            <div class="small">Amount: ${escapeHtml(formatCurrency(row.amount))}</div>
                            <div class="mt-2">${escapeHtml(row.comment || '-')}</div>
                        </div>
                    `;
                });
            }

            $('#maintenanceTimelineBody').html(html);
        },
        error: function (jqXHR, textStatus, errorThrown) {
            $('#maintenanceTimelineBody').html('<div class="text-danger">Unable to load timeline: ' + textStatus + ' ' + (errorThrown || '') + '</div>');
            console.error('Timeline load failed:', textStatus, errorThrown, jqXHR.responseText);
        }
    });
}

function openRfqManager(jobId) {
    $('#rfq_manager_job_id').val(jobId);
    $('#quote_rfq_id').html('<option value="">Select RFQ</option>');
    $('#quote_amount').val('');
    $('#quote_received_date').val('');
    $('#quote_notes').val('');
    $('#quote_file').val('');
    $('#quoteWorkshopSelectedLabel').text('Selected workshop: -');
    resetQuoteLineItemsTable('Select an RFQ vendor to load quote lines.');
    $('#finalWorkshopInfo').text('Final Workshop Selected: -');
    $('#rfqScopeSummary').text('0 items');
    $('#rfqScopeTable').html('<tr><td colspan="2" class="text-muted">Loading work scope...</td></tr>');
    $('#rfqRecommendationInfo').text('Waiting for comparable quotes.');
    updateRfqPolicyPanels({ vendors: 0, quotes: 0 }, null);
    rfqWorkshopMap = {};
    rfqWorkScopeLineItems = [];
    rfqRowsById = {};
    loadRfqManagerData(jobId);
    new bootstrap.Modal(document.getElementById('rfqManagerModal')).show();
}

function loadRfqManagerData(jobId) {

    $('#rfqManagerTable').html('<tr><td colspan="9" class="text-center text-muted">Loading RFQ records...</td></tr>');
    $.get('api/maintenance.php', { action: 'rfq_list', job_id: jobId }, function (res) {
        if (res.status !== 'success') {
            $('#rfqManagerTable').html('<tr><td colspan="9" class="text-center text-danger">Unable to load RFQ records</td></tr>');
            return;
        }

        const rows = (res.data && res.data.rows) ? res.data.rows : [];
        const stats = (res.data && res.data.stats) ? res.data.stats : { vendors: 0, quotes: 0 };
        const finalWorkshop = (res.data && res.data.final_workshop) ? res.data.final_workshop : '-';
        const lineItems = (res.data && res.data.line_items) ? res.data.line_items : [];
        rfqWorkScopeLineItems = Array.isArray(lineItems) ? lineItems : [];
        rfqRowsById = {};
        renderRfqScope(lineItems);
        var _policy = getRfqPolicy();
        $('#rfqPolicyInfo').text(`RFQ Vendors: ${stats.vendors || 0}/${_policy.min_vendors || 3} | Quotes: ${stats.quotes || 0}/${_policy.min_quotes_for_vetting || 2}`);
        $('#finalWorkshopInfo')
            .toggleClass('is-selected', finalWorkshop !== '-')
            .text(`Final Workshop Selected: ${finalWorkshop || '-'}`);
        updateRfqPolicyPanels(stats, finalWorkshop !== '-' ? finalWorkshop : null);

        if (!rows.length) {
            $('#rfqRecommendationInfo').text('Add at least ' + (_policy.min_vendors || 3) + ' workshop vendors before quote vetting.');
            $('#rfqManagerTable').html('<tr><td colspan="9" class="text-center text-muted">No RFQ vendors yet. Add workshop vendors above.</td></tr>');
            $('#quote_rfq_id').html('<option value="">Select RFQ</option>');
            return;
        }

        let rfqOptions = '<option value="">Select RFQ</option>';
        let html = '';
        rfqWorkshopMap = {};
        const comparableRows = rows
            .filter(function (row) {
                const amount = Number(row.quote_amount);
                return Number.isFinite(amount) && amount > 0;
            })
            .sort(function (a, b) {
                return Number(a.quote_amount) - Number(b.quote_amount);
            });
        const lowestQuoteId = comparableRows.length ? String(comparableRows[0].rfq_id) : '';
        const lowestAmount = comparableRows.length ? Number(comparableRows[0].quote_amount) : null;
        const selectedRow = rows.find(function (row) {
            return Number(row.is_final) === 1;
        });

        updateRfqRecommendation(rows, comparableRows, selectedRow);

        $.each(rows, function (_, row) {
            const label = `${row.workshop_name || 'Workshop'} (${row.rfq_number || 'No RFQ #'})`;
            rfqOptions += `<option value="${escapeHtml(String(row.rfq_id))}">${escapeHtml(label)}</option>`;
            rfqWorkshopMap[String(row.rfq_id)] = row.workshop_name || '-';
            rfqRowsById[String(row.rfq_id)] = row;
            const quoteAmount = Number(row.quote_amount);
            const hasQuote = Number.isFinite(quoteAmount) && quoteAmount > 0;
            const isLowest = hasQuote && String(row.rfq_id) === lowestQuoteId;
            const selected = Number(row.is_final) === 1;
            const variance = hasQuote && lowestAmount !== null ? quoteAmount - lowestAmount : null;
            const quoteBadge = hasQuote
                ? `<span class="fw-semibold">${escapeHtml(formatCurrency(quoteAmount))}</span>`
                : '<span class="text-muted">No quote</span>';
            const evidence = row.quote_file
                ? `<a href="${escapeHtml(resolveQuoteFileUrl(row.quote_file))}" target="_blank" rel="noopener" class="btn btn-sm btn-outline-secondary"><i class="bi bi-file-earmark-text"></i></a>`
                : '<span class="text-muted">-</span>';
            const recommendation = isLowest
                ? '<span class="badge bg-success-subtle text-success-emphasis border">Lowest quote</span>'
                : (hasQuote && variance > 0
                    ? `<span class="badge bg-light text-dark border">+${escapeHtml(formatCurrency(variance))}</span>`
                    : '<span class="badge bg-warning-subtle text-warning-emphasis border">Awaiting quote</span>');
            const decision = selected
                ? '<span class="badge bg-success">Final Workshop</span>'
                : (hasQuote
                    ? `<button type="button" class="btn btn-sm btn-outline-success btn-set-final-workshop" data-rfq-id="${row.rfq_id}" data-workshop="${escapeHtml(row.workshop_name || 'Workshop')}">Select Final</button>`
                    : '<button type="button" class="btn btn-sm btn-outline-secondary" disabled title="A submitted quote is required before final selection">Select Final</button>');
            // Add resend button when RFQ exists and was sent
            const resendBtn = `<button type="button" class="btn btn-sm btn-outline-primary btn-resend-invite" data-rfq-id="${row.rfq_id}">Resend</button>`;

            html += `
                <tr class="${selected ? 'rfq-selected-row' : ''}">
                    <td>${escapeHtml(row.workshop_name || '-')}</td>
                    <td>
                        <div>${escapeHtml(row.rfq_number || '-')}</div>
                        <div class="small text-muted">${escapeHtml(row.rfq_status || '-')}</div>
                    </td>
                    <td>${quoteBadge}</td>
                    <td>${escapeHtml(normalizeDateInput(row.quote_received_date) || '-')}</td>
                    <td>${evidence}</td>
                    <td class="rfq-notes-cell">${escapeHtml(row.quote_notes || '-')}</td>
                    <td>${renderQuoteStatusBadge(row.quote_status || row.rfq_status)}</td>
                    <td>${recommendation}</td>
                    <td>${decision} ${resendBtn}</td>
                </tr>
            `;
        });

        $('#quote_rfq_id').html(rfqOptions);
        updateQuoteWorkshopIndicator();
        $('#rfqManagerTable').html(html);
    }, 'json');
}

function renderRfqScope(lineItems) {
    const items = Array.isArray(lineItems) ? lineItems : [];

    const total = calculateLineItemsTotal(items);
    $('#rfqScopeSummary').text(`${items.length} item${items.length === 1 ? '' : 's'} | ${formatCurrency(total)}`);

    if (!items.length) {
        $('#rfqScopeTable').html('<tr><td colspan="2" class="text-muted">No line items found. Raise the work order with line items before RFQ.</td></tr>');
        return;
    }

    const html = items.map(function (item) {
        return `
            <tr>
                <td>${escapeHtml(item.description || '-')}</td>
                <td class="text-end">${escapeHtml(formatCurrency(item.cost || 0))}</td>
            </tr>
        `;
    }).join('');

    $('#rfqScopeTable').html(`
        ${html}
        <tr class="table-light fw-semibold">
            <td>Total Work Scope Estimate</td>
            <td class="text-end">${escapeHtml(formatCurrency(total))}</td>
        </tr>
    `);
}

function calculateLineItemsTotal(lineItems) {
    return (Array.isArray(lineItems) ? lineItems : []).reduce(function (total, item) {
        const value = Number(item && item.cost);
        return total + (Number.isFinite(value) && value > 0 ? value : 0);
    }, 0);
}

function populateQuoteLineItemsForSelectedRfq() {
    const rfqId = String($('#quote_rfq_id').val() || '');
    if (!rfqId) {
        resetQuoteLineItemsTable('Select an RFQ vendor to load quote lines.');
        return;
    }

    const row = rfqRowsById[rfqId] || {};
    const savedItems = Array.isArray(row.quote_line_items) ? row.quote_line_items : [];
    if (savedItems.length) {
        renderQuoteLineItems(savedItems.map(function (item) {
            return {
                source_line_item_id: item.source_line_item_id || '',
                description: item.description || '',
                cost: item.cost || item.quoted_cost || ''
            };
        }));
        return;
    }

    populateQuoteLineItemsFromWorkScope(false);
}

function populateQuoteLineItemsFromWorkScope(forceReset) {
    const rfqId = String($('#quote_rfq_id').val() || '');
    if (!rfqId) {
        resetQuoteLineItemsTable('Select an RFQ vendor before adding quote lines.');
        return;
    }

    const hasRows = $('#quoteLineItemsTable tr').not('.quote-line-items-empty-row').length > 0;
    if (hasRows && forceReset && !confirm('Replace current quote lines with the work-order scope?')) {
        return;
    }

    if (!rfqWorkScopeLineItems.length) {
        resetQuoteLineItemsTable('No work-order line items found for this RFQ.');
        return;
    }

    renderQuoteLineItems(rfqWorkScopeLineItems.map(function (item) {
        return {
            source_line_item_id: item.id || item.source_line_item_id || '',
            description: item.description || '',
            cost: ''
        };
    }));
}

function renderQuoteLineItems(items) {
    $('#quoteLineItemsTable').empty();
    const rows = Array.isArray(items) ? items : [];
    if (!rows.length) {
        resetQuoteLineItemsTable('No quote lines yet. Add a line or use the work-order scope.');
        return;
    }

    rows.forEach(function (item) {
        addQuoteLineItemRow(item);
    });
    updateQuoteLineItemsTotal();
}

function addQuoteLineItemRow(item = {}) {
    const sourceLineItemId = item.source_line_item_id || item.maintenance_line_item_id || '';
    const description = item.description || '';
    const cost = item.cost !== undefined && item.cost !== null ? item.cost : '';

    $('#quoteLineItemsTable .quote-line-items-empty-row').remove();
    $('#quoteLineItemsTable').append(`
        <tr>
            <td>
                <input type="hidden" class="quote-line-item-source-id" value="${escapeHtml(String(sourceLineItemId))}">
                <input type="text" class="form-control form-control-sm quote-line-item-description" value="${escapeHtml(description)}" placeholder="Work, part, or service">
            </td>
            <td style="width: 180px;">
                <input type="number" step="0.01" min="0" class="form-control form-control-sm text-end quote-line-item-cost" value="${escapeHtml(String(cost))}" placeholder="0.00">
            </td>
            <td class="text-end" style="width: 48px;">
                <button type="button" class="btn btn-sm btn-outline-danger remove-quote-line-item" title="Remove line">
                    <i class="bi bi-x-lg"></i>
                </button>
            </td>
        </tr>
    `);
}

function resetQuoteLineItemsTable(message) {
    $('#quoteLineItemsTable').html(`<tr class="quote-line-items-empty-row"><td colspan="3" class="text-muted">${escapeHtml(message || 'No quote lines yet.')}</td></tr>`);
    $('#quoteLineItemsTotal').text('0.00');
    $('#quote_amount').val('');
}

function updateQuoteLineItemsTotal() {
    let total = 0;
    $('#quoteLineItemsTable .quote-line-item-cost').each(function () {
        const value = Number($(this).val());
        if (Number.isFinite(value) && value > 0) {
            total += value;
        }
    });
    $('#quoteLineItemsTotal').text(formatCurrency(total));
    $('#quote_amount').val(total > 0 ? total.toFixed(2) : '');
}

function collectQuoteLineItems() {
    const items = [];
    let total = 0;
    let error = '';

    $('#quoteLineItemsTable tr').not('.quote-line-items-empty-row').each(function () {
        const sourceLineItemId = $(this).find('.quote-line-item-source-id').val();
        const description = $(this).find('.quote-line-item-description').val().trim();
        const costRaw = $(this).find('.quote-line-item-cost').val();
        const cost = Number(costRaw);

        if (!description && !costRaw) {
            return;
        }
        if (!description) {
            error = 'Each quote line must include work, part, or service description';
            return false;
        }
        if (!Number.isFinite(cost) || cost <= 0) {
            error = 'Each quote line must include a quoted amount greater than 0';
            return false;
        }

        total += cost;
        items.push({
            source_line_item_id: sourceLineItemId || '',
            description,
            cost: cost.toFixed(2)
        });
    });

    if (!error && !items.length) {
        error = 'Add at least one quote line before saving the workshop quote';
    }

    return { items, total, error };
}

function addRfqVendor() {
    const jobId = $('#rfq_manager_job_id').val();
    const workshopId = $('#rfq_workshop_select').val();
    if (!jobId || !workshopId) {
        alert('Select workshop to add RFQ vendor');
        return;
    }

    $.ajax({
        url: 'api/maintenance.php?action=add_rfq_vendor',
        method: 'POST',
        dataType: 'json',
        data: {
            job_id: jobId,
            workshop_id: workshopId
        },
        success: function (res) {
            if (res.status !== 'success') {
                alert(res.message || 'Unable to add RFQ vendor');
                return;
            }
            $('#rfq_workshop_select').val('');
            loadRfqManagerData(jobId);
            loadMaintenanceJobs();
        },
        error: function () {
            alert('Server error while adding RFQ vendor');
        }
    });
}

function selectFinalWorkshopForJob(rfqId) {
    const jobId = $('#rfq_manager_job_id').val();
    if (!jobId || !rfqId) {
        alert('Job and RFQ are required');
        return;
    }
    const workshopName = $(`.btn-set-final-workshop[data-rfq-id="${rfqId}"]`).data('workshop') || 'this workshop';
    if (!confirm(`Select ${workshopName} as the final workshop for this job?`)) {
        return;
    }

    $.ajax({
        url: 'api/maintenance.php?action=select_final_workshop',
        method: 'POST',
        dataType: 'json',
        data: {
            job_id: jobId,
            rfq_id: rfqId
        },
        success: function (res) {
            if (res.status !== 'success') {
                alert(res.message || 'Unable to set final workshop');
                return;
            }
            loadRfqManagerData(jobId);
            loadMaintenanceJobs();
        },
        error: function () {
            alert('Server error while setting final workshop');
        }
    });
}

function uploadQuoteForRfq() {
    const jobId = $('#rfq_manager_job_id').val();
    const rfqId = $('#quote_rfq_id').val();
    if (!jobId || !rfqId) {
        alert('Select RFQ vendor before uploading quote');
        return;
    }

    const quoteLineResult = collectQuoteLineItems();
    if (quoteLineResult.error) {
        alert(quoteLineResult.error);
        return;
    }

    const fd = new FormData();
    fd.append('job_id', jobId);
    fd.append('rfq_id', rfqId);
    fd.append('quote_amount', quoteLineResult.total.toFixed(2));
    fd.append('quote_line_items', JSON.stringify(quoteLineResult.items));
    fd.append('quote_received_date', $('#quote_received_date').val());
    fd.append('notes', $('#quote_notes').val().trim());

    const fileInput = $('#quote_file')[0];
    const file = fileInput && fileInput.files ? fileInput.files[0] : null;
    if (file) {
        fd.append('quote_file', file);
    }

    $.ajax({
        url: 'api/maintenance.php?action=upload_quote',
        method: 'POST',
        data: fd,
        processData: false,
        contentType: false,
        dataType: 'json',
        success: function (res) {
            if (res.status !== 'success') {
                alert(res.message || 'Unable to upload quote');
                return;
            }
            $('#quote_amount').val('');
            $('#quote_received_date').val('');
            $('#quote_notes').val('');
            $('#quote_file').val('');
            $('#quote_rfq_id').val('');
            updateQuoteWorkshopIndicator();
            loadRfqManagerData(jobId);
            loadMaintenanceJobs();
        },
        error: function () {
            alert('Server error while uploading quote');
        }
    });
}

function updateRfqPolicyPanels(stats, finalWorkshop) {
    const policy = getRfqPolicy();
    const minV = policy.min_vendors || 3;
    const minQ = policy.min_quotes_for_vetting || 2;
    const vendors = Number(stats && stats.vendors ? stats.vendors : 0);
    const quotes = Number(stats && stats.quotes ? stats.quotes : 0);
    const vendorReady = vendors >= minV;
    const quoteReady = quotes >= minQ;
    const approvalReady = vendorReady && quoteReady && !!finalWorkshop;

    setRfqPolicyPanel('#rfqPolicyVendorStatus', `${vendors}/${minV}`, vendorReady ? 'Ready' : 'Needs vendors', vendorReady);
    setRfqPolicyPanel('#rfqPolicyQuoteStatus', `${quotes}/${minQ}`, quoteReady ? 'Ready' : 'Needs quotes', quoteReady);
    setRfqPolicyPanel('#rfqApprovalReadiness', approvalReady ? 'Ready' : 'Pending', finalWorkshop ? `Final: ${finalWorkshop}` : 'No final workshop', approvalReady);
}

function setRfqPolicyPanel(selector, value, detail, ready) {
    const panel = $(selector);
    panel.toggleClass('is-ready', !!ready);
    panel.find('.rfq-policy-value').text(value);
    panel.find('.rfq-policy-detail').text(detail);
}

function updateRfqRecommendation(rows, comparableRows, selectedRow) {
    if (!comparableRows.length) {
        $('#rfqRecommendationInfo').text('Capture quote amounts before final selection.');
        return;
    }

    const lowest = comparableRows[0];
    if (selectedRow) {
        $('#rfqRecommendationInfo').text(`Selected: ${selectedRow.workshop_name || 'Workshop'} at ${formatCurrency(selectedRow.quote_amount)}.`);
        return;
    }

    if (comparableRows.length < 2) {
        $('#rfqRecommendationInfo').text(`Lowest current quote: ${lowest.workshop_name || 'Workshop'} at ${formatCurrency(lowest.quote_amount)}. Add one more comparable quote.`);
        return;
    }

    $('#rfqRecommendationInfo').text(`Lowest comparable quote: ${lowest.workshop_name || 'Workshop'} at ${formatCurrency(lowest.quote_amount)}.`);
}

function renderQuoteStatusBadge(status) {
    const value = String(status || 'pending');
    const key = value.toLowerCase();
    if (key === 'submitted' || key === 'quoted') {
        return '<span class="badge bg-primary-subtle text-primary border">Submitted</span>';
    }
    if (key === 'selected') {
        return '<span class="badge bg-success">Selected</span>';
    }
    if (key === 'rejected') {
        return '<span class="badge bg-secondary">Not selected</span>';
    }
    return `<span class="badge bg-light text-dark border">${escapeHtml(value)}</span>`;
}

function buildActionButtons(row) {
    const defs = maintenanceReferenceData.stage_actions || {};
    const stageKey = getMaintenanceStageKey(row);
    const hiddenStandaloneActions = ['submit_quote'];

    const actionsByStage = {
        'complaint logged': ['maintenance_review'],
        'maintenance review': ['raise_work_order'],
        'work order raised': ['start_workshop'],
        'rfq sent': [],
        'quote submitted': ['vet_quote'],
        'quote vetted': ['approve_job_order'],
        'job order approved': ['start_workshop', 'suspend_job', 'cancel_job'],
        'workshop in progress': ['complete_work', 'suspend_job'],
        'inspection': ['confirm_work', 'reject_work'],
        'internal control': ['close_job'],
        'closed': ['raise_requisition', 'return_to_workshop'],
        'requisition raised': ['approve_requisition', 'reject_requisition', 'mark_paid', 'mark_not_paid', 'return_to_workshop'],
        'requisition approved': ['mark_paid', 'mark_not_paid', 'return_to_workshop'],
        'requisition rejected': ['start_workshop'],
        'payment made': ['rate_job_order', 'return_to_workshop'],
        'suspended': ['start_workshop', 'cancel_job'],
        'cancelled': []
    };

    // Treat 'In Progress' as the stage where job-order actions should be visible.
    // This mapping ensures the UI doesn't hide raise/approve job order while the job is still actively progressing.
    if (String(row.workflow_stage || row.status || '').toLowerCase().includes('in progress')) {
        if (!Array.isArray(actionsByStage['maintenance review']) || actionsByStage['maintenance review'].indexOf('raise_work_order') === -1) {
            actionsByStage['maintenance review'] = ['raise_work_order'];
        }
        if (!Array.isArray(actionsByStage['quote vetted']) || actionsByStage['quote vetted'].indexOf('approve_job_order') === -1) {
            actionsByStage['quote vetted'] = ['approve_job_order'];
        }
    }

    if (!canCurrentUserActOnStage(stageKey)) {
        return '';
    }

    const list = (actionsByStage[stageKey] || []).filter(function (actionCode) {
        if (hiddenStandaloneActions.includes(actionCode)) {
            return false;
        }

        if ((actionCode === 'vet_quote' || actionCode === 'raise_job_order' || actionCode === 'approve_job_order') && !hasFinalWorkshopSelected(row)) {
            return false;
        }

        // Require at least one captured quote before allowing raise/approve job order actions
        if ((actionCode === 'raise_job_order' || actionCode === 'approve_job_order') && Number(row.quote_count || 0) === 0) {
            return false;
        }

        // Note: do not filter out 'start_workshop' here — render a disabled button with explanation instead

        // Filter requisition actions based on requisition_status
        const reqStatus = row.requisition_status || 'Pending';
        if (actionCode === 'raise_requisition' && reqStatus !== 'Pending') {
            return false;
        }
        if (actionCode === 'approve_requisition' && reqStatus !== 'Raised') {
            return false;
        }
        if (actionCode === 'reject_requisition' && reqStatus !== 'Raised') {
            return false;
        }
        if ((actionCode === 'mark_paid' || actionCode === 'mark_not_paid') && reqStatus !== 'Approved') {
            return false;
        }

        return true;
    });
    return list.map(function (actionCode) {
        let def = defs[actionCode];
        // Provide a client-side fallback for mark_not_paid when backend lacks the definition
        if (!def) {
            if (actionCode === 'mark_not_paid') {
                def = {
                    label: 'Mark Not Paid',
                    description: 'Record that payment was not made for this requisition',
                    requires_comment: true,
                    button_tone: 'danger'
                };
            } else {
                return '';
            }
        }

        // Conditions for enabling start_workshop (Send Job Order)
        if (actionCode === 'start_workshop') {
            const hasWorkOrder = !!(row.work_order_number || row.job_order_date);
            const stageKey = getMaintenanceStageKey(row);
            const isWorkOrderRaised = isRaisedWorkOrderStage(stageKey);
            const isJobOrderApproved = canonicalMaintenanceStageKey(stageKey) === 'job order approved';
            const hasRfq = Number(row.rfq_vendor_count || 0) > 0;
            const hasFinalWorkshop = hasFinalWorkshopSelected(row);
            const canSendToWorkshop = (isWorkOrderRaised && hasRfq && hasFinalWorkshop) || isJobOrderApproved;
            if (!canSendToWorkshop) {
                const tooltip = isWorkOrderRaised
                    ? 'Select final workshop and add line items first'
                    : 'Complete RFQ and approval process first';
                return `<button type="button" class="btn btn-sm ${getMaintenanceActionButtonClass(def)}" disabled title="${escapeHtml(tooltip)}">${escapeHtml(def.label)}</button>`;
            }
        }

        // Highlight 'Raise Work Order' when the job is in Maintenance Review
        if (actionCode === 'raise_work_order' && canonicalMaintenanceStageKey(stageKey) === 'maintenance review') {
            const baseClass = getMaintenanceActionButtonClass(def);
            return `<button type="button" class="btn btn-sm ${baseClass} btn-open-maintenance-action" style="box-shadow: 0 0 0 .25rem rgba(255,193,7,0.35);" data-id="${row.id}" data-action="${actionCode}" data-workshop="${escapeHtml(row.workshop || '')}">${escapeHtml(def.label)}</button>`;
        }

        return `<button type="button" class="btn btn-sm ${getMaintenanceActionButtonClass(def)} btn-open-maintenance-action" data-id="${row.id}" data-action="${actionCode}" data-workshop="${escapeHtml(row.workshop || '')}">${escapeHtml(def.label)}</button>`;
    }).join('');
}

function getMaintenanceActionButtonClass(def) {
    const tone = String(def && def.button_tone ? def.button_tone : 'primary').toLowerCase();
    const allowed = ['primary', 'secondary', 'success', 'danger', 'warning', 'info', 'dark'];
    return `btn-${allowed.includes(tone) ? tone : 'primary'}`;
}

function hasFinalWorkshopSelected(row) {
    return String(row.selected_final_workshop || '').trim() !== '';
}

function buildPdfDropdown(row) {
    const stageKey = getMaintenanceStageKey(row);
    const items = [];

    if (row.work_order_number || isAtOrAfterMaintenanceStage(stageKey, 'work order raised')) {
        items.push(`<li><a class="dropdown-item" href="#" onclick="previewWorkOrder(${row.id})">Preview Work Order</a></li>`);
        items.push(`<li><a class="dropdown-item" href="#" onclick="generateWorkOrderPDF(${row.id})">Work Order PDF</a></li>`);
    }
    if (row.job_order_date || isAtOrAfterMaintenanceStage(stageKey, 'job order approved')) {
        items.push(`<li><a class="dropdown-item" href="#" onclick="previewJobOrder(${row.id})">Preview Job Order</a></li>`);
        items.push(`<li><a class="dropdown-item" href="#" onclick="generateJobOrderPDF(${row.id})">Job Order PDF</a></li>`);
    }
    if (!items.length) {
        return '';
    }

    return `
        <div class="dropdown">
            <button class="btn btn-sm btn-outline-info dropdown-toggle" type="button" data-bs-toggle="dropdown">
                <i class="bi bi-file-earmark-pdf"></i>
            </button>
            <ul class="dropdown-menu">
                ${items.join('')}
            </ul>
        </div>
    `;
}

function buildRfqManagerButton(row) {
    const stageKey = getMaintenanceStageKey(row);
    // Hide RFQ manager before a work order has been raised — no workshop/RFQ context yet.
    if (canonicalMaintenanceStageKey(stageKey) === 'complaint logged') {
        return '';
    }
    if (canonicalMaintenanceStageKey(stageKey) === 'maintenance review') {
        return '';
    }
    // Show RFQ manager regardless of whether the work order has been raised.
    // Keep subsequent visibility rules (requisition raised, workshop in progress exceptions).

    const vendors = Number(row.rfq_vendor_count || 0);
    const quotes = Number(row.quote_count || 0);
    const requisitionStatus = String(row.requisition_status || '').toLowerCase();

    // Hide RFQ manager once a requisition / PO has been raised for the job.
    if (requisitionStatus === 'raised') {
        return '';
    }

    // If we've reached workshop in progress, allow RFQ manager only when vendors are sufficient but quotes are lacking.
    const policy = getRfqPolicy();
    const minV = policy.min_vendors || 3;
    const minQ = policy.min_quotes_for_vetting || 2;
    if (isAtOrAfterMaintenanceStage(stageKey, 'workshop in progress')) {
        if (!(vendors >= minV && quotes < minQ)) {
            return '';
        }
    }

    return `
        <button type="button" class="btn btn-sm btn-outline-dark btn-open-rfq-manager" title="Manage RFQ/Quotes" data-id="${row.id}">
            <i class="bi bi-list-check"></i>
        </button>
    `;
}

function isAtOrAfterMaintenanceStage(stageKey, targetStageKey) {
    stageKey = canonicalMaintenanceStageKey(stageKey);
    targetStageKey = canonicalMaintenanceStageKey(targetStageKey);

    const stages = [
        'complaint logged',
        'maintenance review',
        'work order raised',
        'rfq sent',
        'quote submitted',
        'quote vetted',
        'job order approved',
        'workshop in progress',
        'inspection',
        'internal control',
        'closed',
        'requisition raised',
        'payment made'
    ];
    const stageIndex = stages.indexOf(stageKey);
    const targetIndex = stages.indexOf(targetStageKey);

    return stageIndex !== -1 && targetIndex !== -1 && stageIndex >= targetIndex;
}

function canonicalMaintenanceStageKey(stageKey) {
    if (isRaisedWorkOrderStage(stageKey)) {
        return 'work order raised';
    }
    if (isApprovedComplaintStage(stageKey)) {
        return 'maintenance review';
    }

    return stageKey;
}

function getMaintenanceStageKey(row) {
    const stageKey = canonicalMaintenanceStageKey(normalizeStageKey(row.workflow_stage));
    if (stageKey) {
        return stageKey;
    }
    return canonicalMaintenanceStageKey(normalizeStageKey(row.status));
}

function canCurrentUserActOnStage(stageKey) {
    const role = String(window.CURRENT_USER_ROLE || '').trim();
    if (!role) {
        return true;
    }
    if (role.toLowerCase() === 'admin') {
        return true;
    }

    const matrixKey = findStageMatrixKey(stageKey);
    if (!matrixKey) {
        return true;
    }
    const allowedRoles = (getStageRoleMatrix())[matrixKey] || [];

    return allowedRoles.some(function (allowedRole) {
        return String(allowedRole).toLowerCase() === role.toLowerCase();
    });
}

function normalizeStageKey(stage) {
    return String(stage || '')
        .trim()
        .toLowerCase()
        .replace(/[^a-z0-9]+/g, ' ')
        .replace(/\s+/g, ' ')
        .trim();
}

function findStageMatrixKey(stageKey) {
    const keys = Object.keys(getStageRoleMatrix());
    for (let i = 0; i < keys.length; i++) {
        if (normalizeStageKey(keys[i]) === stageKey) {
            return keys[i];
        }
    }
    return null;
}

function renderMaintenanceStage(stage, status) {
    const value = String(stage || status || 'Pending');
    const normalized = value.toLowerCase();

    if (normalized.includes('cancel')) return '<span class="badge bg-dark">Cancelled</span>';
    if (normalized.includes('suspend')) return '<span class="badge bg-secondary">Suspended</span>';
    if (normalized.includes('workshop')) return '<span class="badge bg-warning text-dark">Workshop</span>';
    if (normalized.includes('rfq')) return '<span class="badge bg-warning-subtle text-warning-emphasis border">RFQ Stage</span>';
    if (normalized.includes('closed')) return '<span class="badge bg-success">Closed</span>';
    if (normalized.includes('not paid') || normalized.includes('payment not') || normalized.includes('not made')) return '<span class="badge bg-danger">Payment Not Made</span>';
    if (normalized.includes('requisition approved')) return '<span class="badge bg-success-subtle text-success-emphasis border">Requisition Approved</span>';
    if (normalized.includes('requisition rejected')) return '<span class="badge bg-danger-subtle text-danger-emphasis border">Requisition Rejected</span>';
    if (normalized.includes('requisition')) return '<span class="badge bg-info-subtle text-info-emphasis border">Requisition Raised</span>';
    if (normalized.includes('payment') || normalized === 'paid') return '<span class="badge bg-dark">Payment Made</span>';
    if (normalized.includes('inspection')) return '<span class="badge bg-info text-dark">Inspection</span>';
    if (normalized.includes('internal')) return '<span class="badge bg-primary">Internal Control</span>';
    if (normalized.includes('quote')) return '<span class="badge bg-primary-subtle text-primary">Quote Stage</span>';

    return `<span class="badge bg-light text-dark border">${escapeHtml(value)}</span>`;
}

function renderWorkOrderNumberCell(row) {
    return escapeHtml(row.work_order_number || '-');
}

function renderWorkflowStatusCell(row) {
    return `
        ${renderMaintenanceStage(row.workflow_stage, row.status)}
        <div class="mt-1">${renderRfqQuoteBadges(row)}</div>
    `;
}

function renderRfqQuoteBadges(row) {
    const policy = getRfqPolicy();
    const minV = policy.min_vendors || 3;
    const minQ = policy.min_quotes_for_vetting || 2;
    const vendors = Number(row.rfq_vendor_count || 0);
    const quotes = Number(row.quote_count || 0);
    const vendorClass = vendors >= minV ? 'bg-success-subtle text-success-emphasis border' : 'bg-danger-subtle text-danger-emphasis border';
    const quoteClass = quotes >= minQ ? 'bg-success-subtle text-success-emphasis border' : 'bg-warning-subtle text-warning-emphasis border';

    return `
        <span class="badge ${vendorClass} me-1">RFQ Vendors: ${escapeHtml(String(vendors))}/${minV}</span>
        <span class="badge ${quoteClass}">Quotes: ${escapeHtml(String(quotes))}/${minQ}</span>
    `;
}

function formatWorkshopDays(savedDays, liveDays) {
    if (savedDays) return savedDays + ' day(s)';
    if (liveDays && Number(liveDays) > 0) return liveDays + ' day(s)';
    return '-';
}

function populateSelect(selector, rows, placeholder) {
    const currentValue = $(selector).val();
    let html = `<option value="">${escapeHtml(placeholder)}</option>`;
    $.each(rows, function (_, row) {
        const dataEmail = row.email ? ` data-email="${escapeHtml(row.email)}"` : '';
        const dataContact = row.contact_person ? ` data-contact="${escapeHtml(row.contact_person)}"` : '';
        html += `<option value="${escapeHtml(row.id)}"${dataEmail}${dataContact}>${escapeHtml(row.label || row.name || '-')}</option>`;
    });
    $(selector).html(html);
    if (currentValue) {
        $(selector).val(String(currentValue));
    }
}

function showMaintenanceJobModal() {
    new bootstrap.Modal(document.getElementById('maintenanceJobModal')).show();
}

function hideMaintenanceJobModal() {
    const modalEl = document.getElementById('maintenanceJobModal');
    const instance = bootstrap.Modal.getInstance(modalEl);
    if (instance) instance.hide();
}

function showMaintenanceActionModal() {
    new bootstrap.Modal(document.getElementById('maintenanceActionModal')).show();
}

function hideMaintenanceActionModal() {
    const modalEl = document.getElementById('maintenanceActionModal');
    const instance = bootstrap.Modal.getInstance(modalEl);
    if (instance) instance.hide();
}

function resetMaintenanceJobForm() {
    const today = new Date().toISOString().slice(0, 10);
    $('#maintenanceJobForm')[0].reset();
    $('#maintenance_job_id').val('');
    $('#maintenance_complaint_source').val('Fleet Office');
    $('#maintenance_work_order_number').val('');
    $('#maintenance_complaint_date').val(today);
    $('#maintenance_complaint_log_date').val(today);
    $('#maintenance_workflow_stage').val('Complaint Logged');
    $('#maintenance_status').val('Pending');
}

function setMaintenanceJobFormMode(mode, record = {}) {
    const stage = normalizeStageKey(record.workflow_stage || $('#maintenance_workflow_stage').val());
    const status = normalizeStageKey(record.status || $('#maintenance_status').val());
    const isComplaintLogged = stage === 'complaint logged' || status === 'complaint logged';
    const isApprovedComplaint = isApprovedComplaintStage(stage);
    const isWorkOrderRaised = isRaisedWorkOrderStage(stage) || isRaisedWorkOrderStage(status);
    const hideWorkOrderControls = mode === 'create' || isComplaintLogged || isApprovedComplaint || isWorkOrderRaised;
    const hideComment = mode === 'create' || isComplaintLogged || isWorkOrderRaised;

    $('#maintenanceWorkOrderNumberGroup').toggleClass('d-none', hideWorkOrderControls);
    $('#maintenanceWorkflowStageGroup').toggleClass('d-none', hideWorkOrderControls);
    $('#maintenanceCommentGroup').toggleClass('d-none', hideComment);
    $('#maintenance_status').prop('disabled', mode === 'edit');
}

function shouldHideRaisedWorkOrderFields(row) {
    const stage = getMaintenanceStageKey(row);
    const status = normalizeStageKey(row.status);

    return isApprovedComplaintStage(stage) || isRaisedWorkOrderStage(stage) || isRaisedWorkOrderStage(status);
}

function isApprovedComplaintStage(stageKey) {
    return stageKey === 'maintenance review' || stageKey === 'approve complaint' || stageKey === 'approved complaint';
}

function isRaisedWorkOrderStage(stageKey) {
    return /\bwork\b/.test(stageKey) && /\border\b/.test(stageKey) && /\brais(?:e|ed|ing)\b/.test(stageKey);
}

function formatCurrency(value) {
    if (value === null || value === undefined || value === '') return '-';
    return Number(value).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
}

function formatDateTime(value) {
    if (!value) return '-';
    const date = new Date(value.replace(' ', 'T'));
    if (Number.isNaN(date.getTime())) return value;
    return date.toLocaleString();
}

function normalizeDateInput(value) {
    if (!value) return '';
    return String(value).slice(0, 10);
}

function safeJson(value) {
    return escapeHtml(JSON.stringify(value));
}

function escapeHtml(text) {
    return $('<div>').text(text || '').html();
}

function resolveQuoteFileUrl(quoteFile) {
    let val = String(quoteFile || '').trim();
    if (!val) return '#';
    try {
        val = decodeURIComponent(val);
    } catch (e) {
        // keep original value when decode fails
    }
    val = val.replace(/^uploads\//i, '');
    if (val.includes('/')) {
        return `uploads/${encodeURI(val)}`;
    }
    return `uploads/quotes/${encodeURIComponent(val)}`;
}

function updateMaintenanceActionWorkshopIndicator() {
    const workshopSelect = $('#maintenance_action_workshop');
    const text = workshopSelect.find('option:selected').text() || '-';
    const hasValue = !!workshopSelect.val();
    $('#maintenanceActionWorkshopSelectedLabel').text(`Selected workshop: ${hasValue ? text : '-'}`);
}

function updateQuoteWorkshopIndicator() {
    const rfqId = String($('#quote_rfq_id').val() || '');
    const workshopName = rfqWorkshopMap[rfqId] || '-';
    $('#quoteWorkshopSelectedLabel').text(`Selected workshop: ${workshopName}`);
    populateQuoteLineItemsForSelectedRfq();
}


