<?php
session_start();
$where                  = " WHERE 1=1 ";
$where                 .= " AND part_id != 0 "; 
$next_page_condition    = "";
$paging                 = "";

if ($_REQUEST['start']) {
    $paging .= "&start=" . $_REQUEST['start'];
}

$from_source        = "planner";
$restricted_groups  = [13];
$can_view           = !in_array($_SESSION['admin_group_id'], $restricted_groups);

############################################ START FILTER CONDITIONS ############################################

$customer = isset($_REQUEST['customer']) ? $_REQUEST['customer'] : "";
if ($customer) {
    $where                .= " AND customer_reff = '$customer' ";
    $next_page_condition  .= "&customer_reff=$customer";
}

$item_name = isset($_REQUEST['item_name']) ? $_REQUEST['item_name'] : "";
if ($item_name) {
    $item_name  = trim($item_name);
    $where     .= " AND part_id IN (
        SELECT im.id 
        FROM item_management im
        WHERE im.part_number LIKE '%$item_name%'
    )";
    $next_page_condition .= "&item_name=" . urlencode($item_name);
}

############################################ END FILTER CONDITIONS ############################################
############################################ PAGING WITH PER PAGE ############################################

$file_name              = "index";
$table_name             = "production_planning";
$paging_table_name      = $table_name;
$paging_query           = $where;
$join_query_for_paging  = "SELECT * FROM $table_name $where ";
$page_header            = "Production Planning Management";
include("paging_for_producrion/paging_query.inc.php");

############################################ END PAGING WITH PER PAGE ############################################

// Fetch date range
$date_range_query  = "SELECT MIN(req_date) AS min_date, MAX(req_date) AS max_date FROM production_planning";
$date_range_result = mysqli_query($conn, $date_range_query);
$date_range        = mysqli_fetch_assoc($date_range_result);
$min_date          = date('Y-m-d');
$min_date          = date('Y-m-d', strtotime('monday this week', strtotime($min_date)));

$weeks_to_show = 9;
$max_date      = date('Y-m-d', strtotime($min_date . " +$weeks_to_show weeks"));

$weeks   = [];
$current = strtotime($min_date);
$end     = strtotime($max_date);

while ($current <= $end) {
    $weeks[] = [
        'yearweek' => date('oW', $current),
        'label'    => date('d-M', $current)
    ];
    $current = strtotime('+1 week', $current);
}

$query = "
    SELECT 
        part_id, status,
        YEARWEEK(req_date, 3) AS week_number,  
        SUM(req_qty) AS total_req_qty
    FROM production_planning 
    $where
    GROUP BY part_id, YEARWEEK(req_date, 3) 
    ORDER BY part_id, week_number ASC 
    LIMIT $eu, $limit
";

$sql                  = mysqli_query($conn, $query);
$result               = mysqli_num_rows($sql);
$total_no_of_records  = mysqli_num_rows(mysqli_query($conn, $query));

// Group numbers
$group_numbers = [];
$group_sql     = "
    SELECT 
        gmm.part_id,
        GROUP_CONCAT(gm.group_number ORDER BY gm.group_number SEPARATOR ', ') AS groups
    FROM group_mangmnt_mapping AS gmm
    LEFT JOIN group_mangmnt AS gm ON gm.id = gmm.group_id
    GROUP BY gmm.part_id
";
$group_res = mysqli_query($conn, $group_sql);
while ($g = mysqli_fetch_assoc($group_res)) {
    $group_numbers[$g['part_id']] = $g['groups'];
}

// Group parts
$group_parts = [];
$gp_sql      = "
    SELECT gm.group_number, im.id AS part_id, im.part_number
    FROM group_mangmnt_mapping gmm
    LEFT JOIN group_mangmnt gm ON gm.id = gmm.group_id
    LEFT JOIN item_management im ON im.id = gmm.part_id
    ORDER BY gm.group_number, im.part_number
";
$gp_res = mysqli_query($conn, $gp_sql);
while ($gp = mysqli_fetch_assoc($gp_res)) {
    $group_parts[$gp['group_number']][] = [
        'part_id'     => $gp['part_id'],
        'part_number' => $gp['part_number']
    ];
}

// Set data
$set_parts = [];
$set_sql   = "
    SELECT smm.part_id,
           GROUP_CONCAT(sm.set_number ORDER BY sm.set_number SEPARATOR ', ') AS sets
    FROM set_mangmnt_mapping smm
    LEFT JOIN set_mangmnt sm ON sm.id = smm.set_id
    GROUP BY smm.part_id
";
$set_res = mysqli_query($conn, $set_sql);
while ($s = mysqli_fetch_assoc($set_res)) {
    $set_parts[$s['part_id']] = $s['sets'];
}

################################# DELETE #########################################

if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'delete') {
    $sql = mysql_query("DELETE FROM $table_name WHERE id = '" . $_REQUEST['id'] . "' ");
    $_SESSION['info_message'] = "Item deleted successfully";
    header("location: $file_name?page=view_country" . $next_page_condition . $paging);
    exit;
}

################################# END DELETE #########################################
?>
<?php
if (!function_exists('getAllBoughtInMaterials')) {
    function getAllBoughtInMaterials($conn, $part_id, &$visited = []) {
        if (in_array($part_id, $visited)) {
            return [];
        }
        $visited[]  = $part_id;
        $materials  = [];
        $query      = "SELECT sub_part_id, sub_part_name, type FROM bom WHERE main_product_id = '$part_id'";
        $result     = mysqli_query($conn, $query);

        if ($result) {
            while ($row = mysqli_fetch_assoc($result)) {
                if ($row['type'] == 'Bought In' && !empty($row['sub_part_name'])) {
                    $inv_query  = "SELECT quantity_in_stock FROM inventory WHERE item_id = '" . mysqli_real_escape_string($conn, $row['sub_part_id']) . "'";
                    $inv_result = mysqli_query($conn, $inv_query);
                    $quantity   = 0;
                    if ($inv_result && mysqli_num_rows($inv_result) > 0) {
                        $inv_row  = mysqli_fetch_assoc($inv_result);
                        $quantity = $inv_row['quantity_in_stock'];
                    }
                    $materials[] = [
                        'name'     => $row['sub_part_name'],
                        'quantity' => $quantity
                    ];
                } elseif ($row['type'] == 'Manufactured' && !empty($row['sub_part_id'])) {
                    $sub_materials = getAllBoughtInMaterials($conn, $row['sub_part_id'], $visited);
                    $materials     = array_merge($materials, $sub_materials);
                }
            }
        }

        return $materials;
    }
}

if (!function_exists('hasSetInBom')) {
    function hasSetInBom($conn, $part_id, $set_parts, &$visited = []) {
        if (in_array($part_id, $visited)) return false;
        $visited[] = $part_id;

        if (isset($set_parts[$part_id]) && !empty($set_parts[$part_id])) {
            return true;
        }

        $query = "SELECT sub_part_id FROM bom WHERE main_product_id = '$part_id'";
        $res   = mysqli_query($conn, $query);

        while ($row = mysqli_fetch_assoc($res)) {
            if (hasSetInBom($conn, $row['sub_part_id'], $set_parts, $visited)) {
                return true;
            }
        }

        return false;
    }
}
?>

<section class="content-header">
    <h1><?= $page_header; ?></h1>
</section>

