<?php
/**
 * assypu_scheduler_lib.php
 * =====================================================================
 * SINGLE SOURCE OF TRUTH for Assypu (assembly) scheduling logic — the
 * Assypu equivalent of cnc_scheduler_lib.php / pu_scheduler_lib.php /
 * vac_scheduler_lib.php.
 *
 * ARCHITECTURE NOTE: Assypu has no dual-table concept, no composite
 * machine-id encoding, and no setting-instruction-based setup hours (VAC's
 * distinguishing feature) — its setup hours come from route_details /
 * set_route_details.set_time at insertion time and are never
 * recalculated afterward. Assypu's distinguishing feature is that its
 * required quantity is DERIVED from an upstream stage's (VAC/CNC/PU)
 * actual completed output rather than being a fixed planned number — see
 * getRemainingassypuRequiredQty() in dashboard_assypu.php (reviewed,
 * found correct, left as dashboard-only logic since nothing else needs
 * it).
 *
 * BUG FIXES APPLIED HERE:
 *
 *   1. recalculateMachineDatesforassypu() existed 3 times. The batch
 *      script's and the tablet's copies both correctly filtered
 *      `WHERE finished = 0`. The ADMIN file's copy used
 *      `WHERE end_date >= '$today'` instead — the same stale-filter bug
 *      already found and fixed in CNC's, PU's, and VAC's equivalent
 *      endpoints, except here it lived in the reschedule engine itself
 *      rather than just a priority-renumbering query. Every admin-
 *      triggered reschedule (drag/drop, add break, edit) was walking a
 *      DIFFERENT set of tasks than the batch job and tablet actions —
 *      a finished task with a still-future end_date stayed in the admin
 *      walk (consuming capacity for a done job), while an unfinished
 *      task whose end_date had already slipped into the past was
 *      silently excluded from ever being rescheduled by an admin action
 *      again. Fixed by having exactly one copy, using the already-
 *      correct `finished = 0` filter.
 *
 *   2. handleMachineBreakdowns() (only ever defined in the batch script)
 *      was called from a loop that queried
 *      `WHERE sd.machine_type = 2` — every OTHER query identifying
 *      assypu machines in this same codebase uses `machine_type = 5`.
 *      This looks like a copy-paste leftover from another module. Net
 *      effect: the breakdown-hours accrual loop (which adds elapsed
 *      breakdown time onto affected tasks' total_hours) never actually
 *      ran for assypu's own machines unless a machine_id happened to
 *      collide with a type-2 machine elsewhere. Fixed to
 *      `machine_type = 5`.
 *
 *   3. select_order_for_assypu (in ajax_actions_for_assypu.php) had its
 *      own 4th inline copy of the shift-walking scheduling primitive.
 *      Consolidated into assypu_scheduleNewTaskInline() here.
 *
 *   4. Planned/target UPH computed as quantity / total_hours everywhere
 *      shown (dashboard, get_live_uph_assypu) — same drift problem
 *      already fixed in PU and VAC. assypu_calculateBasicUph() computes
 *      a stable, route-derived number instead.
 *
 *   5. TWO DIFFERENT "actual UPH" algorithms disagreeing with each
 *      other — the exact same bug found in VAC. The dashboard's
 *      page-load render used a correct, session-based calculation
 *      (moved here unchanged as assypu_calculateAndStoreActualUPH()).
 *      get_live_uph_assypu's 30-second poll used a different, broken
 *      one: `round($okUnits / ($windowMinutes / 60), 2)` — since
 *      window_minutes is always 60, that denominator is a hard-coded
 *      constant of 1.0, not real elapsed time. Fixed by calling the one
 *      shared algorithm from both places.
 *
 *   6. Breakdown status: the admin Gantt page's start/stop-breakdown
 *      endpoints wrote only to the shared machine_breakdown table; the
 *      dashboard's breakdown check reads assypu_machine_breakdown_log.
 *      Same two-systems bug already fixed in CNC/PU/VAC.
 *      assypu_isMachineInBreakdown() is now the one check, and the
 *      admin-side endpoints mirror into assypu_machine_breakdown_log.
 *
 *   7. The batch script's OK-quantity lookup (Pass 3) had no
 *      task-instance scoping at all (matching CNC's and PU's original
 *      bug) — summed production across every instance of a
 *      work_order+part combination ever scheduled, not just this one.
 *      Fixed to scope by manuf_id (assypu_machine_table_id in the count
 *      log), consistent with the fixed CNC/PU batch scripts.
 * =====================================================================
 */

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

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

function assypu_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_assypu's single-task insert (which
 * previously had its own, 4th, copy of this exact logic).
 */
function assypu_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 (#1): 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 assypu_recalculateMachineDates($conn, $machineId) {
    set_time_limit(300);

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

    $taskResult = mysqli_query($conn, "
        SELECT id, total_hours AS hours_to_schedule
        FROM assypu_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 = assypu_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 assypu_machine_table SET
                start_date    = '$startStr',
                end_date      = '$endStr',
                shift_ids     = '$shiftId',
                remaing_hours = '$remStr'
            WHERE id = {$task['id']}
        ");

        $cursor = $endStr;
    }
}

