<?php
$machineQuery = "
    SELECT id, resource_code
    FROM station_detail_table
    WHERE machine_type = 1
      AND hide = 0
    ORDER BY resource_code
";
$machineResult = $conn->query($machineQuery);
$machineRows = [];
while ($m = $machineResult->fetch_assoc()) {
    $machineRows[] = $m;
}
?>

<style>
    /* ===== EXISTING STYLES (unchanged) ===== */
    .container {
        max-width: 100%;
        margin: 0 auto;
        background: white;
        border-radius: 10px;
        box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        overflow: hidden;
    }
    .header {
        background: #2c3e50;
        color: white;
        padding: 2px 30px;
    }
    .header h1 {
        font-size: 21px;
        margin-bottom: 2px;
        margin-top: 5px;
    }
    .header p {
        opacity: 0.9;
        font-size: 14px;
    }
    .filters {
        padding: 20px 30px;
        background: #f8f9fa;
        border-bottom: 1px solid #e9ecef;
        display: flex;
        gap: 15px;
        flex-wrap: wrap;
        align-items: center;
    }
    .filter-group {
        display: flex;
        flex-direction: column;
        gap: 5px;
    }
    .filter-group label {
        font-size: 12px;
        font-weight: 600;
        color: #495057;
        text-transform: uppercase;
    }
    .filter-group input,
    .filter-group select {
        padding: 8px 12px;
        border: 1px solid #ced4da;
        border-radius: 5px;
        font-size: 14px;
        min-width: 180px;
    }
    .filter-group input:focus,
    .filter-group select:focus {
        outline: none;
        border-color: #667eea;
        box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
    }
    .btn-reset {
        padding: 8px 20px;
        background: #6c757d;
        color: white;
        border: none;
        border-radius: 5px;
        cursor: pointer;
        font-size: 14px;
        margin-top: 20px;
        transition: background 0.3s;
    }
    .btn-reset:hover { background: #5a6268; }

    /* ===== Drag-Drop trigger button ===== */
    .btn-dnd {
        padding: 8px 20px;
        background: #2c3e50;
        color: white;
        border: none;
        border-radius: 5px;
        cursor: pointer;
        font-size: 14px;
        margin-top: 20px;
        transition: background 0.3s;
        display: inline-flex;
        align-items: center;
        gap: 7px;
    }
    .btn-dnd:hover { background: #1a252f; }

    .stats {
        padding: 15px 30px;
        background: #fff3cd;
        border-bottom: 1px solid #ffeaa7;
        display: flex;
        justify-content: space-between;
        align-items: center;
    }
    .stats-item { display: flex; align-items: center; gap: 8px; }
    .stats-item strong { color: #856404; }
    .table-wrapper {
        overflow-x: auto;
        padding: 20px 30px;
        min-height: 400px;
        position: relative;
    }
    .loading-overlay {
        position: absolute;
        top: 0; left: 0; right: 0; bottom: 0;
        background: rgba(255,255,255,0.9);
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        z-index: 100;
    }
    .loading-overlay.hidden { display: none; }
    .spinner {
        border: 4px solid #f3f3f3;
        border-top: 4px solid #667eea;
        border-radius: 50%;
        width: 50px; height: 50px;
        animation: spin 1s linear infinite;
    }
    @keyframes spin { 0%{transform:rotate(0deg)} 100%{transform:rotate(360deg)} }
    table { width: 100%; border-collapse: collapse; font-size: 14px; }
    thead { background: #f8f9fa; position: sticky; top: 0; z-index: 10; }
    th { padding: 12px 10px; text-align: left; font-weight: 600; color: #495057; border-bottom: 2px solid #dee2e6; white-space: nowrap; }
    td { padding: 12px 10px; border-bottom: 1px solid #e9ecef; color: #212529; }
    tbody tr { transition: background 0.2s; }
    tbody tr:hover { background: #f8f9fa; }
    .priority-badge { display: inline-block; padding: 4px 10px; border-radius: 12px; font-size: 12px; font-weight: 600; text-align: center; min-width: 60px; }
    .priority-1 { background: #dc3545; color: white; }
    .priority-2 { background: #fd7e14; color: white; }
    .priority-3 { background: #ffc107; color: #000; }
    .priority-4 { background: #28a745; color: white; }
    .priority-5 { background: #17a2b8; color: white; }
    .status-badge { display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 12px; font-weight: 600; text-align: center; }
    .status-finished  { background: #28a745; color: white; }
    .status-in-progress { background: #007bff; color: white; }
    .status-pending { background: #ffc107; color: #000; }
    .no-data { text-align: center; padding: 60px 20px; color: #6c757d; }
    .text-muted { color: #6c757d; font-size: 12px; }
    .text-center { text-align: center; }
    .text-right  { text-align: right; }
    .pagination-wrapper {
        padding: 20px 30px;
        display: flex;
        justify-content: space-between;
        align-items: center;
        border-top: 1px solid #e9ecef;
        background: #f8f9fa;
    }
    .pagination { display: flex; gap: 5px; align-items: center; }
    .pagination a, .pagination span {
        padding: 8px 12px;
        border: 1px solid #dee2e6;
        background: white;
        color: #495057;
        text-decoration: none;
        border-radius: 5px;
        font-size: 14px;
        transition: all 0.2s;
        cursor: pointer;
    }
    .pagination a:hover { background: #667eea; color: white; border-color: #667eea; }
    .pagination .active { background: #667eea; color: white; border-color: #667eea; font-weight: 600; }
    .pagination .disabled { opacity: 0.5; cursor: not-allowed; pointer-events: none; }
    .per-page-selector { display: flex; align-items: center; gap: 10px; }
    .per-page-selector select { padding: 8px 12px; border: 1px solid #ced4da; border-radius: 5px; font-size: 14px; }
    .hidden { display: none !important; }

    /* ===== substitution indicator ===== */
    .substituted-flag {
        color: #c0392b;
        font-weight: 700;
        margin-left: 2px;
        cursor: help;
    }

    /* ===== ⭐ UPDATED: Live UPH badge with GREY state ===== */
    .uph-badge {
        display: inline-flex;
        align-items: center;
        gap: 5px;
        font-size: 11px;
        font-weight: 600;
        padding: 2px 8px;
        border-radius: 12px;
        white-space: nowrap;
    }
    .uph-badge.green { background: #d4edda; color: #155724; }
    .uph-badge.red   { background: #f8d7da; color: #721c24; }
    .uph-badge.grey  { background: #e9ecef; color: #6c757d; }  /* ⭐ NEW: grey state */
    .uph-badge .dot  { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
    .uph-badge.green .dot { background: #28a745; }
    .uph-badge.red   .dot { background: #dc3545; }
    .uph-badge.grey  .dot { background: #adb5bd; }  /* ⭐ NEW: grey dot */

    /* ===== DRAG & DROP MODAL STYLES ===== */
    .dnd-overlay {
        position: fixed;
        inset: 0;
        background: rgba(0,0,0,0.5);
        z-index: 99999;
        display: flex;
        align-items: center;
        justify-content: center;
        opacity: 0;
        pointer-events: none;
        transition: opacity 0.2s;
    }
    .dnd-overlay.open {
        opacity: 1;
        pointer-events: all;
    }
    .dnd-modal {
        background: #fff;
        border-radius: 12px;
        width: min(1300px, 98vw);  
        height: min(850px, 95vh);   
        display: flex;
        flex-direction: column;
        box-shadow: 0 20px 60px rgba(0,0,0,0.25);
        transform: translateY(20px);
        transition: transform 0.22s;
        overflow: hidden;
    }
    .dnd-overlay.open .dnd-modal { transform: translateY(0); }

    .dnd-modal-header {
        background: #2c3e50;
        color: white;
        padding: 14px 22px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        flex-shrink: 0;
    }
    .dnd-modal-header h3 {
        margin: 0;
        font-size: 16px;
        font-weight: 600;
        display: flex;
        align-items: center;
        gap: 8px;
    }
    .dnd-close {
        background: rgba(255,255,255,0.15);
        border: none;
        color: white;
        width: 30px; height: 30px;
        border-radius: 50%;
        font-size: 16px;
        cursor: pointer;
        display: flex;
        align-items: center;
        justify-content: center;
        transition: background 0.2s;
    }
    .dnd-close:hover { background: rgba(255,255,255,0.3); }

    .dnd-machine-selectors {
        padding: 14px 20px;
        border-bottom: 1px solid #e9ecef;
        display: flex;
        gap: 16px;
        background: #f8f9fa;
        flex-shrink: 0;
    }
    .dnd-sel-group { flex: 1; display: flex; flex-direction: column; gap: 5px; }
    .dnd-sel-group label { font-size: 11px; font-weight: 700; color: #6c757d; text-transform: uppercase; letter-spacing: 0.05em; }
    .dnd-sel-group select {
        padding: 8px 12px;
        border: 1px solid #ced4da;
        border-radius: 6px;
        font-size: 14px;
        background: white;
        color: #212529;
        width: 100%;
        cursor: pointer;
    }
    .dnd-sel-group select:focus { outline: none; border-color: #667eea; box-shadow: 0 0 0 3px rgba(102,126,234,0.15); }

    .dnd-columns {
        display: flex;
        flex: 1;
        overflow: hidden;
    }
    .dnd-col {
        flex: 1;
        display: flex;
        flex-direction: column;
        min-width: 0;
    }
    .dnd-col:first-child { border-right: 1px solid #dee2e6; }

    .dnd-col-header {
        padding: 10px 16px;
        background: #f8f9fa;
        border-bottom: 1px solid #e9ecef;
        display: flex;
        align-items: center;
        justify-content: space-between;
        flex-shrink: 0;
    }
    .dnd-col-machine-name { font-weight: 700; font-size: 14px; color: #2c3e50; }
    .dnd-col-count {
        font-size: 11px;
        background: #dee2e6;
        color: #495057;
        padding: 2px 8px;
        border-radius: 10px;
        font-weight: 600;
    }

    .dnd-col-body {
        flex: 1;
        overflow-y: auto;
        padding: 10px;
        display: flex;
        flex-direction: column;
        gap: 7px;
        min-height: 100px;
        transition: background 0.15s;
    }
    .dnd-col-body.drag-over { background: #eef2ff; }

    /* Task cards */
    .task-card {
        background: white;
        border: 1px solid #e9ecef;
        border-left: 4px solid #667eea;
        border-radius: 7px;
        padding: 10px 12px;
        cursor: grab;
        user-select: none;
        transition: box-shadow 0.15s, opacity 0.15s, transform 0.1s;
        position: relative;
    }
    .task-card:hover { box-shadow: 0 2px 10px rgba(0,0,0,0.1); transform: translateY(-1px); }
    .task-card:active { cursor: grabbing; }
    .task-card.dragging { opacity: 0.3; box-shadow: none; transform: none; }

    .task-card.border-p1 { border-left-color: #dc3545; }
    .task-card.border-p2 { border-left-color: #fd7e14; }
    .task-card.border-p3 { border-left-color: #ffc107; }
    .task-card.border-p4 { border-left-color: #28a745; }
    .task-card.border-p5 { border-left-color: #17a2b8; }

    .task-card.drag-placeholder {
        border: 2px dashed #667eea;
        background: #f0f3ff;
        border-left: 2px dashed #667eea;
        height: 64px;
        border-radius: 7px;
        pointer-events: none;
    }
    .tc-wo   { font-size: 13px; font-weight: 700; color: #212529; margin-bottom: 3px; }
    .tc-prod { font-size: 12px; color: #6c757d; margin-bottom: 6px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .tc-meta { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
    .tc-badge {
        font-size: 10px;
        padding: 2px 8px;
        border-radius: 10px;
        font-weight: 700;
    }
    .tc-badge.p1 { background: #f8d7da; color: #721c24; }
    .tc-badge.p2 { background: #fde8d0; color: #7d3c01; }
    .tc-badge.p3 { background: #fff3cd; color: #856404; }
    .tc-badge.p4 { background: #d4edda; color: #155724; }
    .tc-badge.p5 { background: #d1ecf1; color: #0c5460; }
    .tc-badge.done     { background: #d4edda; color: #155724; }
    .tc-badge.progress { background: #cce5ff; color: #004085; }
    .tc-badge.pending  { background: #fff3cd; color: #856404; }
    .tc-hours { font-size: 10px; color: #adb5bd; font-weight: 600; }

    .dnd-empty   { text-align: center; padding: 50px 16px; color: #adb5bd; font-size: 13px; }
    .dnd-loading { text-align: center; padding: 50px 16px; font-size: 13px; color: #6c757d; }

    .dnd-footer {
        padding: 10px 20px;
        border-top: 1px solid #e9ecef;
        background: #f8f9fa;
        display: flex;
        align-items: center;
        justify-content: space-between;
        flex-shrink: 0;
    }
    .dnd-status { font-size: 12px; color: #6c757d; }
    .dnd-status.ok  { color: #155724; font-weight: 600; }
    .dnd-status.err { color: #721c24; font-weight: 600; }
    .dnd-status.saving { color: #004085; font-weight: 600; }

    .btn-dnd-refresh {
        padding: 6px 16px;
        font-size: 12px;
        background: white;
        border: 1px solid #ced4da;
        border-radius: 5px;
        cursor: pointer;
        color: #495057;
        transition: background 0.2s;
    }
    .btn-dnd-refresh:hover { background: #e9ecef; }

    /* Floating drag ghost */
    #dndGhost {
        position: fixed;
        pointer-events: none;
        z-index: 999999;
        background: white;
        border: 1px solid #dee2e6;
        border-left: 4px solid #667eea;
        border-radius: 7px;
        padding: 8px 12px;
        box-shadow: 0 8px 24px rgba(0,0,0,0.18);
        font-size: 12px;
        max-width: 240px;
        display: none;
        transform: rotate(2deg);
    }
    #dndGhost .g-wo   { font-weight: 700; color: #212529; }
    #dndGhost .g-prod { color: #6c757d; font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
</style>

<!-- ===== EXISTING MARKUP (unchanged) ===== -->
<div class="container">
    <div class="header">
        <h1>⚙️ VAC Scheduler - Work Order List</h1>
        <p>View and filter VAC machine work orders</p>
    </div>

    <div class="filters">
        <div class="filter-group">
            <label>Search Work Order</label>
            <input type="text" id="filterWorkOrder" placeholder="Enter work order number...">
        </div>
        <div class="filter-group">
            <label>Product Name</label>
            <input type="text" id="filterProduct" placeholder="Search product...">
        </div>
        <div class="filter-group">
            <label>Machine</label>
            <select id="filterMachine">
                <option value="">All Machines</option>
                <?php foreach ($machineRows as $machine): ?>
                    <option value="<?= $machine['id'] ?>"><?= htmlspecialchars($machine['resource_code']) ?></option>
                <?php endforeach; ?>
            </select>
        </div>
        <div class="filter-group hide">
            <label>Priority</label>
            <select id="filterPriority">
                <option value="">All Priorities</option>
                <option value="1">Priority 1 (Highest)</option>
                <option value="2">Priority 2</option>
                <option value="3">Priority 3</option>
                <option value="4">Priority 4</option>
                <option value="5">Priority 5 (Lowest)</option>
            </select>
        </div>
        <div class="filter-group">
            <label>Start Date From</label>
            <input type="date" id="filterStartDate" value="<?php echo date('Y-m-d'); ?>">
        </div>
        <button class="btn-reset" onclick="resetFilters()">🔄 Reset Filters</button>
        <!-- Drag & Drop button -->
        <button class="btn-dnd" onclick="openDnd()">
            <svg width="15" height="15" viewBox="0 0 16 16" fill="none">
                <rect x="1" y="2.5" width="6" height="11" rx="1.5" stroke="white" stroke-width="1.3"/>
                <rect x="9" y="2.5" width="6" height="11" rx="1.5" stroke="white" stroke-width="1.3"/>
                <path d="M7 8H9M9 8L7.5 6.5M9 8L7.5 9.5" stroke="white" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
            </svg>
            Drag &amp; Drop
        </button>
    </div>

    <div class="stats">
        <div class="stats-item">
            <strong>Total Work Orders:</strong>
            <span id="totalCount">0</span>
        </div>
        <div class="stats-item">
            <strong>Showing:</strong>
            <span id="showingRange">0 - 0</span>
        </div>
        <div class="stats-item">
            <strong>Page:</strong>
            <span id="pageInfo">0 of 0</span>
        </div>
    </div>

    <div class="table-wrapper">
        <div id="loadingOverlay" class="loading-overlay">
            <div class="spinner"></div>
            <p style="margin-top: 15px; color: #667eea; font-weight: 600;">Loading work orders...</p>
        </div>
        <div id="noDataMessage" class="no-data hidden">
            <h3>No Work Orders Found</h3>
            <p>There are no scheduled work orders matching your criteria.</p>
        </div>
        <table id="workOrderTable">
            <thead>
                <tr>
                    <th class="text-center hide">Priority</th>
                    <th>Work Order No.</th>
                    <th>Product Name</th>
                    <th>Machine</th>
                    <th class="text-center">Quantity</th>
                    <th class="text-center">Qty Made</th>
                    <th class="text-center">Live UPH</th>
                    <th>Component</th>
                    <th class="text-center">Status</th>
                    <th>Required Date</th>
                    <th>Start Date</th>
                    <th>End Date</th>
                    <th class="text-right">Total Hours</th>
                    <th class="text-right">Setup Hours</th>
                    <th>Customer Orders</th>
                    <th class="hide">Part Number</th>
                </tr>
            </thead>
            <tbody id="tableBody">
                <!-- Data loaded via AJAX -->
            </tbody>
        </table>
    </div>

    <div id="paginationWrapper" class="pagination-wrapper hidden">
        <div class="per-page-selector">
            <label><strong>Records per page:</strong></label>
            <select id="perPageSelect" onchange="changePerPage(this.value)">
                <option value="25">25</option>
                <option value="50" selected>50</option>
                <option value="100">100</option>
                <option value="200">200</option>
                <option value="500">500</option>
            </select>
        </div>
        <div id="paginationControls" class="pagination"></div>
    </div>
</div>

<!-- ===== DRAG & DROP MODAL ===== -->
<div class="dnd-overlay" id="dndOverlay">
    <div class="dnd-modal" role="dialog" aria-modal="true" aria-label="Drag and drop task scheduler">

        <div class="dnd-modal-header">
            <h3>
                <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
                    <rect x="1" y="2.5" width="6" height="11" rx="1.5" stroke="white" stroke-width="1.3"/>
                    <rect x="9" y="2.5" width="6" height="11" rx="1.5" stroke="white" stroke-width="1.3"/>
                    <path d="M7 8H9M9 8L7.5 6.5M9 8L7.5 9.5" stroke="white" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
                </svg>
                Drag &amp; Drop Task Scheduler
            </h3>
            <button class="dnd-close" onclick="closeDnd()" aria-label="Close">&times;</button>
        </div>

        <div class="dnd-machine-selectors">
            <div class="dnd-sel-group">
                <label>Left Machine</label>
                <select id="dndMachineLeft" onchange="dndLoadColumn('left')">
                    <option value="">— Select a machine —</option>
                    <?php foreach ($machineRows as $machine): ?>
                        <option value="<?= $machine['id'] ?>"><?= htmlspecialchars($machine['resource_code']) ?></option>
                    <?php endforeach; ?>
                </select>
            </div>
            <div class="dnd-sel-group">
                <label>Right Machine</label>
                <select id="dndMachineRight" onchange="dndLoadColumn('right')">
                    <option value="">— Select a machine —</option>
                    <?php foreach ($machineRows as $machine): ?>
                        <option value="<?= $machine['id'] ?>"><?= htmlspecialchars($machine['resource_code']) ?></option>
                    <?php endforeach; ?>
                </select>
            </div>
        </div>

        <div class="dnd-columns">
            <div class="dnd-col">
                <div class="dnd-col-header">
                    <span class="dnd-col-machine-name" id="dndColLeftName">Left Machine</span>
                    <span class="dnd-col-count" id="dndColLeftCount">0</span>
                </div>
                <div class="dnd-col-body" id="dndBodyLeft" data-side="left">
                    <div class="dnd-empty">Select a machine above</div>
                </div>
            </div>
            <div class="dnd-col">
                <div class="dnd-col-header">
                    <span class="dnd-col-machine-name" id="dndColRightName">Right Machine</span>
                    <span class="dnd-col-count" id="dndColRightCount">0</span>
                </div>
                <div class="dnd-col-body" id="dndBodyRight" data-side="right">
                    <div class="dnd-empty">Select a machine above</div>
                </div>
            </div>
        </div>

        <div class="dnd-footer">
            <span class="dnd-status" id="dndStatus">Select machines above to load tasks</span>
            <button class="btn-dnd-refresh" onclick="dndRefreshBoth()">&#8635; Refresh</button>
        </div>
    </div>
</div>

<!-- Floating drag ghost element -->
<div id="dndGhost">
    <div class="g-wo" id="dndGhostWo"></div>
    <div class="g-prod" id="dndGhostProd"></div>
</div>

<!-- ===== SCRIPTS ===== -->
<script>
/* =============================================
   EXISTING TABLE / FILTER / PAGINATION LOGIC
   (unchanged from original, plus auto-refresh)
============================================= */
let currentPage = 1;
let perPage = 50;
let totalPages = 0;

// How often to silently re-pull data so Live UPH stays fresh (ms)
const VAC_LIST_REFRESH_MS = 30000;

document.addEventListener('DOMContentLoaded', function() {
    loadData();

    // Keep the table's Live UPH / Qty Made figures current without
    // disturbing the user's current page, filters, or the D&D modal.
    setInterval(function () {
        loadData(false);
    }, VAC_LIST_REFRESH_MS);
});

document.getElementById('filterWorkOrder').addEventListener('input', debounce(loadData, 500));
document.getElementById('filterProduct').addEventListener('input', debounce(loadData, 500));
document.getElementById('filterMachine').addEventListener('change', loadData);
document.getElementById('filterPriority').addEventListener('change', loadData);
document.getElementById('filterStartDate').addEventListener('change', loadData);

function debounce(func, wait) {
    let timeout;
    return function(...args) {
        const later = () => { clearTimeout(timeout); func(...args); };
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
    };
}

function loadData(resetPage = true) {
    if (resetPage) currentPage = 1;
    const params = new URLSearchParams({
        current_page: currentPage,
        per_page: perPage,
        start_date: document.getElementById('filterStartDate').value,
        work_order: document.getElementById('filterWorkOrder').value,
        product: document.getElementById('filterProduct').value,
        machine: document.getElementById('filterMachine').value,
        priority: document.getElementById('filterPriority').value
    });
    document.getElementById('loadingOverlay').classList.remove('hidden');
    document.getElementById('noDataMessage').classList.add('hidden');
    document.getElementById('workOrderTable').style.opacity = '0.3';

    const url = site_url + "/modules/machines_management/index.php?page=ajax_vac_list_data&" + params.toString();
    fetch(url)
        .then(r => r.json())
        .then(data => {
            if (data.success) {
                renderTable(data.data);
                updatePagination(data.pagination);
                updateStats(data.pagination);
            } else {
                console.error('Error:', data.error);
                alert('Error loading data: ' + data.error);
            }
        })
        .catch(error => {
            console.error('Error:', error);
            const errorMsg = error.message || 'Please try again or refresh the page';
            alert('Error loading data: ' + errorMsg);
            document.getElementById('tableBody').innerHTML = `
                <tr><td colspan="14" class="text-center" style="padding:40px;color:#e74c3c;">
                    <div style="font-size:18px;font-weight:bold;">Error loading data</div>
                    <div style="font-size:14px;margin-top:10px;">${errorMsg}</div>
                </td></tr>`;
        })
        .finally(() => {
            document.getElementById('loadingOverlay').classList.add('hidden');
            document.getElementById('workOrderTable').style.opacity = '1';
        });
}

/* ── Shared helper to render a bought_in_materials array ── */
function formatMaterialsList(mats) {
    if (!Array.isArray(mats) || mats.length === 0) return null;
    return mats.map(function (m) {
        const isObj = m && typeof m === 'object';
        const name  = escapeHtml(isObj ? (m.name || '') : m);
        const flag  = isObj && m.substituted
            ? ' <span class="substituted-flag" title="Material substituted for this work order">↺</span>'
            : '';
        return name + flag;
    }).join(', ');
}

/* ── ⭐ UPDATED: Live UPH badge with GREY state ── */
function formatUphBadge(row) {
    const live    = parseFloat(row.live_uph);
    const planned = parseFloat(row.planned_uph);
    const hasLive    = !isNaN(live);
    const hasPlanned = !isNaN(planned) && planned > 0;
    
    // ⭐ Check if this task should show grey (finished OR no production started)
    const isGrey = row.is_uph_grey === true;

    if (isGrey) {
        // For finished or pending tasks, show grey with no comparison
        return `<span class="uph-badge grey" title="${row.finished == 1 ? 'Finished' : 'Not started'}">
                    <span class="dot"></span>${hasLive ? live : '—'}
                </span>`;
    }

    if (!hasLive) {
        return '<span class="uph-badge grey"><span class="dot"></span>—</span>';
    }

    if (!hasPlanned) {
        return `<span class="uph-badge grey" title="No planned UPH to compare against">
                    <span class="dot"></span>${live}
                </span>`;
    }

    const isGreen = !!row.is_uph_green;
    const cls     = isGreen ? 'green' : 'red';
    return `<span class="uph-badge ${cls}" title="Live UPH (last 60 min) vs planned ${planned}">
                <span class="dot"></span>${live} / ${planned}
            </span>`;
}

function renderTable(workOrders) {
    const tbody = document.getElementById('tableBody');
    const noDataMsg = document.getElementById('noDataMessage');
    if (workOrders.length === 0) {
        tbody.innerHTML = '';
        noDataMsg.classList.remove('hidden');
        return;
    }
    noDataMsg.classList.add('hidden');
    tbody.innerHTML = workOrders.map(row => {
        const startDate = new Date(row.start_date);
        const endDate = new Date(row.end_date);
        const startTime = row.start_time ? formatTime(row.start_time) : '';
        let statusText = '', statusClass = '';
        if (row.finished == 1) { statusText = 'Finished'; statusClass = 'status-finished'; }
        else if (row.quantity_made > 0) { statusText = 'In Progress'; statusClass = 'status-in-progress'; }
        else { statusText = 'Pending'; statusClass = 'status-pending'; }
        return `
            <tr>
                <td class="text-center hide"><span class="priority-badge priority-${row.priority}">P${row.priority}</span></td>
                <td><strong>${escapeHtml(row.work_order_name || row.work_order_no)}</strong></td>
                <td>
                    ${escapeHtml(row.product_name || row.work_order_name || '')}
                    ${row.setting_instruction ? '<br><span class="text-muted">' + escapeHtml(row.setting_instruction.substring(0,50)) + '</span>' : ''}
                </td>
                <td>${escapeHtml(row.machine_name || row.task)}</td>
                <td class="text-center"><strong>${formatNumber(row.quantity)}</strong></td>
                <td class="text-center"><strong>${formatNumber(row.quantity_made || 0)}</strong></td>
                <td class="text-center">${formatUphBadge(row)}</td>
                <td>${formatMaterialsList(row.bought_in_materials) || '-'}</td>
                <td class="text-center"><span class="status-badge ${statusClass}">${statusText}</span></td>
                <td>${row.require_date ? formatDate(new Date(row.require_date)) : '-'}</td>
                <td>${formatDate(startDate)}${startTime ? '<br><span class="text-muted">' + startTime + '</span>' : ''}</td>
                <td>${formatDate(endDate)}</td>
                <td class="text-right"><strong>${formatNumber(row.total_hours, 2)}</strong> hrs</td>
                <td class="text-right">${formatNumber(row.setup_hours, 2)} hrs</td>
                <td>${escapeHtml(row.customer_order_nos || '-')}</td>
                <td class="hide">${escapeHtml(row.parent_part_number || '-')}</td>
            </tr>`;
    }).join('');
}

function updatePagination(pagination) {
    totalPages = pagination.total_pages;
    const wrapper = document.getElementById('paginationWrapper');
    const controls = document.getElementById('paginationControls');
    if (totalPages <= 1) { wrapper.classList.add('hidden'); return; }
    wrapper.classList.remove('hidden');
    let html = '';
    if (currentPage > 1) {
        html += `<a onclick="goToPage(1)">« First</a>`;
        html += `<a onclick="goToPage(${currentPage - 1})">‹ Prev</a>`;
    } else {
        html += `<span class="disabled">« First</span><span class="disabled">‹ Prev</span>`;
    }
    const startPage = Math.max(1, currentPage - 2);
    const endPage = Math.min(totalPages, currentPage + 2);
    if (startPage > 1) html += '<span>...</span>';
    for (let i = startPage; i <= endPage; i++) {
        html += i === currentPage ? `<span class="active">${i}</span>` : `<a onclick="goToPage(${i})">${i}</a>`;
    }
    if (endPage < totalPages) html += '<span>...</span>';
    if (currentPage < totalPages) {
        html += `<a onclick="goToPage(${currentPage + 1})">Next ›</a>`;
        html += `<a onclick="goToPage(${totalPages})">Last »</a>`;
    } else {
        html += `<span class="disabled">Next ›</span><span class="disabled">Last »</span>`;
    }
    controls.innerHTML = html;
}

function updateStats(pagination) {
    document.getElementById('totalCount').textContent = formatNumber(pagination.total_records);
    document.getElementById('showingRange').textContent = `${formatNumber(pagination.showing_from)} - ${formatNumber(pagination.showing_to)}`;
    document.getElementById('pageInfo').textContent = `${pagination.current_page} of ${pagination.total_pages}`;
}

function goToPage(page) { currentPage = page; loadData(false); }
function changePerPage(newPerPage) { perPage = parseInt(newPerPage); currentPage = 1; loadData(false); }
function resetFilters() {
    document.getElementById('filterWorkOrder').value = '';
    document.getElementById('filterProduct').value = '';
    document.getElementById('filterMachine').value = '';
    document.getElementById('filterPriority').value = '';
    document.getElementById('filterStartDate').value = '<?php echo date('Y-m-d'); ?>';
    loadData();
}
function escapeHtml(text) {
    if (!text) return '';
    const div = document.createElement('div');
    div.textContent = text;
    return div.innerHTML;
}
function formatNumber(num, decimals = 0) {
    if (!num) return '0';
    return parseFloat(num).toLocaleString('en-IN', { minimumFractionDigits: decimals, maximumFractionDigits: decimals });
}
function formatDate(date) {
    return date.toLocaleDateString('en-GB', { day: '2-digit', month: 'short', year: 'numeric' });
}
function formatTime(timeStr) {
    const [hours, minutes] = timeStr.split(':');
    const hour = parseInt(hours);
    const ampm = hour >= 12 ? 'PM' : 'AM';
    const displayHour = hour % 12 || 12;
    return `${displayHour}:${minutes} ${ampm}`;
}


/* =============================================
   DRAG & DROP SCHEDULER — NEW FUNCTIONALITY
============================================= */
const DND_DATA_URL  = site_url + '/modules/machines_management/index.php?page=ajax_vac_list_data';
const DND_SAVE_URL = site_url + '/modules/machines_management/index.php?page=ajax_vac_list_data';

let dndTasks       = { left: [], right: [] };
let dndMachineIds  = { left: 0, right: 0 };

// Keep both open D&D columns' Live UPH figures fresh while the modal is open
let dndRefreshTimer = null;

/* ---- Drag state ---- */
let dndDragging    = null;   // the card element being dragged
let dndDragSide    = null;   // 'left' | 'right'
let dndPlaceholder = null;   // placeholder div shown in target column
const dndGhost     = document.getElementById('dndGhost');

/* ---- Open / Close ---- */
function openDnd() {
    document.getElementById('dndOverlay').classList.add('open');

    // Start polling only while the modal is actually open, so we don't
    // hit the server needlessly when no one is looking at the board.
    if (!dndRefreshTimer) {
        dndRefreshTimer = setInterval(dndRefreshBoth, VAC_LIST_REFRESH_MS);
    }
}
function closeDnd() {
    document.getElementById('dndOverlay').classList.remove('open');

    if (dndRefreshTimer) {
        clearInterval(dndRefreshTimer);
        dndRefreshTimer = null;
    }
}
// Click backdrop to close
document.getElementById('dndOverlay').addEventListener('click', function(e) {
    if (e.target === this) closeDnd();
});

/* ---- Load tasks for one column ---- */
async function dndLoadColumn(side) {
    const selEl  = document.getElementById(side === 'left' ? 'dndMachineLeft' : 'dndMachineRight');
    const mid    = selEl.value;
    const mname  = selEl.options[selEl.selectedIndex]?.text || '';
    dndMachineIds[side] = parseInt(mid) || 0;

    const nameEl  = document.getElementById(side === 'left' ? 'dndColLeftName'  : 'dndColRightName');
    const bodyEl  = document.getElementById(side === 'left' ? 'dndBodyLeft'     : 'dndBodyRight');
    nameEl.textContent = mname || (side + ' machine');

    if (!mid) { dndRenderColumn(side, []); return; }

    bodyEl.innerHTML = '<div class="dnd-loading">⏳ Loading tasks…</div>';

    try {
        const url = DND_DATA_URL + `&machine=${mid}&per_page=500&current_page=1`;
        const res = await fetch(url);
        const dat = await res.json();
        dndTasks[side] = dat.success ? dat.data : [];
        dndRenderColumn(side, dndTasks[side]);
    } catch(err) {
        bodyEl.innerHTML = '<div class="dnd-empty">Failed to load tasks. Check connection.</div>';
    }
}

/* ---- Render cards inside a column ---- */
function dndRenderColumn(side, rows) {
    const bodyEl  = document.getElementById(side === 'left' ? 'dndBodyLeft'     : 'dndBodyRight');
    const countEl = document.getElementById(side === 'left' ? 'dndColLeftCount' : 'dndColRightCount');
    countEl.textContent = rows.length;

    if (!rows.length) {
        bodyEl.innerHTML = '<div class="dnd-empty">No tasks found for this machine</div>';
        dndSetupDropZone(bodyEl, side);
        return;
    }

    bodyEl.innerHTML = '';
    rows.forEach((row, idx) => bodyEl.appendChild(dndMakeCard(row, side, idx)));
    dndSetupDropZone(bodyEl, side);
}

/* ---- Build a single task card (includes grey UPH badge) ---- */
function dndMakeCard(row, side, idx) {
    const card = document.createElement('div');
    const pNum = Math.min(Math.max(parseInt(row.priority) || 1, 1), 5);
    card.className = `task-card border-p${pNum}`;
    card.draggable = true;

    // Store all data we need for the save call
    card.dataset.taskId      = row.id || row.task_id || '';
    card.dataset.workOrderId = row.work_order_id || '';
    card.dataset.partId      = row.part_id || '';
    card.dataset.priority    = row.priority || 0;
    card.dataset.setting     = row.setting_instruction || '';
    card.dataset.side        = side;

    const materialsHtml = formatMaterialsList(row.bought_in_materials);
    const uphBadgeHtml   = formatUphBadge(row);

    card.innerHTML = `
    <div style="display:flex; align-items:flex-start; gap:10px;">
        <div style="flex:1; min-width:0;">
            <div class="tc-wo" style="font-size:4px; font-weight:700; color:#212529; margin-bottom:1px;">
                ${escapeHtml(row.work_order_name || row.work_order_no || '—')}
            </div>
            <div class="tc-prod" style="font-size:12px; font-weight:700; color:#212529; margin-bottom:1px;" title="${escapeHtml(row.product_name || row.work_order_name || '')}">
                ${escapeHtml(row.product_name || row.work_order_name || '—')}
            </div>
            <div style="font-size:11px; color:#6c757d; margin-bottom:1px;">
                Required: ${row.require_date ? formatDate(new Date(row.require_date)) : '—'}
            </div>
            <div style="font-size:11px; color:#495057; font-weight:600;">
                Qty: ${formatNumber(row.quantity || 0)} &nbsp;·&nbsp; Made: ${formatNumber(row.quantity_made || 0)}
            </div>
            <div style="margin-top:3px;">${uphBadgeHtml}</div>
            <div class="tc-hours" style="font-size:10px; color:#adb5bd; font-weight:600; margin-top:3px;">
                ${parseFloat(row.total_hours || 0).toFixed(1)} hrs
            </div>
        </div>

        <div style="flex:0 0 260px; text-align:center;">
            <div style="font-size:10px; color:#6c757d;">Setting Instruction:</div>
            <div style="font-size:13px; font-weight:800; color:#212529; white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">
                ${escapeHtml(row.setting_instruction || '—')}
            </div>
        </div>

        <div style="flex:0 0 180px; text-align:right;">
            <div>
                <div style="font-size:10px; color:#6c757d; margin-bottom:2px;">Component:</div>
                <div style="font-size:13px; font-weight:800; color:#212529;">
                    ${materialsHtml || '—'}
                </div>
                ${(() => {
                    if (!materialsHtml) return '';
                    return '<div style="font-size:10px; color:#495057; margin-top:2px;">' +
                        materialsHtml +
                    '</div>';
                })()}
                ${(() => {
                    const dots = Array.isArray(row.inventory_dots) ? row.inventory_dots : [];
                    if (dots.length === 0) return '';
                    return '<div style="display:flex; gap:4px; flex-wrap:wrap; justify-content:flex-end; margin-top:4px;">' +
                        dots.map(d => {
                            const title = escapeHtml(d.part_number || '') + ' — ' + (d.in_stock ? 'In Stock' : 'No Stock') + (d.substituted ? ' (substituted)' : '');
                            const ring  = d.substituted ? 'box-shadow:0 0 0 2px #c0392b;' : '';
                            return `<span title="${title}"
                                style="display:inline-block; width:10px; height:10px; border-radius:50%;
                                       background:${d.in_stock ? '#28a745' : '#dc3545'};
                                       ${ring}
                                       flex-shrink:0;"></span>`;
                        }).join('') +
                    '</div>';
                })()}
            </div>
        </div>
    </div>`;

    card.addEventListener('dragstart', dndOnDragStart);
    card.addEventListener('dragend',   dndOnDragEnd);
    return card;
}

/* ---- Setup drop zone on a column body ---- */
function dndSetupDropZone(bodyEl, side) {
    // Remove old listeners by cloning (clean approach)
    const fresh = bodyEl.cloneNode(true);
    bodyEl.parentNode.replaceChild(fresh, bodyEl);

    // Re-attach drag listeners to cloned cards
    fresh.querySelectorAll('.task-card').forEach(c => {
        c.addEventListener('dragstart', dndOnDragStart);
        c.addEventListener('dragend',   dndOnDragEnd);
    });

    fresh.addEventListener('dragover', function(e) {
        e.preventDefault();
        fresh.classList.add('drag-over');
        if (dndPlaceholder) {
            const after = dndGetAfterElement(fresh, e.clientY);
            if (after) fresh.insertBefore(dndPlaceholder, after);
            else        fresh.appendChild(dndPlaceholder);
        }
    });
    fresh.addEventListener('dragleave', function(e) {
        if (!fresh.contains(e.relatedTarget)) fresh.classList.remove('drag-over');
    });
    fresh.addEventListener('drop', function(e) {
        e.preventDefault();
        fresh.classList.remove('drag-over');
        if (!dndDragging) return;

        const destSide      = side;
        const destMachineId = dndMachineIds[destSide];
        if (!destMachineId) { dndSetStatus('Please select a machine for this column first.', 'err'); return; }

        // Find left neighbour (card before placeholder / drop position)
        const after = dndGetAfterElement(fresh, e.clientY);
        const allCards = [...fresh.querySelectorAll('.task-card:not(.dragging):not(.drag-placeholder)')];
        let leftNeighborCard = null;
        if (after) {
            const afterIdx = allCards.indexOf(after);
            leftNeighborCard = afterIdx > 0 ? allCards[afterIdx - 1] : null;
        } else {
            leftNeighborCard = allCards.length ? allCards[allCards.length - 1] : null;
        }

        dndSaveMove(dndDragging, destMachineId, leftNeighborCard);
    });
}

/* ---- Drag events ---- */
function dndOnDragStart(e) {
    dndDragging = e.currentTarget;
    dndDragSide = dndDragging.dataset.side;

    setTimeout(() => { if (dndDragging) dndDragging.classList.add('dragging'); }, 0);

    // Create placeholder
    dndPlaceholder = document.createElement('div');
    dndPlaceholder.className = 'task-card drag-placeholder';

    // Populate ghost
    dndGhost.style.display = 'block';
    document.getElementById('dndGhostWo').textContent   = dndDragging.querySelector('.tc-wo')?.textContent || '';
    document.getElementById('dndGhostProd').textContent = dndDragging.querySelector('.tc-prod')?.textContent || '';
}

function dndOnDragEnd() {
    if (dndDragging) dndDragging.classList.remove('dragging');
    if (dndPlaceholder && dndPlaceholder.parentNode) dndPlaceholder.parentNode.removeChild(dndPlaceholder);
    document.querySelectorAll('.dnd-col-body').forEach(b => b.classList.remove('drag-over'));
    dndGhost.style.display = 'none';
    dndDragging = null;
    dndPlaceholder = null;
}

// Move ghost with cursor
document.addEventListener('dragover', function(e) {
    dndGhost.style.left = (e.clientX + 16) + 'px';
    dndGhost.style.top  = (e.clientY + 10) + 'px';
});

/* ---- Find the card element after cursor Y ---- */
function dndGetAfterElement(container, y) {
    const draggableEls = [...container.querySelectorAll('.task-card:not(.dragging):not(.drag-placeholder)')];
    return draggableEls.reduce((closest, child) => {
        const box    = child.getBoundingClientRect();
        const offset = y - box.top - box.height / 2;
        if (offset < 0 && offset > closest.offset) return { offset, element: child };
        return closest;
    }, { offset: Number.NEGATIVE_INFINITY }).element;
}

/* ---- Send move to server ---- */
async function dndSaveMove(card, destMachineId, leftNeighborCard) {
    const taskId      = card.dataset.taskId;
    const workOrderId = card.dataset.workOrderId;
    const partId      = card.dataset.partId;
    const leftPriority = leftNeighborCard ? leftNeighborCard.dataset.priority : null;
    const leftTaskId   = leftNeighborCard ? leftNeighborCard.dataset.taskId   : null;

    dndSetStatus('Saving…', 'saving');

    try {
        const params = new URLSearchParams({
            action:       'update_vac_task_priority',
            task_id:      taskId,
            new_machine_id: destMachineId,
            part_id:      partId,
            work_order_id: workOrderId,
        });
        if (leftPriority !== null) params.append('left_neighbor_priority', leftPriority);
        if (leftTaskId   !== null) params.append('left_neighbor_task_id',   leftTaskId);

        const res = await fetch(DND_SAVE_URL, { method: 'POST', body: params });
        const dat = await res.json();

        if (dat.status === 'success') {
            dndSetStatus('Task moved successfully!', 'ok');
            setTimeout(dndRefreshBoth, 500);
            // Also refresh the main table so changes are visible
            loadData(false);
        } else {
            dndSetStatus('Error: ' + (dat.message || 'Unknown error'), 'err');
            dndRefreshBoth();
        }
    } catch(err) {
        dndSetStatus('Network error — refreshing…', 'err');
        dndRefreshBoth();
    }
}

/* ---- Refresh both columns ---- */
function dndRefreshBoth() {
    if (dndMachineIds.left)  dndLoadColumn('left');
    if (dndMachineIds.right) dndLoadColumn('right');
}

/* ---- Status message ---- */
function dndSetStatus(msg, type) {
    const el = document.getElementById('dndStatus');
    el.textContent  = msg;
    el.className    = 'dnd-status ' + (type || '');
}
</script>