<style>
table {
    width: 100%;
    border-collapse: collapse;
    table-layout: fixed;
}
th, td {
    border: 1px solid #ddd;
    padding: 8px;
    text-align: center;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
th:nth-last-child(5), td:nth-last-child(5) { width: 90px; }
th:first-child, td:first-child             { width: 180px; white-space: normal; }
th:last-child, td:last-child               { width: 170px; }
th:nth-child(2), td:nth-child(2)           { width: 100px; }
th:nth-child(3), td:nth-child(3)           { width: 100px; }
th, td                                     { width: 65px; }

#groupModal .modal-dialog,
#setModal .modal-dialog,
#groupDetailsModal .modal-dialog {
    max-width: 100% !important;
    width: 800px !important;
    margin-right: 0 !important;
    margin-left: auto !important;
    margin-top: 20px !important;
    position: fixed;
    right: 20px;
    top: 20px;
    max-height: calc(100vh - 40px);
    overflow-y: auto;
}
#groupModal .modal-content,
#setModal .modal-content,
#groupDetailsModal .modal-content {
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
    border-radius: 8px;
}
#groupModal .modal-body,
#setModal .modal-body,
#groupDetailsModal .modal-body {
    max-height: calc(100vh - 200px);
    overflow-y: auto;
}
.spinner-border-sm {
    width: 1rem;
    height: 1rem;
    margin-right: 5px;
}
.create-order:disabled {
    opacity: 0.7;
    cursor: not-allowed;
}
.order-qty-error  { border-color: #dc3545 !important; }
.error-message    { color: #dc3545; font-size: 12px; display: none; margin-top: 5px; }
.table-responsive {
    max-height: 70vh;
    overflow-y: auto;
    overflow-x: auto;
    position: relative;
}
#example1 thead th {
    position: sticky;
    top: 0;
    z-index: 15;
    background: #f4f6f9;
}
.modal-backdrop { display: none !important; }
#groupModal .modal-dialog,
#setModal .modal-dialog,
#groupDetailsModal .modal-dialog {
    position: fixed;
    right: 20px;
    top: 20px;
    margin: 0;
    max-height: calc(100vh - 40px);
}
.modal-body { max-height: calc(100vh - 180px); overflow-y: auto; }

#bulkGroupModal .modal-dialog,
#bulkGroupDetailsModal .modal-dialog,
#bulkSetModal .modal-dialog {
    max-width: 800px !important;
    width: 800px !important;
    margin-right: 0 !important;
    margin-left: auto !important;
    position: fixed;
    right: 20px;
    top: 20px;
    max-height: calc(100vh - 40px);
}
#bulkGroupModal .modal-content,
#bulkGroupDetailsModal .modal-content,
#bulkSetModal .modal-content {
    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
    border-radius: 8px;
}
#bulkGroupModal .modal-body,
#bulkGroupDetailsModal .modal-body,
#bulkSetModal .modal-body {
    max-height: calc(100vh - 200px);
    overflow-y: auto;
}
.week-cell          { cursor: pointer; user-select: none; }
.week-cell:hover    { outline: 2px solid #378ADD; outline-offset: -2px; }
.week-cell.selected { outline: 3px solid #185FA5; outline-offset: -2px; background: rgba(55, 138, 221, 0.18) !important; }

#sumBar {
    position: fixed;
    bottom: 24px;
    left: 50%;
    transform: translateX(-50%);
    background: #fff;
    border: 1px solid #ddd;
    border-radius: 8px;
    padding: 10px 22px;
    display: flex;
    align-items: center;
    gap: 20px;
    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.12);
    z-index: 9999;
    font-size: 13px;
    transition: opacity 0.2s;
}
#sumBar.hidden { opacity: 0; pointer-events: none; }
</style>

