<?php
/**
 * assbly_scheduler_lib.php
 * =====================================================================
 * SINGLE SOURCE OF TRUTH for Assembly (assbly) scheduling logic — the
 * assbly equivalent of cnc_scheduler_lib.php / pu_scheduler_lib.php /
 * vac_scheduler_lib.php / assypu_scheduler_lib.php.
 *
 * BEFORE THIS FILE, the assbly module had the same family of bugs those
 * other three modules had already been through a fix pass for:
 *
 *   1. recalculateMachineDatesforassbly() existed 3 times (batch script,
 *      tablet AJAX file, admin AJAX file). The batch script's and
 *      tablet's copies both correctly filtered `WHERE finished = 0`.
 *      The ADMIN file's copy (ajax_for_assmbly.php) used
 *      `WHERE end_date >= '$currentDateStr'` instead — the same
 *      stale-filter bug already found and fixed in CNC's/PU's/VAC's/
 *      Assypu's equivalent code, except here it lived in the reschedule
 *      engine itself, not just a priority-renumbering query. Every
 *      admin-triggered reschedule (drag/drop, priority edit) was
 *      walking a DIFFERENT set of tasks than the batch job and tablet
 *      actions. Fixed by having exactly one copy, using the
 *      already-correct `finished = 0` filter.
 *
 *   2. select_order_for_assmbly (in ajax_for_assmbly.php) had its own
 *      3rd inline copy of the shift-walking scheduling primitive.
 *      Consolidated into assbly_scheduleNewTaskInline() here.
 *
 *   3. Two different breakdown-status sources: the admin Gantt page's
 *      start/stop-breakdown endpoints wrote only to the shared
 *      `machine_breakdown` table; the dashboard's breakdown banner
 *      reads `assbly_machine_breakdown_log`. Same two-systems bug
 *      already fixed in CNC/PU/VAC/Assypu. assbly_isMachineInBreakdown()
 *      is now the one check, and the admin-side endpoints mirror into
 *      assbly_machine_breakdown_log.
 *
 *   4. Planned/target UPH computed as quantity / total_hours in both
 *      the dashboard and the live-UPH poll — same drift problem already
 *      fixed in PU/VAC/Assypu. assbly_calculateBasicUph() computes a
 *      stable, route-derived number instead (operation_time /
 *      parts_per_operation from route_details / set_route_details).
 *
 *   5. The dashboard's calculateAndStoreActualUPH() was already CORRECT
 *      (session-based: first start to now/last finish, minus
 *      pause/break/breakdown gaps from the real activity log) but lived
 *      only in the dashboard file. Moved here unchanged as
 *      assbly_calculateAndStoreActualUPH() so any future caller shares
 *      the one algorithm instead of growing a second copy.
 *
 *   6. markBreakdownAffectedWorkOrders() and the split-work-order logic
 *      lived only in ajax_for_assmbly.php. Moved here as
 *      assbly_markBreakdownAffectedWorkOrders() / assbly_splitTask() so
 *      any other entry point (e.g. a future tablet-side breakdown flow)
 *      can reuse them instead of re-implementing.
 * =====================================================================
 */

if (defined('ASSBLY_SCHEDULER_LIB_LOADED')) {
    return;
}
define('ASSBLY_SCHEDULER_LIB_LOADED', true);

/* =====================================================================
 * SHIFT / CALENDAR PRIMITIVES
 * ===================================================================== */

function assbly_getShiftsByDay($conn) {
    $shiftsByDay = [];
    $res = mysqli_query($conn, "SELECT * FROM working_hours");
    while ($row = mysqli_fetch_assoc($res)) {
        $shiftsByDay[(int)$row['day_shifts']][] = $row;
    }
    return $shiftsByDay;
}

/**
 * Walk shifts from $startFrom (or "today 00:00" if not given) allocating
 * $hoursToSchedule hours of work. Used both by the per-machine recalc
 * loop below and by select_order_for_assmbly's single-task insert
 * (which previously had its own, 3rd, copy of this exact logic).
 */