function assypu_fullReschedule($conn, $machineId) {
    assypu_recalculateMachineDates($conn, $machineId);
}

/* =====================================================================
 * BREAKDOWN HOUR ACCRUAL
 * ---------------------------------------------------------------------
 * Moved here from the batch script (its only prior home). Logic itself
 * is unchanged — the bug was in the CALLER's machine_type filter (see
 * fix #2 in the file header), not in this function.
 * ===================================================================== */

function assypu_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 assypu_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 assypu_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()).
 * ===================================================================== */

function assypu_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 algorithm, used everywhere
 * ---------------------------------------------------------------------
 * FIX (#5): this is dashboard_assypu.php's original
 * calculateAndStoreActualUPH(), moved here unchanged in its logic — it
 * was already correct. The bug was that get_live_uph_assypu had a
 * SECOND, different, broken implementation. Now there is only this one,
 * called from both places.
 * ===================================================================== */

function assypu_calculateAndStoreActualUPH($conn, $workOrderId, $partId, $machineId, $assypuMachineTableId) {
    $workOrderId          = intval($workOrderId);
    $partId               = intval($partId);
    $assypuMachineTableId = intval($assypuMachineTableId);

    $countSql = "
        SELECT SUM(quantity) AS total
        FROM assypu_part_count_log
        WHERE work_order_id = $workOrderId
          AND assypu_machine_table_id = $assypuMachineTableId
          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 assypu_machine_activity_log
                    WHERE work_order_id = $workOrderId
                    AND assypu_machine_table_id = $assypuMachineTableId
                    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 assypu_machine_activity_log
                WHERE work_order_id = $workOrderId
                AND assypu_machine_table_id = $assypuMachineTableId
                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 assypu_part_count_log
                    WHERE work_order_id = $workOrderId
                    AND assypu_machine_table_id = $assypuMachineTableId
                    AND part_id = $partId
                    LIMIT 1";
    $checkResult = mysqli_query($conn, $checkSql);
    if ($checkResult && mysqli_num_rows($checkResult) > 0) {
        mysqli_query($conn, "UPDATE assypu_part_count_log
                         SET uph = $actualUph
                         WHERE work_order_id = $workOrderId
                         AND assypu_machine_table_id = $assypuMachineTableId
                         AND part_id = $partId");
    }

    return $actualUph;
}

/* =====================================================================
 * BREAKDOWN STATUS — single source of truth
 * ===================================================================== */

function assypu_isMachineInBreakdown($conn, $machineId) {
    $machineId = intval($machineId);
    $res = mysqli_query($conn, "
        SELECT status FROM assypu_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;
}

/* =====================================================================
 * SPLIT WORK ORDER
 * ---------------------------------------------------------------------
 * NEW: Assypu previously had no manual split feature at all — only the
 * breakdown-triggered auto-split inside log_breakdown_assypu (which
 * remains untouched). This adds the same "Split Work Order" capability
 * CNC, PU, and VAC's admin Gantt pages already have, using the same
 * approach as VAC's split_vac_work_order (the closest analogous
 * schema — a manuf_id link to manufacture_machine_table, setup_hours
 * kept flat and applied to both halves, quantity/hours split
 * proportionally by hours-per-unit).
 *
 * Splits BOTH the linked manufacture_machine_table row (so downstream
 * consumers — including getRemainingassypuRequiredQty()'s upstream-
 * stage lookups — see the correct split quantities) and the
 * assypu_machine_table row itself. Neither half is marked finished by
 * this function; that's deliberately left to normal production
 * (finishing the original, or a future breakdown on either half).
 *
 * Returns ['success'=>true, ...details] or ['success'=>false,'message'=>...].
 * Caller is responsible for calling assypu_fullReschedule() afterward —
 * this function does not reschedule itself, to match the pattern every
 * other write path in this library follows (mutate, then let the caller
 * decide when to reschedule).
 * ===================================================================== */

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

    $result = mysqli_query($conn, "SELECT * FROM assypu_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'];
    }

    $originalTotalHoursWithoutSetup = $totalHours - $setupHours;
    $hoursPerUnit = $originalQuantity > 0 ? ($originalTotalHoursWithoutSetup / $originalQuantity) : 0;

    $remainingQuantity = $originalQuantity - $splitQuantity;
    $remainingHours = ($remainingQuantity * $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 assypu_machine_table SET
                quantity            = $remainingQuantity,
                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 assypu_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 assypu_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'  => $remainingQuantity,
            '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()];
    }
}

function assypu_markBreakdownAffectedWorkOrders($conn, $machineId, $breakdownStart) {
    $machineId = intval($machineId);
    $startEsc  = mysqli_real_escape_string($conn, $breakdownStart);

    $res = mysqli_query($conn, "
        SELECT id FROM assypu_machine_table
        WHERE machine_id = $machineId
          AND is_break = 0
          AND start_date < '$startEsc'
          AND end_date   > '$startEsc'
    ");
    $count = 0;
    while ($row = mysqli_fetch_assoc($res)) {
        mysqli_query($conn, "UPDATE assypu_machine_table SET is_breakdown_affected = 1 WHERE id = {$row['id']}");
        $count++;
    }
    return $count;
}
?>