<section class="content">
    <div class="row">
        <div class="col-lg-12">

            <!-- Message -->
            <?php if (isset($_SESSION['info_message'])) { ?>
                <div class="alert alert-success alert-dismissable">
                    <button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button>
                    <?php echo $_SESSION['info_message']; unset($_SESSION['info_message']); ?>
                </div>
            <?php } ?>
            <!-- End Message -->

            <div class="box">
                <div class="box-body">

                    <!-- Search form -->
                    <?php include_once('planning_search_options.inc.php'); ?>
                    <!-- End Search form -->

                    <div class="table-responsive">

                        <div style="margin-bottom: 10px;">
                            <span style="background-color: #adf79d; color: black; padding: 5px; margin-right: 10px;">In Stock</span>
                            <span style="background-color: #fbf9ab; color: black; padding: 5px;">Works Order raised</span>
                        </div>

                        <?php if ($result > 0) { ?>
                        <table id="example1" class="table table-bordered table-striped" style="font-size:12px;">
                            <thead>
                                <tr>
                                    <th>Part Number</th>
                                    <th>Raw Material</th>
                                    <th>Group Number</th>
                                    <th>Stock</th>
                                    <th>Works Order Outstanding</th>
                                    <th>Arrears</th>
                                    <?php foreach ($weeks as $w) { echo "<th>{$w['label']}</th>"; } ?>
                                    <?php if ($can_view) { ?>
                                    <th>Qty</th>
                                    <th>Group</th>
                                    <th>Set</th>
                                    <th class="hide">Efficiency</th>
                                    <th>Action</th>
                                    <?php } ?>
                                </tr>
                            </thead>
                            <tbody>
                            <?php

                            // Build part_data from query results
                            $part_data = [];
                            while ($row = mysqli_fetch_assoc($sql)) {
                                $part_id = $row['part_id'];
                                $week    = $row['week_number'];
                                $qty     = $row['total_req_qty'];
                                $status  = $row['status'];
                                $part_data[$part_id][$week] = [
                                    'qty'    => $qty,
                                    'status' => $status
                                ];
                            }

                            $all_week_keys = array_column($weeks, 'yearweek');

                            // Extend yellow (status=1) weeks through zeros
                            foreach ($part_data as $pid => $weeks_data) {
                                $week_keys   = array_keys($weeks_data);
                                sort($week_keys);
                                $yellow_weeks = [];

                                foreach ($week_keys as $wk) {
                                    if (isset($weeks_data[$wk]['status']) && $weeks_data[$wk]['status'] == 1) {
                                        $yellow_weeks[] = $wk;
                                    }
                                }

                                if (count($yellow_weeks) == 0) continue;

                                $first = min($yellow_weeks);
                                $last  = max($yellow_weeks);

                                foreach ($all_week_keys as $yearweek) {
                                    if ($yearweek > $last) {
                                        if (isset($part_data[$pid][$yearweek])) {
                                            $week_data = $part_data[$pid][$yearweek];
                                            if ($week_data['qty'] > 0 && $week_data['status'] != 1) {
                                                break;
                                            }
                                            $last = $yearweek;
                                        } else {
                                            $last = $yearweek;
                                        }
                                    }
                                }

                                foreach ($all_week_keys as $yearweek) {
                                    if ($yearweek >= $first && $yearweek <= $last) {
                                        if (!isset($part_data[$pid][$yearweek])) {
                                            $part_data[$pid][$yearweek] = ['qty' => 0, 'status' => 1];
                                        } else {
                                            $part_data[$pid][$yearweek]['status'] = 1;
                                        }
                                    }
                                }
                            }

                            // Render each part row
                            foreach ($part_data as $part_id => $weeks_data) {

                                // Inventory stock
                                $inventory_query  = "SELECT quantity_in_stock FROM inventory WHERE item_id = '$part_id'";
                                $inventory_result = mysqli_query($conn, $inventory_query);
                                $inventory_row    = mysqli_fetch_assoc($inventory_result);
                                $original_stock   = (float)($inventory_row['quantity_in_stock'] ?? 0) + 0.0001;

                                // Part info
                                $item_row    = mysqli_fetch_assoc(mysqli_query($conn, "SELECT part_number, price_uom FROM item_management WHERE id = '$part_id' LIMIT 1"));
                                $part_number = $item_row['part_number'] ?? '';
                                $price_uom   = $item_row['price_uom'] ?? '';

                                // Set check
                                $visited         = [];
                                $has_set         = hasSetInBom($conn, $part_id, $set_parts, $visited);
                                $set_check_value = $has_set ? '1' : '0';

                                // Group type check
                                // NOTE: this LIMIT 1 query only decides how the checkbox is
                                // *rendered* on page load (forced-checked vs toggleable). It does
                                // NOT decide actual behaviour on click — that always goes through
                                // the server-side check_group_type AJAX call, which correctly scans
                                // every group the part belongs to. If a part sits in both a mandatory
                                // and an optional group, prefer showing it as mandatory here so the
                                // checkbox isn't misleading; order by group_type so 'mandatory' wins.
                                $group_type_query  = "
                                    SELECT gm.group_type 
                                    FROM group_mangmnt_mapping gmm 
                                    JOIN group_mangmnt gm ON gm.id = gmm.group_id 
                                    WHERE gmm.part_id = '$part_id' 
                                    ORDER BY (gm.group_type = 'mandatory') DESC
                                    LIMIT 1
                                ";
                                $group_type_result   = mysqli_query($conn, $group_type_query);
                                $group_type_row      = mysqli_fetch_assoc($group_type_result);
                                $is_mandatory_group  = isset($group_type_row['group_type']) && $group_type_row['group_type'] === 'mandatory';
                                $group_check_value   = $is_mandatory_group ? '1' : '0';

                                // Works Order Outstanding — only status=2 (real raised orders)
                                // status=1 rows are already visible as week demand — excluded to avoid double counting
                                $produced_query = "
    SELECT 
        SUM(
            CASE 
                WHEN status = 1 THEN produce_qty
                WHEN status = 2 THEN produce_qty
                ELSE 0
            END
        ) AS total
    FROM production_planning
    WHERE part_id = '$part_id'
    AND status IN (1, 2)
";
                                $produced_result = mysqli_query($conn, $produced_query);
                                $produced_row    = mysqli_fetch_assoc($produced_result);
                                $produced_qty    = $produced_row['total'] ?? 0;

                                // Arrears — overdue orders before current week
                                $current_week_start = date('Y-m-d', strtotime('monday this week'));
                                $arrears_query      = "
                                    SELECT SUM(req_qty) AS total
                                    FROM production_planning
                                    WHERE part_id = '$part_id'
                                    AND req_date < '$current_week_start'
                                    AND status != 1
                                ";
                                $arrears_result = mysqli_query($conn, $arrears_query);
                                $arrears_row    = mysqli_fetch_assoc($arrears_result);
                                $arrears_qty    = $arrears_row['total'] ?? 0;
                            ?>
                            <tr>
                                <!-- Part Number -->
                                <td>
                                    <span class="toggle-group"
                                          data-group="<?= $group_numbers[$part_id] ?? '' ?>"
                                          data-part="<?= $part_id ?>"
                                          style="cursor:pointer; color:#007bff;">
                                        <a href="<?= $config->frontEndURL . '/admin/work_order_management/index?page=view&part_id=' . urlencode(utility::app_crypt($part_id, 'e')) . '&from=' . urlencode($from_source); ?>"
                                           target="_blank"
                                           class="text-primary part-number-link">
                                            <?= ucfirst($part_number); ?>
                                        </a>
                                        <span class="arrow">&#9660;</span>
                                    </span>
                                    <?php if (!empty($price_uom)): ?>
                                        <div style="font-size:11px; color:#555; margin-top:3px;">
                                            <strong>MBQ:</strong> <?= htmlspecialchars($price_uom); ?>
                                        </div>
                                    <?php endif; ?>
                                </td>

                                <!-- Raw Materials -->
                                <td>
                                    <?php
                                    $visited_parts   = [];
                                    $raw_materials   = getAllBoughtInMaterials($conn, $part_id, $visited_parts);
                                    $unique_materials = [];
                                    foreach ($raw_materials as $material) {
                                        $name = $material['name'];
                                        if (!isset($unique_materials[$name])) {
                                            $unique_materials[$name] = $material['quantity'];
                                        }
                                    }
                                    ksort($unique_materials);
                                    $count = count($unique_materials);

                                    if ($count > 0) {
                                        $materials_list = [];
                                        foreach ($unique_materials as $name => $quantity) {
                                            $materials_list[] = htmlspecialchars($name) . ' (Qty: ' . htmlspecialchars($quantity) . ')';
                                        }
                                        $materials_display = implode('<br>', $materials_list);
                                        echo "<span class='raw-material-click'
                                                    data-toggle='popover'
                                                    data-placement='top'
                                                    data-html='true'
                                                    data-content='" . htmlspecialchars($materials_display, ENT_QUOTES) . "'
                                                    style='cursor:pointer; color:#007bff; text-decoration:underline;'>
                                                $count item(s)
                                              </span>";
                                    } else {
                                        echo '-';
                                    }
                                    ?>
                                </td>

                                <!-- Group Number -->
                                <td><?= $group_numbers[$part_id] ?? ''; ?></td>

                                <!-- Stock -->
                                <td><?= intval($original_stock); ?></td>

                                <!-- Works Order Outstanding -->
                                <td><?= $produced_qty; ?></td>

                                <!-- Arrears -->
                                <?php
                                if ($arrears_qty <= 0) {
                                    $arrears_color = '';
                                } elseif ($original_stock >= $arrears_qty) {
                                    $arrears_color = 'background-color: #5bc236; color: white; font-weight: bold;';
                                } else {
                                    $arrears_color = 'background-color: #ff6b6b; color: white; font-weight: bold;';
                                }
                                ?>
                                <td style="<?= $arrears_color ?>">
                                    <?= number_format($arrears_qty, 1) ?>
                                </td>

                                <!-- Week Cells -->
                                <?php
                                // Stock covers first → then Works Order Outstanding → then white
$remaining_stock  = max(0, $original_stock - $arrears_qty);
$remaining_wo     = (float)$produced_qty;
$stock_exhausted  = false;

foreach ($weeks as $w) {
    $week_key = $w['yearweek'];
    $data     = $part_data[$part_id][$week_key] ?? ['qty' => 0, 'status' => 0];
    $qty      = $data['qty'];

    if ($qty == 0) {
        if ($remaining_stock > 0 && !$stock_exhausted) {
            $color = "#adf79d"; // GREEN
        } elseif ($stock_exhausted && $remaining_wo > 0) {
            $color = '#fff38c'; // YELLOW
        } else {
            $color = 'white';
        }
        echo "<td class='week-cell' style='background-color: $color; text-align: center;'>0</td>";
        continue;
    }

    if (!$stock_exhausted && $remaining_stock >= $qty) {
        // Stock covers fully — GREEN
        $color            = "#adf79d";
        $remaining_stock -= $qty;

    } else {
        // Stock cannot fully cover — deduct FULL qty from WO
        $stock_exhausted  = true;
        $remaining_stock  = 0;

        if ($remaining_wo >= $qty) {
            // WO covers full week qty — YELLOW
            $color         = '#fff38c';
            $remaining_wo -= $qty;
        } else {
            // WO cannot cover — WHITE
            $color        = 'white';
            $remaining_wo = 0;
        }
    }

    echo "<td class='week-cell' style='background-color: $color; text-align: center;'>$qty</td>";
}
                                ?>

                                <?php if ($can_view) { ?>
                                <!-- Order Qty -->
                                <td>
                                    <input type="number" id="order_qty_<?= $part_id; ?>" class="form-control order_qty" min="1" placeholder="Qty" required>
                                    <div id="error_qty_<?= $part_id; ?>" class="error-message">Please enter a valid quantity</div>
                                </td>

                                <!-- Group Check -->
                                <td>
                                    <input type="hidden"
                                           name="group_check_hidden"
                                           id="group_check_hidden_<?= $part_id; ?>"
                                           value="<?= $group_check_value; ?>">
                                    <input type="checkbox"
                                           class="form-control group_check"
                                           id="group_check<?= $part_id; ?>"
                                           name="group_check"
                                           data-part-id="<?= $part_id; ?>"
                                           value="1"
                                           <?= $is_mandatory_group ? 'checked disabled' : ''; ?>>
                                    <?php if ($is_mandatory_group): ?>
                                        <small style="color:#c0392b; font-weight:bold; display:block;">Mandatory</small>
                                    <?php endif; ?>
                                </td>

                                <!-- Set Check -->
                                <td>
                                    <input type="hidden"
                                           name="set_check_hidden"
                                           id="set_check_hidden_<?= $part_id; ?>"
                                           value="<?= $set_check_value; ?>">
                                    <input type="checkbox"
                                           class="form-control set_check"
                                           id="set_check_<?= $part_id; ?>"
                                           name="set_check"
                                           value="1"
                                           <?= $has_set ? 'checked' : ''; ?>>
                                </td>

                                <!-- Efficiency (hidden) -->
                                <td class="hide">
                                    <input type="text" id="efficiency_<?= $part_id; ?>" name="efficiency" value="<?= $efficiency; ?>" class="form-control" placeholder="">
                                    <input type="hidden" id="current_part_id" value="">
                                </td>

                                <!-- Action -->
                                <td>
                                    <button class="btn btn-danger create-order" data-part-id="<?= $part_id; ?>">
                                        <span class="btn-text">Create Work Order</span>
                                        <span class="spinner-border spinner-border-sm" style="display: none;"></span>
                                    </button>
                                </td>
                                <?php } ?>
                            </tr>

                            <?php
                            // Group expand row
                            $grp = $group_numbers[$part_id] ?? '';
                            if ($grp && isset($group_parts[$grp])) {
                                echo "<tr class='group-row group-row-$part_id' style='display:none; background:#f7f7f7;'>
                                        <td colspan='15' style='text-align:left;'>
                                            <strong>Parts in Group $grp:</strong><br>";
                                foreach ($group_parts[$grp] as $gp) {
                                    echo "<div style='margin-left:20px;'>
                                            • <a href='" . $config->frontEndURL . "/admin/work_order_management/index?page=view&part_id="
                                                . urlencode(utility::app_crypt($gp['part_id'], 'e'))
                                                . "&from=$from_source' target='_blank'>
                                                {$gp['part_number']}
                                              </a>
                                          </div>";
                                }
                                echo "</td></tr>";
                            }
                            ?>

                            <?php } // end foreach part_data ?>

                            <?php
                            $next_page_condition .= "&page=view_production";
                            include("paging/paging_row.inc.php");
                            ?>
                            </tbody>
                        </table>
                        <?php } else { ?>
                            <p>No Order found...</p>
                        <?php } ?>

                    </div><!-- /.table-responsive -->
                </div><!-- /.box-body -->
            </div><!-- /.box -->
        </div><!-- /.col -->
    </div><!-- /.row -->