function assbly_scheduleNewTaskInline($conn, $hoursToSchedule, $shiftsByDay, $startFrom = null) {
    $currentDateTime = $startFrom !== null
        ? strtotime($startFrom)
        : strtotime(date('Y-m-d') . ' 00:00:00');

    $dateHoursMap   = [];
    $taskStartTime  = null;
    $taskEndTime    = null;
    $firstShiftUsed = null;
    $hoursRemaining = $hoursToSchedule;

    if ($hoursRemaining <= 0) {
        return [
            'start_date'      => date('Y-m-d H:i:s', $currentDateTime),
            'end_date'        => date('Y-m-d H:i:s', $currentDateTime),
            'shift_ids'       => null,
            'remaining_hours' => '',
        ];
    }

    while ($hoursRemaining > 0) {
        $currentDate = date('Y-m-d', $currentDateTime);
        $dayOfWeek   = (int) date('w', $currentDateTime);

        if (!isset($shiftsByDay[$dayOfWeek])) {
            $currentDateTime = strtotime($currentDate . ' +1 day');
            continue;
        }

        foreach ($shiftsByDay[$dayOfWeek] as $shift) {
            $shiftStart = strtotime($currentDate . ' ' . $shift['start_time']);
            $shiftEnd   = strtotime($currentDate . ' ' . $shift['end_time']);
            if ($shiftEnd <= $shiftStart) $shiftEnd = strtotime('+1 day', $shiftEnd);
            $shiftName  = $shift['shift_name'];

            if ($shiftEnd <= $currentDateTime) continue;

            $actualStart = max($currentDateTime, $shiftStart);
            $availableHours = max(0, $shiftEnd - $actualStart) / 3600;
            if ($availableHours <= 0) continue;

            $allocatable = min($hoursRemaining, $availableHours);

            if (!$taskStartTime) {
                $taskStartTime  = $actualStart;
                $firstShiftUsed = $shiftName;
            }

            $taskEndTime = $actualStart + ($allocatable * 3600);
            $hoursRemaining -= $allocatable;

            $dateOnly = date('Y-m-d', $actualStart);
            $dateHoursMap[$dateOnly] = ($dateHoursMap[$dateOnly] ?? 0) + $allocatable;

            $currentDateTime = $taskEndTime;
            if ($hoursRemaining <= 0) break;
        }

        if ($hoursRemaining > 0) {
            $currentDateTime = strtotime(date('Y-m-d', $currentDateTime) . ' +1 day 00:00:00');
        }
    }

    $remainingHoursStr = '';
    foreach ($dateHoursMap as $date => $hrs) {
        $remainingHoursStr .= $date . '=' . round($hrs, 2) . ',';
    }
    $remainingHoursStr = rtrim($remainingHoursStr, ',');

    return [
        'start_date'      => date('Y-m-d H:i:s', $taskStartTime),
        'end_date'        => date('Y-m-d H:i:s', $taskEndTime),
        'shift_ids'       => $firstShiftUsed,
        'remaining_hours' => $remainingHoursStr,
    ];
}

/* =====================================================================
 * PER-MACHINE RESCHEDULE — the single canonical version
 * ---------------------------------------------------------------------
 * FIX: filters WHERE finished = 0, matching the batch script's and
 * tablet's already-correct copies — not WHERE end_date >= today, which
 * the admin file's copy used and which excluded/included the wrong set
 * of tasks depending on staleness.
 * ===================================================================== */