</section>

<!-- Sum Bar -->
<div id="sumBar" class="hidden">
    <span style="color:#888">Sum</span>
    <span id="sumValue" style="font-size:18px; font-weight:500">0</span>
    <span id="sumCount" style="color:#888; font-size:12px">0 cells</span>
    <span style="color:#888; margin-left:8px">Avg</span>
    <span id="avgValue" style="font-size:14px; color:#888">0</span>
    <button id="clearSelBtn" style="border:1px solid #ddd; border-radius:6px; padding:4px 10px; cursor:pointer; background:none">✕ Clear</button>
</div>

<!-- Modals -->
<div class="modal fade" id="groupModal" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Groups</h5>
                <button type="button" class="close" data-dismiss="modal">&times;</button>
            </div>
            <div class="modal-body">
                <div id="groupItemsContainer"></div>
            </div>
        </div>
    </div>
</div>

<div class="modal fade" id="setModal" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Set Items</h5>
                <button type="button" class="close" data-dismiss="modal">&times;</button>
            </div>
            <div class="modal-body">
                <div id="setItemsContainer"></div>
            </div>
        </div>
    </div>
</div>

<div id="groupDetailsModal" class="modal fade" tabindex="-1" role="dialog">
    <div class="modal-dialog">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Group Details</h5>
                <button type="button" class="close" data-dismiss="modal">&times;</button>
            </div>
            <div class="modal-body">
                <p>Loading details...</p>
            </div>
        </div>
    </div>
</div>

<!-- Bulk Modals -->
<div class="modal fade" id="bulkProcessModal" tabindex="-1" role="dialog" data-backdrop="static" data-keyboard="false">
    <div class="modal-dialog modal-lg" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Bulk Work Order Creation</h5>
            </div>
            <div class="modal-body">
                <div id="bulkProcessContent">
                    <p>Processing parts with quantities...</p>
                    <div class="progress">
                        <div id="bulkProgressBar" class="progress-bar" role="progressbar" style="width: 0%">0%</div>
                    </div>
                    <div id="bulkCurrentItem" style="margin-top: 10px;"></div>
                </div>
            </div>
        </div>
    </div>
</div>

<div class="modal fade" id="bulkGroupModal" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Select Group for <span id="bulkGroupPartName"></span></h5>
            </div>
            <div class="modal-body">
                <div id="bulkGroupItemsContainer"></div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" id="bulkSkipGroup">Skip This Part</button>
            </div>
        </div>
    </div>
</div>

<div class="modal fade" id="bulkGroupDetailsModal" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Group Details for <span id="bulkGroupDetailsPartName"></span></h5>
            </div>
            <div class="modal-body">
                <div id="bulkGroupDetailsContainer"></div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" id="bulkSkipGroupDetails">Skip This Part</button>
            </div>
        </div>
    </div>
</div>

<div class="modal fade" id="bulkSetModal" tabindex="-1" role="dialog">
    <div class="modal-dialog" role="document">
        <div class="modal-content">
            <div class="modal-header">
                <h5 class="modal-title">Select Sets for <span id="bulkSetPartName"></span></h5>
            </div>
            <div class="modal-body">
                <div id="bulkSetItemsContainer"></div>
            </div>
            <div class="modal-footer">
                <button type="button" class="btn btn-secondary" id="bulkSkipSet">Skip This Part</button>
            </div>
        </div>
    </div>
</div>

<div style="margin-bottom: 10px;">
    <button id="bulkCreateOrders" class="btn btn-primary hide">
        <span class="btn-text">Bulk Create Work Orders</span>
        <span class="spinner-border spinner-border-sm" style="display: none;"></span>
    </button>
    <span id="bulkProgress" style="margin-left: 10px; display: none;"></span>
</div>

<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

<script>
$(document).ready(function () {
    if (!$('[data-toggle="popover"]').data('bs.popover')) {
        $('[data-toggle="popover"]').popover({ trigger: 'click', html: true, container: 'body' });
        $('body').on('click', function (e) {
            $('[data-toggle="popover"]').each(function () {
                if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
                    $(this).popover('hide');
                }
            });
        });
    }
});

$(document).on('wheel', '.modal', function (e) { e.stopPropagation(); });

// Group rearrange
document.addEventListener('DOMContentLoaded', function () {
    let originalOrder  = null;
    let currentGrouped = null;

    function shareCommonGroup(groupStr1, groupStr2) {
        if (!groupStr1 || !groupStr2) return false;
        const groups1 = groupStr1.split(',').map(g => g.trim()).filter(g => g);
        const groups2 = groupStr2.split(',').map(g => g.trim()).filter(g => g);
        return groups1.some(g1 => groups2.includes(g1));
    }

    function rearrangeByGroup(groupString, clickedPartId) {
        if (!groupString) return;

        const tbody   = document.querySelector('table tbody');
        const allRows = Array.from(tbody.querySelectorAll('tr:not(.group-row)'));

        if (!originalOrder) {
            originalOrder = allRows.map(row => ({
                element:     row.cloneNode(true),
                partId:      row.querySelector('.toggle-group')?.getAttribute('data-part'),
                groupString: row.querySelector('.toggle-group')?.getAttribute('data-group')
            }));
        }

        Array.from(tbody.querySelectorAll('tr')).forEach(row => { row.style.backgroundColor = ''; });

        if (currentGrouped === groupString) {
            tbody.innerHTML = '';
            originalOrder.forEach(item => {
                const clonedRow = item.element.cloneNode(true);
                tbody.appendChild(clonedRow);
                const originalGroupRow = document.querySelector('.group-row-' + item.partId);
                if (originalGroupRow) tbody.appendChild(originalGroupRow.cloneNode(true));
            });
            currentGrouped = null;
            reattachEventListeners();
            return;
        }

        currentGrouped = groupString;

        const groupRows    = [];
        const nonGroupRows = [];

        allRows.forEach(row => {
            const toggleSpan     = row.querySelector('.toggle-group');
            const rowGroupString = toggleSpan?.getAttribute('data-group');
            const rowPartId      = toggleSpan?.getAttribute('data-part');
            if (shareCommonGroup(rowGroupString, groupString)) {
                groupRows.push({ mainRow: row, partId: rowPartId, groupString: rowGroupString });
            } else {
                nonGroupRows.push({ mainRow: row, partId: rowPartId, groupString: rowGroupString });
            }
        });

        const clickedRowIndex = allRows.findIndex(row => {
            const partId = row.querySelector('.toggle-group')?.getAttribute('data-part');
            return partId === clickedPartId;
        });

        const beforeRows = nonGroupRows.filter(item => allRows.indexOf(item.mainRow) < clickedRowIndex);
        const afterRows  = nonGroupRows.filter(item => allRows.indexOf(item.mainRow) >= clickedRowIndex);

        tbody.innerHTML = '';

        beforeRows.forEach(item => {
            tbody.appendChild(item.mainRow.cloneNode(true));
            const expandRow = document.querySelector('.group-row-' + item.partId);
            if (expandRow) tbody.appendChild(expandRow.cloneNode(true));
        });

        groupRows.forEach(item => {
            const clonedRow = item.mainRow.cloneNode(true);
            clonedRow.style.backgroundColor = '#d4edff';
            tbody.appendChild(clonedRow);
            const expandRow = document.querySelector('.group-row-' + item.partId);
            if (expandRow) tbody.appendChild(expandRow.cloneNode(true));
        });

        afterRows.forEach(item => {
            tbody.appendChild(item.mainRow.cloneNode(true));
            const expandRow = document.querySelector('.group-row-' + item.partId);
            if (expandRow) tbody.appendChild(expandRow.cloneNode(true));
        });

        if (groupRows.length > 0) {
            const firstGroupedRow = Array.from(tbody.querySelectorAll('tr:not(.group-row)')).find(row => {
                const grp = row.querySelector('.toggle-group')?.getAttribute('data-group');
                return shareCommonGroup(grp, groupString);
            });
            if (firstGroupedRow) firstGroupedRow.scrollIntoView({ behavior: 'smooth', block: 'center' });
        }

        reattachEventListeners();
    }

    function reattachEventListeners() {
        document.querySelectorAll('.toggle-group').forEach(span => { span.style.cursor = 'pointer'; });
    }

    document.addEventListener('click', function (e) {
        const toggleSpan = e.target.closest('.toggle-group');
        const partLink   = e.target.closest('.part-number-link');
        if (partLink) return;
        if (toggleSpan) {
            e.preventDefault();
            e.stopPropagation();
            rearrangeByGroup(toggleSpan.getAttribute('data-group'), toggleSpan.getAttribute('data-part'));
        }
    });

    document.addEventListener('mouseover', function (e) {
        const toggleSpan = e.target.closest('.toggle-group');
        if (toggleSpan && !e.target.closest('.part-number-link')) toggleSpan.style.backgroundColor = '#f0f0f0';
    });

    document.addEventListener('mouseout', function (e) {
        const toggleSpan = e.target.closest('.toggle-group');
        if (toggleSpan) toggleSpan.style.backgroundColor = '';
    });
});

// Group / Set / Order
$(document).on('change', '.group_check', function () {
    let partId = $(this).data('part-id');
    $('#order_qty_' + partId).prop('required', $(this).is(':checked'));
});

$(document).ready(function () {
    $('.group_check').each(function () { $(this).trigger('change'); });
});

let selectedGroupId = null;
let currentGroupId  = 0;

$(document).ready(function () {
    $(document).on('click', '.group-item-row_1', function () {
        selectedGroupId    = $(this).data('group-id');
        currentGroupId     = selectedGroupId;
        selectedPartId     = $(this).data('part-id');
        selectedOrderQty   = $(this).data('orderqty-id');
        let selectedSetCheck = $(this).find('.set_check_value').val();
        let partQtyMap       = $('#groupModal').data('partQtyMap') || {};

        $.ajax({
            url:  site_url + '/modules/production_planning/index.php?page=ajax_actions&action=fetch_group_details',
            type: 'POST',
            data: { group_id: selectedGroupId, part_id: selectedPartId, OrderQty: selectedOrderQty, set_check: selectedSetCheck },
            success: function (response) {
                $('#groupDetailsModal .modal-body').html(response);
                $('.group-item-row').each(function () {
                    let groupItemId = String($(this).data('group-item-id'));
                    if (partQtyMap[groupItemId] !== undefined) {
                        $(this).find('.group-item-qty').val(partQtyMap[groupItemId]);
                    }
                });
                $('#groupDetailsModal').modal('show');
            }
        });
    });
});

$(document).ready(function () {
    $(document).on('change', '.group_check', function () {
        if ($(this).prop('disabled')) { $(this).prop('checked', true); return; }
        let partId    = $(this).attr('id').replace('group_check', '');
        let isChecked = $(this).is(':checked') ? '1' : '0';
        $('#group_check_hidden_' + partId).val(isChecked);
    });

    $(document).on('change', '.set_check', function () {
        let partId    = $(this).attr('id').split('_')[2];
        let isChecked = $(this).is(':checked') ? '1' : '0';
        $('#set_check_hidden_' + partId).val(isChecked);
    });
});

// Validation helpers
function validateQuantity(partId) {
    let qtyInput = $('#order_qty_' + partId);
    let qtyValue = qtyInput.val();
    let errorDiv = $('#error_qty_' + partId);
    qtyInput.removeClass('order-qty-error');
    errorDiv.hide();
    if (!qtyValue || qtyValue <= 0) {
        qtyInput.addClass('order-qty-error');
        errorDiv.show();
        return false;
    }
    return true;
}

function showButtonSpinner(button) {
    button.prop('disabled', true);
    button.find('.btn-text').hide();
    button.find('.spinner-border-sm').show();
}

function hideButtonSpinner(button) {
    button.prop('disabled', false);
    button.find('.btn-text').show();
    button.find('.spinner-border-sm').hide();
}