function assbly_recalculateMachineDates($conn, $machineId) {
    set_time_limit(300);

    $machineId = intval($machineId);
    $shiftsByDay = assbly_getShiftsByDay($conn);

    $taskResult = mysqli_query($conn, "
        SELECT id, total_hours AS hours_to_schedule
        FROM assbly_machine_table
        WHERE machine_id = $machineId
          AND finished   = 0
        ORDER BY priority
    ");

    $tasks = [];
    while ($row = mysqli_fetch_assoc($taskResult)) $tasks[] = $row;

    $cursor = null;
    foreach ($tasks as $task) {
        $hours = floatval($task['hours_to_schedule']);
        $sched = assbly_scheduleNewTaskInline($conn, $hours, $shiftsByDay, $cursor);

        $startStr = $sched['start_date'];
        $endStr   = $sched['end_date'];
        $shiftId  = mysqli_real_escape_string($conn, (string) $sched['shift_ids']);
        $remStr   = mysqli_real_escape_string($conn, $sched['remaining_hours']);

        mysqli_query($conn, "
            UPDATE assbly_machine_table SET
                start_date    = '$startStr',
                end_date      = '$endStr',
                shift_ids     = '$shiftId',
                remaing_hours = '$remStr'
            WHERE id = {$task['id']}
        ");

        $cursor = $endStr;
    }
}

function assbly_fullReschedule($conn, $machineId) {
    assbly_recalculateMachineDates($conn, $machineId);
}

/* =====================================================================
 * BREAKDOWN HOUR ACCRUAL
 * ---------------------------------------------------------------------
 * Moved here from the batch script (its only prior home). Logic itself
 * is unchanged. Note: unlike Assypu (which had a machine_type=2/5
 * copy-paste mismatch), assbly's callers already correctly filter
 * station_detail_table on machine_type = 2, so there is no equivalent
 * caller-side bug here — this function is just centralized.
 * ===================================================================== */

function assbly_handleMachineBreakdowns($conn) {

    $currentTime = date('Y-m-d H:i:s');
    $currentTs   = strtotime($currentTime);
    $todayStart  = date('Y-m-d 00:00:00');
    $todayTs     = strtotime($todayStart);

    $breakdownQuery = "
        SELECT mb.*, sdt.resource_code
        FROM machine_breakdown mb
        JOIN station_detail_table sdt
            ON mb.machine_id = sdt.id
        WHERE mb.breakdown_start <= '$currentTime'
          AND mb.is_active = 1
    ";

    $breakdownResult = mysqli_query($conn, $breakdownQuery);
    if (!$breakdownResult) return;

    while ($breakdown = mysqli_fetch_assoc($breakdownResult)) {

        $machineId      = (int) $breakdown['machine_id'];
        $breakdownId    = (int) $breakdown['id'];
        $breakdownStart = $breakdown['breakdown_start'];
        $breakdownStartTs = strtotime($breakdownStart);

        $breakdownHours = ($breakdownStartTs >= $todayTs)
            ? max(0, ($currentTs - $breakdownStartTs) / 3600)
            : max(0, ($currentTs - $todayTs) / 3600);

        mysqli_query($conn, "
            UPDATE machine_breakdown
            SET breakdown_hours = $breakdownHours, updated_at = '$currentTime'
            WHERE id = $breakdownId
        ");

        $affectedTasksQuery = "
            SELECT id, total_hours, setup_hours, total_hours_update, breakdown_hours
            FROM assbly_machine_table
            WHERE machine_id = $machineId
              AND is_breakdown_affected = 1
              AND is_break = 0
        ";
        $tasksResult = mysqli_query($conn, $affectedTasksQuery);

        if ($tasksResult && mysqli_num_rows($tasksResult) > 0) {
            while ($task = mysqli_fetch_assoc($tasksResult)) {
                $taskId          = (int) $task['id'];
                $prevSetupHours  = (float) $task['setup_hours'];
                $prevUpdateHours = (float) $task['total_hours_update'];
                $newTotalHours   = $prevSetupHours + $prevUpdateHours + $breakdownHours;

                mysqli_query($conn, "
                    UPDATE assbly_machine_table
                    SET total_hours = $newTotalHours, breakdown_hours = $breakdownHours
                    WHERE id = $taskId
                ");
            }
        }
    }
}

/* =====================================================================
 * BASIC UPH — route-derived, quantity-independent (same principle as
 * pu_calculateBasicUph() / vac_calculateBasicUph() / assypu_calculateBasicUph()).
 * ---------------------------------------------------------------------
 * FIX: replaces the quantity / total_hours calculation that both the
 * dashboard and the live-UPH poll were using for "planned/target UPH" —
 * that number drifts every time total_hours is recalculated by a
 * breakdown split or reschedule, since quantity stays fixed but hours
 * change. Route-derived UPH is stable regardless of scheduling state.
 * ===================================================================== */

function assbly_calculateBasicUph($conn, $partId, $setId = 0) {
    $partId = intval($partId);
    $setId  = intval($setId);

    if ($setId > 0) {
        $query = "SELECT operation_time, parts_per_operation FROM set_route_details WHERE set_id = $setId LIMIT 1";
    } else {
        $query = "SELECT operation_time, parts_per_operation FROM route_details WHERE item_id = $partId LIMIT 1";
    }
    $result = mysqli_query($conn, $query);
    if (!$result || mysqli_num_rows($result) == 0) return 0.0;

    $row = mysqli_fetch_assoc($result);

    if (!empty($row['operation_time'])) {
        [$h, $m, $s] = explode(':', $row['operation_time']);
        $operationTimeSeconds = ($h * 3600) + ($m * 60) + $s;
    } else {
        $operationTimeSeconds = 0;
    }

    $partsPerOperation = (!empty($row['parts_per_operation']) && $row['parts_per_operation'] > 0)
        ? (float) $row['parts_per_operation'] : 1.0;

    if ($operationTimeSeconds <= 0) return 0.0;

    $cycleSecondsPerPart = $operationTimeSeconds / $partsPerOperation;
    if ($cycleSecondsPerPart <= 0) return 0.0;

    return round(3600 / $cycleSecondsPerPart, 2);
}

/* =====================================================================
 * ACTUAL (LIVE) UPH — the ONE correct session-based algorithm
 * ---------------------------------------------------------------------
 * Moved here unchanged from dashboard_assbly.php's original
 * calculateAndStoreActualUPH() — it was already correct. Centralizing
 * it means any future caller (e.g. a live-poll endpoint) reuses this
 * instead of growing a second, possibly-diverging implementation.
 * ===================================================================== */

function assbly_calculateAndStoreActualUPH($conn, $workOrderId, $partId, $machineId, $assblyMachineTableId) {
    $workOrderId          = intval($workOrderId);
    $partId               = intval($partId);
    $assblyMachineTableId = intval($assblyMachineTableId);

    $countSql = "
        SELECT SUM(quantity) AS total
        FROM assbly_part_count_log
        WHERE work_order_id = $workOrderId
          AND assbly_machine_table_id = $assblyMachineTableId
          AND part_id = $partId
          AND type = 'ok'
    ";
    $countResult = mysqli_query($conn, $countSql);
    $countRow    = mysqli_fetch_assoc($countResult);
    $totalMade   = floatval($countRow['total'] ?? 0);

    $activitySql = "SELECT
                    MIN(CASE WHEN action_type = 'start' THEN created_at END) as first_start,
                    MAX(CASE WHEN action_type = 'finish' THEN created_at END) as last_finish
                    FROM assbly_machine_activity_log
                    WHERE work_order_id = $workOrderId
                    AND assbly_machine_table_id = $assblyMachineTableId
                    AND part_id = $partId";
    $activityRes = mysqli_query($conn, $activitySql);
    $actualUph   = 0;

    if ($activityRes && mysqli_num_rows($activityRes) > 0) {
        $activityRow = mysqli_fetch_assoc($activityRes);
        $firstStart  = $activityRow['first_start'];
        $lastFinish  = $activityRow['last_finish'];

        if ($firstStart) {
            $startTime    = strtotime($firstStart);
            $totalSeconds = $lastFinish ? (strtotime($lastFinish) - $startTime) : (time() - $startTime);

            $nonProductiveSql = "
                SELECT action_type, created_at
                FROM assbly_machine_activity_log
                WHERE work_order_id = $workOrderId
                AND assbly_machine_table_id = $assblyMachineTableId
                AND part_id = $partId
                AND action_type IN ('start', 'pause', 'break', 'breakdown')
                ORDER BY created_at ASC
            ";
            $nonProdRes           = mysqli_query($conn, $nonProductiveSql);
            $nonProductiveSeconds = 0;
            $lastPauseStart       = null;

            if ($nonProdRes && mysqli_num_rows($nonProdRes) > 0) {
                while ($row = mysqli_fetch_assoc($nonProdRes)) {
                    $actionType = $row['action_type'];
                    $timestamp  = strtotime($row['created_at']);
                    if (in_array($actionType, ['pause', 'break', 'breakdown'])) {
                        $lastPauseStart = $timestamp;
                    } else if ($actionType === 'start' && $lastPauseStart !== null) {
                        $nonProductiveSeconds += $timestamp - $lastPauseStart;
                        $lastPauseStart = null;
                    }
                }
                if ($lastPauseStart !== null) {
                    $endPoint = $lastFinish ? strtotime($lastFinish) : time();
                    $nonProductiveSeconds += $endPoint - $lastPauseStart;
                }
            }

            $productiveSeconds = max(0, $totalSeconds - $nonProductiveSeconds);
            $totalHours        = $productiveSeconds / 3600;
            $actualUph         = ($totalHours > 0) ? round($totalMade / $totalHours, 2) : 0;
        }
    }

    $checkSql    = "SELECT id FROM assbly_part_count_log
                    WHERE work_order_id = $workOrderId
                    AND assbly_machine_table_id = $assblyMachineTableId
                    AND part_id = $partId
                    LIMIT 1";
    $checkResult = mysqli_query($conn, $checkSql);
    if ($checkResult && mysqli_num_rows($checkResult) > 0) {
        mysqli_query($conn, "UPDATE assbly_part_count_log
                         SET uph = $actualUph
                         WHERE work_order_id = $workOrderId
                         AND assbly_machine_table_id = $assblyMachineTableId
                         AND part_id = $partId");
    }

    return $actualUph;
}

/* =====================================================================
 * BREAKDOWN STATUS — single source of truth
 * ---------------------------------------------------------------------
 * FIX: the admin Gantt's start/stop-breakdown endpoints previously wrote
 * only to the generic `machine_breakdown` table. The dashboard's
 * breakdown banner reads `assbly_machine_breakdown_log`. A breakdown
 * started from the admin side was invisible on the tablet. Both admin
 * endpoints now mirror into assbly_machine_breakdown_log, and this is
 * the one function anything should call to check current status.
 * ===================================================================== */

function assbly_isMachineInBreakdown($conn, $machineId) {
    $machineId = intval($machineId);
    $res = mysqli_query($conn, "
        SELECT status FROM assbly_machine_breakdown_log
        WHERE machine_id = $machineId
        ORDER BY created_at DESC, id DESC
        LIMIT 1
    ");
    if ($res && mysqli_num_rows($res) > 0) {
        return mysqli_fetch_assoc($res)['status'] === 'active';
    }
    return false;
}

/* =====================================================================
 * MARK BREAKDOWN-AFFECTED WORK ORDERS
 * ---------------------------------------------------------------------
 * Moved here unchanged from ajax_for_assmbly.php's local copy.
 * ===================================================================== */

function assbly_markBreakdownAffectedWorkOrders($conn, $machineId, $breakdownStart, $breakdownEnd = null) {
    $machineId = intval($machineId);
    $startEsc  = mysqli_real_escape_string($conn, $breakdownStart);

    $overlapQuery = "
        SELECT id, start_date, end_date
        FROM assbly_machine_table
        WHERE machine_id = $machineId
          AND is_break = 0
          AND start_date < '$startEsc'
          AND end_date   > '$startEsc'
    ";

    $overlapResult = mysqli_query($conn, $overlapQuery);
    $affectedCount = 0;

    while ($row = mysqli_fetch_assoc($overlapResult)) {
        $taskId = (int) $row['id'];
        mysqli_query($conn, "UPDATE assbly_machine_table SET is_breakdown_affected = 1 WHERE id = $taskId");
        $affectedCount++;
    }

    return $affectedCount;
}

/* =====================================================================
 * SPLIT WORK ORDER
 * ---------------------------------------------------------------------
 * Moved here unchanged (logic-wise) from ajax_for_assmbly.php's
 * split_assmbly_work_order handler, generalized into a reusable
 * function so the AJAX endpoint becomes a thin wrapper. Caller is
 * responsible for calling assbly_fullReschedule() afterward — this
 * function does not reschedule itself, matching the pattern every
 * other write path in this library follows (mutate, then let the
 * caller decide when to reschedule).
 * ===================================================================== */

function assbly_splitTask($conn, $taskId, $splitQuantity) {
    $taskId        = intval($taskId);
    $splitQuantity = floatval($splitQuantity);

    $result = mysqli_query($conn, "SELECT * FROM assbly_machine_table WHERE id = $taskId");
    if (!$result || mysqli_num_rows($result) == 0) {
        return ['success' => false, 'message' => 'Work order not found'];
    }
    $originalWO = mysqli_fetch_assoc($result);

    if (intval($originalWO['is_break']) === 1) {
        return ['success' => false, 'message' => 'Cannot split a break task'];
    }

    $originalQuantity = floatval($originalWO['quantity']);
    $machineId   = intval($originalWO['machine_id']);
    $workOrderId = intval($originalWO['work_order_id']);
    $partId      = intval($originalWO['part_id']);
    $setupHours  = floatval($originalWO['setup_hours']);
    $totalHours  = floatval($originalWO['total_hours']);
    $priority    = intval($originalWO['priority']);
    $manufId     = intval($originalWO['manuf_id']);

    if ($splitQuantity >= $originalQuantity) {
        return ['success' => false, 'message' => 'Split quantity must be less than current quantity (' . $originalQuantity . ')'];
    }
    if ($splitQuantity < 1) {
        return ['success' => false, 'message' => 'Split quantity must be at least 1'];
    }

    $hoursPerUnit   = $originalQuantity > 0 ? (($totalHours - $setupHours) / $originalQuantity) : 0;
    $remainingQty   = $originalQuantity - $splitQuantity;
    $remainingHours = ($remainingQty * $hoursPerUnit) + $setupHours;
    $splitHours     = ($splitQuantity * $hoursPerUnit) + $setupHours;

    mysqli_autocommit($conn, false);

    try {
        $newManufId = null;

        if ($manufId > 0) {
            $manufResult = mysqli_query($conn, "SELECT * FROM manufacture_machine_table WHERE id = $manufId");
            if (!$manufResult || mysqli_num_rows($manufResult) == 0) {
                throw new Exception('Linked manufacture record not found for manuf_id ' . $manufId);
            }
            $originalManuf = mysqli_fetch_assoc($manufResult);
            $manufOriginalQuantity = floatval($originalManuf['quantity']);
            $manufTotalHours       = floatval($originalManuf['total_hours']);
            $manufHoursPerUnit     = $manufOriginalQuantity > 0 ? ($manufTotalHours / $manufOriginalQuantity) : 0;

            $manufRemainingQuantity = $manufOriginalQuantity - $splitQuantity;
            $manufRemainingHours    = $manufRemainingQuantity * $manufHoursPerUnit;
            $manufSplitHours        = $splitQuantity * $manufHoursPerUnit;

            if (!mysqli_query($conn, "UPDATE manufacture_machine_table SET quantity = $manufRemainingQuantity, total_hours = $manufRemainingHours WHERE id = $manufId")) {
                throw new Exception('Failed to update original manufacture record: ' . mysqli_error($conn));
            }

            $manufColumns = []; $manufValues = [];
            foreach ($originalManuf as $col => $val) {
                if ($col === 'id') continue;
                $manufColumns[] = "`$col`";
                if ($col === 'quantity')        $manufValues[] = $splitQuantity;
                elseif ($col === 'total_hours') $manufValues[] = $manufSplitHours;
                elseif (is_null($val))          $manufValues[] = 'NULL';
                else $manufValues[] = "'" . mysqli_real_escape_string($conn, $val) . "'";
            }
            $insertManufQuery = "INSERT INTO manufacture_machine_table (" . implode(', ', $manufColumns) . ") VALUES (" . implode(', ', $manufValues) . ")";
            if (!mysqli_query($conn, $insertManufQuery)) {
                throw new Exception('Failed to create new manufacture record: ' . mysqli_error($conn));
            }
            $newManufId = $conn->insert_id;
        }

        if (!mysqli_query($conn, "
            UPDATE assbly_machine_table SET
                quantity            = $remainingQty,
                total_hours         = $remainingHours,
                total_hours_update  = " . ($remainingHours - $setupHours) . "
            WHERE id = $taskId
        ")) {
            throw new Exception('Failed to update original work order: ' . mysqli_error($conn));
        }

        $newPriority = $priority + 1;
        if (!mysqli_query($conn, "
            UPDATE assbly_machine_table SET priority = priority + 1
            WHERE machine_id = $machineId AND priority >= $newPriority AND id != $taskId
        ")) {
            throw new Exception('Failed to update priorities: ' . mysqli_error($conn));
        }

        $manufIdForInsert = $newManufId !== null ? $newManufId : ($manufId > 0 ? $manufId : null);

        $insertQuery = "INSERT INTO assbly_machine_table (
                           machine_id, manuf_id, work_order_id, part_id, quantity, product_name,
                           total_hours, total_hours_update, setup_hours, priority, set_id, cycle,
                           shift_ids, start_date, end_date, is_break, finished
                       ) VALUES (
                           $machineId,
                           " . ($manufIdForInsert !== null ? intval($manufIdForInsert) : 'NULL') . ",
                           $workOrderId, $partId, $splitQuantity,
                           '" . mysqli_real_escape_string($conn, $originalWO['product_name']) . "',
                           $splitHours, " . ($splitHours - $setupHours) . ", $setupHours,
                           $newPriority, " . intval($originalWO['set_id']) . ", " . intval($originalWO['cycle']) . ",
                           '" . mysqli_real_escape_string($conn, $originalWO['shift_ids']) . "',
                           '" . mysqli_real_escape_string($conn, $originalWO['start_date']) . "',
                           '" . mysqli_real_escape_string($conn, $originalWO['end_date']) . "',
                           0, 0
                       )";
        if (!mysqli_query($conn, $insertQuery)) {
            throw new Exception('Failed to create new work order: ' . mysqli_error($conn));
        }

        mysqli_commit($conn);
        mysqli_autocommit($conn, true);

        return [
            'success'            => true,
            'machine_id'         => $machineId,
            'original_quantity'  => $remainingQty,
            'split_quantity'     => $splitQuantity,
            'original_hours'     => round($remainingHours, 2),
            'split_hours'        => round($splitHours, 2),
            'new_manuf_id'       => $newManufId,
        ];

    } catch (Exception $e) {
        mysqli_rollback($conn);
        mysqli_autocommit($conn, true);
        return ['success' => false, 'message' => $e->getMessage()];
    }
}
?>