// Create Work Order
$(document).ready(function () {
    $(document).on('click', '.create-order', function () {
        let partId = $(this).data('part-id');
        $('#current_part_id').val(partId);
        let button = $(this);

        if (!validateQuantity(partId)) { $('#order_qty_' + partId).focus(); return; }
        showButtonSpinner(button);

        let isChecked  = $('#group_check' + partId).is(':checked');
        let qty        = $('#order_qty_' + partId).val();
        let set_check  = $('#set_check_hidden_' + partId).val();
        let group_check = $('#group_check_hidden_' + partId).val();
        let efficiency  = $('#efficiency_' + partId).val();
        let orderQty    = $('#order_qty_' + partId).val();

        if (isChecked && (!qty || qty <= 0)) {
            alert('Please enter a valid quantity for part ID: ' + partId);
            $('#order_qty_' + partId).focus();
            hideButtonSpinner(button);
            return;
        }

if (group_check === '1') {
    let allPartQtyMap = {};
    $('.order_qty').each(function () {
        let val = parseFloat($(this).val()) || 0;
        let pid = $(this).attr('id').replace('order_qty_', '');
        allPartQtyMap[pid] = val;
    });

    $.ajax({
        url:      site_url + '/modules/production_planning/index.php?page=ajax_actions&action=check_group_type',
        type:     'POST',
        dataType: 'json',
        data:     { part_id: partId },
        success: function (res) {
            // ------------------------------------------------------------
            // UNIFIED RULE: exactly one group (mandatory OR optional) -> auto
            // resolve straight to details, nothing to choose. Two or more
            // groups, any mix of mandatory/optional -> always show the
            // picker (fetch_group_items), which already renders a type
            // badge (Mandatory/Optional) per row.
            // ------------------------------------------------------------
            if (res.group_type === 'mandatory' || res.group_type === 'optional') {
                currentGroupId = res.group_id;
                $.ajax({
                    url:  site_url + '/modules/production_planning/index.php?page=ajax_actions&action=fetch_group_details',
                    type: 'POST',
                    data: { group_id: res.group_id, part_id: partId, OrderQty: orderQty, set_check: set_check, efficiency: efficiency },
                    success: function (response) {
                        $('#groupDetailsModal .modal-body').html(response);
                        // Overwrite each row's qty with whatever was already typed in the main table for that part
                        $('.group-item-row').each(function () {
                            let rowPartId = String($(this).data('group-item-id'));
                            if (rowPartId !== String(partId) && allPartQtyMap[rowPartId] !== undefined && allPartQtyMap[rowPartId] > 0) {
                                $(this).find('.group-item-qty').val(allPartQtyMap[rowPartId]);
                            }
                        });
                        $('#groupModal').data('partQtyMap', allPartQtyMap);
                        $('#groupDetailsModal').modal('show');
                        hideButtonSpinner(button);
                    },
                    error: function () { hideButtonSpinner(button); alert('Error fetching group details.'); }
                });

            } else if (res.group_type === 'multiple') {
                // 2+ groups (mandatory and/or optional) — always let the user pick.
                // fetch_group_items already lists every group with a type badge, and
                // the .group-item-row_1 click handler above is generic — it works
                // the same whether the chosen row is mandatory or optional.
                $.ajax({
                    url:  site_url + '/modules/production_planning/index.php?page=ajax_actions&action=fetch_group_items',
                    type: 'POST',
                    data: { part_id: partId, group_check: group_check, set_check: set_check, efficiency: efficiency, order_qty: orderQty },
                    success: function (response) {
                        $('#groupItemsContainer').html(response);
                        $('#groupModal').data('partQtyMap', allPartQtyMap);
                        $('#groupModal').modal('show');
                        hideButtonSpinner(button);
                    },
                    error: function () { hideButtonSpinner(button); alert('Error fetching group items.'); }
                });

            } else {
                // 'none' — group_check was on but the part has no group configured
                hideButtonSpinner(button);
                alert('No group configuration found for this part.');
            }
        },
        error: function () { hideButtonSpinner(button); alert('Error checking group type.'); }
    });

        } else {
            currentGroupId = 0;
            let orderData  = [{ groupItemId: partId, quantity: orderQty }];

            if (set_check === '1') {
                $.ajax({
                    url:  site_url + '/modules/production_planning/index.php?page=ajax_actions&action=fetch_set_items',
                    type: 'POST',
                    data: { orderData: JSON.stringify(orderData) },
                    success: function (response) {
                        $('#setItemsContainer').html(response);
                        $('#setModal').modal('show');
                        hideButtonSpinner(button);
                    },
                    error: function () { hideButtonSpinner(button); alert('An error occurred while fetching set items.'); }
                });
            } else {
                submitOrder(orderData, button, currentGroupId);
            }
        }
    });

    // Submit Group Order
    $(document).on('click', '#submitGroupOrder', function (e) {
        e.preventDefault();
        let $button = $(this);
        $button.prop('disabled', true).html('<span class="spinner-border spinner-border-sm"></span> Submitting...');

        let orderData = [];
        $('.group-item-row').each(function () {
            let groupItemId = $(this).data('group-item-id');
            let quantity    = parseFloat($(this).find('.group-item-qty').val()) || 0;
            let ratio       = parseFloat($(this).find('.group-item-ratio').val()) || 1;
            $(this).find('.group-item-qty').removeClass('order-qty-error');
            if (quantity > 0) orderData.push({ groupItemId: groupItemId, quantity: quantity, ratio: ratio });
        });

        $('#groupModal').modal('hide');
        $('#groupDetailsModal').modal('hide');

        let currentPartId = $('#current_part_id').val();
        let set_check     = $('#set_check_hidden_' + currentPartId).val();
        if (!set_check || set_check === '') {
            set_check = $('.group-item-row .set_check_value').first().val() || '0';
        }

        if (set_check === '1') {
            $.ajax({
                url:  site_url + '/modules/production_planning/index.php?page=ajax_actions&action=fetch_set_items',
                type: 'POST',
                data: { orderData: JSON.stringify(orderData) },
                success: function (response) {
                    $('#setItemsContainer').html(response);
                    $('#setModal').modal('show');
                    $button.prop('disabled', false).html('Submit Order');
                },
                error: function () {
                    $button.prop('disabled', false).html('Submit Order');
                    alert('An error occurred while fetching set items.');
                }
            });
        } else {
            submitOrder(orderData, null, currentGroupId);
        }
    });

    // Submit Set Order
    $(document).on('click', '#submitSetOrder', function (e) {
        e.preventDefault();
        let $button = $(this);
        $button.prop('disabled', true).html('<span class="spinner-border spinner-border-sm"></span> Submitting...');

        let setorderData = [];
        $('.set-item-row').each(function () {
            if ($(this).find('.set-group-checkbox').is(':checked')) {
                let partIds = $(this).data('part-ids').toString().split(',');
                let setId   = $(this).data('set-id');
                partIds.forEach(partId => {
                    if (partId.trim() !== '') setorderData.push({ setItemId: partId.trim(), setId: setId });
                });
            }
        });

        let orderData = [];
        try {
            orderData = JSON.parse($('#orderDataHidden').val());
        } catch (err) {
            alert('Internal data error. Please refresh and try again.');
            $button.prop('disabled', false).html('Submit');
            return;
        }

        $.ajax({
            url:  site_url + '/modules/production_planning/index.php?page=ajax_actions&action=work_order_fuction',
            type: 'POST',
            data: { setorderData: JSON.stringify(setorderData), orderData: JSON.stringify(orderData), group_id: currentGroupId },
            success: function (response) {
                $button.prop('disabled', false).html('Submit');
                try {
                    let res = JSON.parse(response);
                    if (res.status === 'success') { alert(res.message || 'Order successfully created.'); saveScrollPosition(); location.reload(); }
                    else { alert(res.message || 'An error occurred while processing the order.'); }
                } catch (err) { alert('Order successfully processed.'); saveScrollPosition(); location.reload(); }
            },
            error: function () { alert('An unexpected error occurred.'); $button.prop('disabled', false).html('Submit'); }
        });
    });

    $(document).on('input', '.order_qty', function () {
        validateQuantity($(this).attr('id').replace('order_qty_', ''));
    });
});

// Submit 
function submitOrder(orderData, button = null, groupId = 0) {
    $.ajax({
        url:  site_url + '/modules/production_planning/index.php?page=ajax_actions&action=work_order_fuction',
        type: 'POST',
        data: { orderData: JSON.stringify(orderData), group_id: groupId },
        success: function (response) {
            try {
                var res = JSON.parse(response);
                if (res.status === 'success') { alert(res.message); saveScrollPosition(); location.reload(); }
                else { alert('An error occurred while processing the order.'); if (button) hideButtonSpinner(button); }
            } catch (e) { alert('Successfully Done'); saveScrollPosition(); location.reload(); }
        },
        error: function () { alert('Something went wrong.'); if (button) hideButtonSpinner(button); }
    });
}

function submitOrderset(setorderData, orderData, button, groupId = 0) {
    $.ajax({
        url:  site_url + '/modules/production_planning/index.php?page=ajax_actions&action=work_order_fuction',
        type: 'POST',
        data: { setorderData: JSON.stringify(setorderData), orderData: JSON.stringify(orderData), group_id: groupId },
        success: function (response) {
            try {
                var res = JSON.parse(response);
                if (res.status === 'success') { alert(res.message); saveScrollPosition(); location.reload(); }
                else { alert('An error occurred.'); button.prop('disabled', false).html('Submit Order'); }
            } catch (e) { alert('An unexpected error occurred.'); saveScrollPosition(); location.reload(); }
        },
        error: function () { alert('Successfully Done'); saveScrollPosition(); location.reload(); }
    });
}

function selectBulkMandatoryGroup(groupId) {
    let part = $('#bulkGroupModal').data('pendingPart');
    if (!part) return;
    $('#bulkGroupModal').modal('hide');
    fetchBulkGroupDetails(groupId, part);
}
$(document).on('click', '#calculateGroupQty', function (e) {
    e.preventDefault();
    let baseQty = 0;
    $('.group-item-row').each(function () {
        let qty = parseFloat($(this).find('.group-item-qty').val()) || 0;
        if (qty > 0 && baseQty === 0) baseQty = qty;
    });
    if (baseQty === 0) { alert('Please enter a base part quantity first.'); }
});

document.addEventListener('DOMContentLoaded', function () {
    if (window.location.href.includes('page=view_production')) {
        document.body.classList.add('sidebar-mini', 'layout-fixed', 'sidebar-collapse');
    }
});

// Bulk Work Order Creation
$(document).ready(function () {
    let bulkQueue        = [];
    let currentBulkIndex = 0;
    let bulkOrderData    = null;
    let currentBulkPart  = null;

    // Tracks part_ids that have already been consumed by a submitted work
    // order during this bulk run. Any later queue entry whose parts are
    // ALL already in this set gets skipped — this is what prevents the
    // "one part gets ordered twice" bug (once inside a group order, once
    // again as a leftover standalone order).
    let processedPartIds = new Set();

$('#bulkCreateOrders').on('click', function () {
    bulkQueue = [];
    processedPartIds = new Set();
    window.bulkPartQtyMap = {};

    // Collect all parts with quantities
    let partsWithQty = [];
    $('.order_qty').each(function () {
        let qtyValue = parseFloat($(this).val()) || 0;
        if (qtyValue > 0) {
            let partId     = $(this).attr('id').replace('order_qty_', '');
            let partNumber = $(this).closest('tr').find('.part-number-link').text().trim();
            let groupCheck = $('#group_check_hidden_' + partId).val();
            let setCheck   = $('#set_check_hidden_' + partId).val();
            let efficiency = $('#efficiency_' + partId).val();

            window.bulkPartQtyMap[partId] = qtyValue;

            partsWithQty.push({ partId, partNumber, qtyValue, groupCheck, setCheck, efficiency });
        }
    });

    if (partsWithQty.length === 0) {
        alert('No parts with quantities found. Please enter quantities first.');
        return;
    }

    // Resolve group membership for every part with a quantity — NOT just the
    // ones whose checkbox happens to be ticked. Group membership is a
    // database fact (group_mangmnt_mapping), not something the per-row
    // checkbox should gate for dedup purposes: if part A is ticked and part
    // B (same group, untouched checkbox) also has a quantity, we still need
    // to know B belongs to the same group so it doesn't get queued twice.
    let fetchPromises = partsWithQty.map(part => {
        return $.ajax({
            url:      site_url + '/modules/production_planning/index.php?page=ajax_actions&action=check_group_type',
            type:     'POST',
            dataType: 'json',
            data:     { part_id: part.partId }
        }).then(res => ({
            ...part,
            groupId:   (res.group_type === 'mandatory' || res.group_type === 'optional') ? String(res.group_id) : null,
            groupType: res.group_type
        })).catch(() => ({ ...part, groupId: null, groupType: 'none' }));
    });

    Promise.all(fetchPromises).then(resolvedParts => {
        let seenGroupIds   = new Set();
        let groupMap        = {};
        let ambiguousParts  = []; // parts with 2+ possible groups — resolved interactively via the picker

        resolvedParts.forEach(part => {
            if (part.groupType === 'multiple') {
                // Can't dedup this yet — we don't know which of its several
                // groups the user will pick. Queue it as its own entry and
                // let checkBulkGroupType/fetchBulkGroupList resolve it live.
                ambiguousParts.push(part);
                return;
            }

            // Deduplication key: real group_id for grouped parts, unique key for singles
            let key = (part.groupId) ? 'group_' + part.groupId : 'single_' + part.partId;

            // Skip if this group was already claimed by another member part
            if (part.groupId && seenGroupIds.has(part.groupId)) {
                return;
            }
            if (part.groupId) seenGroupIds.add(part.groupId);

            if (!groupMap[key]) {
                groupMap[key] = {
                    parts:       [],
                    partNumbers: [],
                    groupCheck:  part.groupCheck,
                    setCheck:    part.setCheck,
                    efficiency:  part.efficiency
                };
            }

            groupMap[key].parts.push({ partId: part.partId, quantity: part.qtyValue });
            groupMap[key].partNumbers.push(part.partNumber);
        });

        bulkQueue = Object.values(groupMap).map(item => ({
            parts:      item.parts,
            partIds:    item.parts.map(p => p.partId),
            partNumber: item.partNumbers.join(', '),
            groupCheck: item.groupCheck,
            setCheck:   item.setCheck,
            efficiency: item.efficiency
        }));

        // Ambiguous (multi-group) parts go in as their own single-part
        // entries. processedPartIds guards against them being re-submitted
        // if the group the user eventually picks also covers a part that
        // was already ordered elsewhere in this run.
        ambiguousParts.forEach(part => {
            bulkQueue.push({
                parts:      [{ partId: part.partId, quantity: part.qtyValue }],
                partIds:    [part.partId],
                partNumber: part.partNumber,
                groupCheck: part.groupCheck,
                setCheck:   part.setCheck,
                efficiency: part.efficiency
            });
        });

        if (bulkQueue.length === 0) {
            alert('No parts to process.');
            return;
        }

        if (confirm(`Found ${bulkQueue.length} order(s) to create. Start bulk creation?`)) {
            currentBulkIndex = 0;
            $('#bulkProcessModal').modal('show');
            processBulkNext();
        }
    });
});

    function processBulkNext() {
        if (currentBulkIndex >= bulkQueue.length) {
            $('#bulkProcessContent').html('<div class="alert alert-success">All work orders created successfully!</div>');
            setTimeout(function () { $('#bulkProcessModal').modal('hide'); saveScrollPosition(); location.reload(); }, 2000);
            return;
        }

        currentBulkPart = bulkQueue[currentBulkIndex];

        // If every part in this entry was already covered by an earlier
        // group order in this same run, skip it — this is what closes the
        // "part gets ordered twice" gap for ambiguous multi-group parts.
        if (currentBulkPart.partIds.every(id => processedPartIds.has(String(id)))) {
            currentBulkIndex++;
            return processBulkNext();
        }

        let progress    = Math.round(((currentBulkIndex + 1) / bulkQueue.length) * 100);
        $('#bulkProgressBar').css('width', progress + '%').text(progress + '%');
        $('#bulkCurrentItem').html(`Processing: <strong>${currentBulkPart.partNumber}</strong> (${currentBulkIndex + 1} of ${bulkQueue.length})`);

        if (currentBulkPart.groupCheck === '1') {
            checkBulkGroupType(currentBulkPart);
        } else {
            bulkOrderData = currentBulkPart.parts.map(p => ({ groupItemId: p.partId, quantity: p.quantity }));
            if (currentBulkPart.setCheck === '1') { fetchBulkSetItems(); } else { submitBulkOrder(); }
        }
    }

    function checkBulkGroupType(part) {
        $.ajax({
            url:      site_url + '/modules/production_planning/index.php?page=ajax_actions&action=check_group_type',
            type:     'POST',
            dataType: 'json',
            data:     { part_id: part.parts[0].partId },
            success:  function (res) {
                if (res.group_type === 'mandatory' || res.group_type === 'optional') {
                    // Only one group exists — resolve automatically, no picker needed
                    fetchBulkGroupDetails(res.group_id, part);
                } else if (res.group_type === 'multiple') {
                    // 2+ groups, any type mix — always show the picker
                    fetchBulkGroupList(part);
                } else {
                    skipCurrentPart();
                }
            },
            error: function () { skipCurrentPart(); }
        });
    }

    function fetchBulkGroupList(part) {
        $('#bulkProcessModal').modal('hide');
        $('#bulkGroupPartName').text(part.partNumber);
        $.ajax({
            url:  site_url + '/modules/production_planning/index.php?page=ajax_actions&action=fetch_group_items',
            type: 'POST',
            data: { part_id: part.parts[0].partId, group_check: part.groupCheck, efficiency: part.efficiency, order_qty: part.parts[0].quantity },
            success: function (response) {
                $('#bulkGroupItemsContainer').html(response);
                $(document).off('click', '.group-item-row_1').on('click', '.group-item-row_1', function () {
                    $('#bulkGroupModal').modal('hide');
                    fetchBulkGroupDetails($(this).data('group-id'), part);
                });
                $('#bulkGroupModal').modal('show');
            }
        });
    }

function fetchBulkGroupDetails(groupId, part) {
    $('#bulkGroupDetailsPartName').text(part.partNumber);
    $.ajax({
        url:  site_url + '/modules/production_planning/index.php?page=ajax_actions&action=fetch_group_details',
        type: 'POST',
        data: { 
            group_id: groupId, 
            part_id: part.parts[0].partId,   // just the first, for context
            set_check: part.setCheck, 
            efficiency: part.efficiency, 
            OrderQty: part.parts[0].quantity 
        },
        success: function (response) {
            $('#bulkGroupDetailsContainer').html(response);

            // Overwrite each row's qty from the stored per-part values —
            // check the current part's own parts array first, then fall back
            // to the global bulk qty map (covers parts that share a group
            // but were entered on a different row).
            let groupMemberIds = [];
            $('.group-item-row').each(function () {
                let rowPartId = String($(this).data('group-item-id'));
                groupMemberIds.push(rowPartId);
                let match = part.parts.find(p => String(p.partId) === rowPartId);
                if (match) {
                    $(this).find('.group-item-qty').val(match.quantity);
                } else if (window.bulkPartQtyMap && window.bulkPartQtyMap[rowPartId] !== undefined) {
                    $(this).find('.group-item-qty').val(window.bulkPartQtyMap[rowPartId]);
                }
            });

            // Remember every member of this group so processBulkNext() can
            // skip any other queue entry that turns out to be one of these
            // parts (this is what stops a part being ordered a second time
            // as a leftover standalone entry after already going out here).
            $('#bulkGroupDetailsContainer').data('groupMemberIds', groupMemberIds);

            $('#bulkGroupDetailsContainer').find('#submitGroupOrder').remove();
            $('#bulkGroupDetailsContainer').append(
                '<button id="bulkSubmitGroupOrder" class="btn btn-success" data-group-id="' + groupId + '">Continue</button>'
            );
            $('#bulkGroupModal').modal('hide');
            $('#bulkGroupDetailsModal').modal('show');
        }
    });
}

    $(document).on('click', '#bulkSubmitGroupOrder', function () {
        bulkOrderData     = [];
        let bulkGroupId   = $(this).data('group-id') || 0;
        let groupMemberIds = $('#bulkGroupDetailsContainer').data('groupMemberIds') || [];
        $('.group-item-row').each(function () {
            let quantity = parseFloat($(this).find('.group-item-qty').val()) || 0;
            let ratio    = parseFloat($(this).find('.group-item-ratio').val()) || 1;
            if (quantity > 0) bulkOrderData.push({ groupItemId: $(this).data('group-item-id'), quantity: quantity, ratio: ratio });
        });
        // Mark every member of this group as processed now, so subsequent
        // queue entries for the same parts get skipped rather than re-ordered.
        groupMemberIds.forEach(id => processedPartIds.add(String(id)));
        $('#bulkGroupDetailsModal').modal('hide');
        if (currentBulkPart.setCheck === '1') { fetchBulkSetItems(bulkGroupId); } else { submitBulkOrder([], bulkGroupId); }
    });

    function fetchBulkSetItems(groupId = 0) {
        $('#bulkSetPartName').text(currentBulkPart.partNumber);
        $.ajax({
            url:  site_url + '/modules/production_planning/index.php?page=ajax_actions&action=fetch_set_items',
            type: 'POST',
            data: { orderData: JSON.stringify(bulkOrderData) },
            success: function (response) {
                $('#bulkSetItemsContainer').html(response);
                $('#bulkSetItemsContainer').find('#submitSetOrder').remove();
                $('#bulkSetItemsContainer').append('<button id="bulkSubmitSetOrder" class="btn btn-success" data-group-id="' + groupId + '">Continue</button>');
                $('#bulkSetModal').modal('show');
            }
        });
    }

    $(document).on('click', '#bulkSubmitSetOrder', function () {
        let setorderData  = [];
        let bulkSetGroupId = $(this).data('group-id') || 0;
        $('.set-item-row').each(function () {
            if ($(this).find('.set-group-checkbox').is(':checked')) {
                let partIds = $(this).data('part-ids').toString().split(',');
                let setId   = $(this).data('set-id');
                partIds.forEach(partId => { if (partId.trim() !== '') setorderData.push({ setItemId: partId.trim(), setId: setId }); });
            }
        });
        $('#bulkSetModal').modal('hide');
        submitBulkOrder(setorderData, bulkSetGroupId);
    });

    function submitBulkOrder(setorderData = [], groupId = 0) {
        $('#bulkProcessModal').modal('show');
        $.ajax({
            url:  site_url + '/modules/production_planning/index.php?page=ajax_actions&action=work_order_fuction',
            type: 'POST',
            data: { orderData: JSON.stringify(bulkOrderData), setorderData: JSON.stringify(setorderData), group_id: groupId },
            success: function () {
                // Mark every part in this queue entry as processed (covers
                // the non-group / single-part path too, not just groups).
                currentBulkPart.partIds.forEach(id => processedPartIds.add(String(id)));
                currentBulkIndex++; bulkOrderData = null; processBulkNext();
            },
            error:   function () { alert('Error creating order for ' + currentBulkPart.partNumber); skipCurrentPart(); }
        });
    }

    function skipCurrentPart() {
        currentBulkIndex++;
        $('#bulkGroupModal, #bulkGroupDetailsModal, #bulkSetModal').modal('hide');
        $('#bulkProcessModal').modal('show');
        processBulkNext();
    }

    $('#bulkSkipGroup, #bulkSkipGroupDetails, #bulkSkipSet').on('click', function () { skipCurrentPart(); });
});

// ============================================================
// Scroll position save/restore
// ============================================================
function saveScrollPosition() {
    let container = document.querySelector('.table-responsive');
    if (container) localStorage.setItem('scrollPosition', container.scrollTop);
}

function restoreScrollPosition() {
    let scrollPos = localStorage.getItem('scrollPosition');
    let container = document.querySelector('.table-responsive');
    if (scrollPos !== null && container) {
        container.scrollTop = parseInt(scrollPos);
        localStorage.removeItem('scrollPosition');
    }
}

document.addEventListener('DOMContentLoaded', function () {
    setTimeout(restoreScrollPosition, 800);
});

// ============================================================
// Set modal popovers
// ============================================================
$(document).ready(function () {
    $('#setModal').on('shown.bs.modal', function () {
        $('.set-parts-popover').popover({ trigger: 'click', html: true, container: 'body', title: 'Parts in Set', placement: 'right' });
        $('.set-description-popover').popover({ trigger: 'click', html: true, container: 'body', title: 'Description', placement: 'right' });
        $(document).on('click', function (e) {
            $('.set-parts-popover, .set-description-popover').each(function () {
                if (!$(this).is(e.target) && $(this).has(e.target).length === 0 && $('.popover').has(e.target).length === 0) {
                    $(this).popover('hide');
                }
            });
        });
    });
    $('#setModal').on('hidden.bs.modal', function () {
        $('.set-parts-popover, .set-description-popover').popover('dispose');
        $(document).off('click');
    });
});

// ============================================================
// Cell Selection Sum Bar
// ============================================================
(function () {
    const cells    = document.querySelectorAll('.week-cell');
    const sumBar   = document.getElementById('sumBar');
    const sumValue = document.getElementById('sumValue');
    const avgValue = document.getElementById('avgValue');
    const sumCount = document.getElementById('sumCount');
    let selected   = new Set();
    let lastClicked = null;

    function updateBar() {
        if (selected.size === 0) { sumBar.classList.add('hidden'); return; }
        sumBar.classList.remove('hidden');
        let total = 0;
        selected.forEach(c => total += parseInt(c.textContent) || 0);
        sumValue.textContent = total.toLocaleString();
        avgValue.textContent = Math.round(total / selected.size);
        sumCount.textContent = selected.size + ' cell' + (selected.size === 1 ? '' : 's');
    }

    cells.forEach((cell, idx) => {
        cell.addEventListener('click', e => {
            const allCells = Array.from(cells);
            if (e.shiftKey && lastClicked) {
                const a = allCells.indexOf(lastClicked), b = idx;
                for (let i = Math.min(a, b); i <= Math.max(a, b); i++) {
                    allCells[i].classList.add('selected');
                    selected.add(allCells[i]);
                }
            } else if (e.ctrlKey || e.metaKey) {
                if (selected.has(cell)) { selected.delete(cell); cell.classList.remove('selected'); }
                else { selected.add(cell); cell.classList.add('selected'); }
            } else {
                selected.forEach(c => c.classList.remove('selected'));
                selected.clear();
                selected.add(cell);
                cell.classList.add('selected');
            }
            lastClicked = cell;
            updateBar();
        });
    });

    document.getElementById('clearSelBtn').addEventListener('click', () => {
        selected.forEach(c => c.classList.remove('selected'));
        selected.clear();
        lastClicked = null;
        updateBar();
    });

    document.addEventListener('keydown', e => {
        if (e.key === 'Escape') document.getElementById('clearSelBtn').click();
    });
})();
</script>