<?php
/**
 * ajax_actions_assypu.php  (shop-floor tablet AJAX endpoints)
 * =====================================================================
 * CHANGES FROM ORIGINAL:
 *
 *   1. recalculateMachineDatesforassypu() removed — this file's copy was
 *      already correct (`finished = 0` filter), but is now the single
 *      shared assypu_recalculateMachineDates() in
 *      assypu_scheduler_lib.php instead of a standalone copy.
 *
 *   2. get_live_uph_assypu: FIX — the ROOT CAUSE of the "Actual UPH
 *      disagreeing with itself" bug, identical to VAC's. This endpoint
 *      had its own, DIFFERENT "live UPH" calculation from the one
 *      dashboard_assypu.php's page-load render used:
 *          $liveUph = round($okUnits / ($windowMinutes / 60), 2);
 *      Since window_minutes is always sent as 60, that denominator is a
 *      hard-coded constant of exactly 1.0 — not real elapsed time. This
 *      poll (every 30 seconds) was overwriting the CORRECT page-load
 *      value (computed by the session-based algorithm now shared as
 *      assypu_calculateAndStoreActualUPH()) with a different, wrong
 *      number, over and over. Fixed by calling the exact same shared
 *      function here. Planned UPH also switched to the route-based
 *      assypu_calculateBasicUph() (was quantity/total_hours, same drift
 *      problem already fixed in PU and VAC).
 *
 *   3. end_breakdown_assypu: FIX — previously only inserted a
 *      'completed' log row. Never cleared is_breakdown_affected, never
 *      triggered any reschedule — the same bug already found and fixed
 *      in CNC's, PU's, and VAC's equivalent endpoints. Now does both.
 *
 *   4. log_breakdown_assypu: FIX — `SELECT code FROM keycode WHERE id =
 *      ...` queried a table named `keycode`; every other breakdown-
 *      reason lookup in this codebase (CNC, PU, VAC, and this same
 *      module's own admin-side start_machine_breakdown) queries
 *      `keycode_table`. If `keycode` isn't a real table in your schema,
 *      this silently failed on every call and every Assypu breakdown got
 *      logged with reason "Unknown" regardless of what was selected.
 *      Changed to `keycode_table` to match the established convention —
 *      **please confirm this against your schema before deploying**,
 *      since I can't verify table names directly (same caveat as the
 *      identical VAC fix).
 *
 *   5. All recalculateMachineDatesforassypu() calls now call the shared
 *      assypu_fullReschedule() instead.
 *
 *   6. Inventory/BOM functions, log_count_assypu, changeover, first-off,
 *      get_current_task_priority_assypu (already correctly scoped by
 *      machine_id) — no bugs identified, unchanged.
 * =====================================================================
 */

include("../../../include/startup.php");
require_once __DIR__ . '/assypu_scheduler_lib.php';

if (isset($_REQUEST['action'])) {
    while (ob_get_level()) ob_end_clean();
    header('Content-Type: application/json');
    header('Cache-Control: no-cache, must-revalidate');
}

/* =====================================================================
 * Inventory / BOM helpers — unchanged from original, no scheduling bugs
 * identified in this block.
 * ===================================================================== */

function reduceBomChildInventory($conn, $mainProductId, $countQty, $workOrderId, $setId = 0) {
    if ($setId > 0) {
        $setResult = mysqli_query($conn, "SELECT part_id FROM set_mangmnt_mapping WHERE set_id=$setId");
        if (!$setResult || mysqli_num_rows($setResult) == 0) return;
        $childTotals = [];
        while ($setRow = mysqli_fetch_assoc($setResult)) {
            $bomResult = mysqli_query($conn, "SELECT sub_part_id,quantity_required FROM bom WHERE main_product_id={$setRow['part_id']}");
            if (!$bomResult) continue;
            while ($bomRow = mysqli_fetch_assoc($bomResult))
                $childTotals[$bomRow['sub_part_id']] = ($childTotals[$bomRow['sub_part_id']] ?? 0) + floatval($bomRow['quantity_required']);
        }
        foreach ($childTotals as $childId => $sumQty) {
            $ceiledDeduct = $countQty >= 0 ? ceil($sumQty * $countQty) : -ceil(abs($sumQty * $countQty));
            updateInventoryStock($conn, $childId, -$ceiledDeduct, $workOrderId, $setId);
            reduceWorkOrderRawMaterial($conn, $childId, $ceiledDeduct, $workOrderId, $setId);
        }
    } else {
        $mainProductId = intval($mainProductId);
        $bomResult     = mysqli_query($conn, "SELECT sub_part_id,quantity_required FROM bom WHERE main_product_id=$mainProductId");
        if (!$bomResult) return;
        while ($bomRow = mysqli_fetch_assoc($bomResult)) {
            $subPartId   = intval($bomRow['sub_part_id']);
            $qtyRequired = floatval($bomRow['quantity_required']);
            $deductQty = -1 * $qtyRequired * $countQty;
            updateInventoryStock($conn, $subPartId, $deductQty, $workOrderId, $setId);
            reduceWorkOrderRawMaterial($conn, $subPartId, -$deductQty, $workOrderId, $setId);
        }
    }
}

function updateInventoryStock($conn, $itemId, $qtyChange, $workOrderId, $set_id) {
    $itemId    = intval($itemId);
    $qtyChange = floatval($qtyChange);
    $result    = mysqli_query($conn, "SELECT quantity_in_stock FROM inventory WHERE item_id=$itemId");
    if ($result && mysqli_num_rows($result) > 0) {
        if (!mysqli_query($conn, "UPDATE inventory SET quantity_in_stock=quantity_in_stock+($qtyChange) WHERE item_id=$itemId")) {
            echo json_encode(["success"=>false,"message"=>"Inventory update failed: ".mysqli_error($conn)]); exit;
        }
    } else {
        if ($qtyChange < 0) { echo json_encode(["success"=>false,"message"=>"Cannot create inventory with negative stock (Item ID: $itemId)"]); exit; }
        if (!mysqli_query($conn, "INSERT INTO inventory(item_id,quantity_in_stock) VALUES($itemId,$qtyChange)")) {
            echo json_encode(["success"=>false,"message"=>"Inventory insert failed: ".mysqli_error($conn)]); exit;
        }
    }

    if ($qtyChange > 0) {
        $existResult = mysqli_query($conn, "SELECT COUNT(*) AS total FROM production_planning WHERE part_id = $itemId AND status IN (1, 2)");
        $existRow = mysqli_fetch_assoc($existResult);
        if ($existRow && intval($existRow['total']) > 0) {
            $remainingQty = $qtyChange;
            $planResult = mysqli_query($conn, "SELECT id, produce_qty FROM production_planning WHERE part_id = $itemId AND status IN (1, 2) AND produce_qty > 0 ORDER BY req_date ASC");
            if ($planResult && mysqli_num_rows($planResult) > 0) {
                while ($planRow = mysqli_fetch_assoc($planResult)) {
                    if ($remainingQty <= 0) break;
                    $planId = intval($planRow['id']);
                    $currentProdQty = floatval($planRow['produce_qty']);
                    if ($remainingQty >= $currentProdQty) {
                        mysqli_query($conn, "UPDATE production_planning SET produce_qty = 0 WHERE id = $planId");
                        $remainingQty -= $currentProdQty;
                    } else {
                        $newProdQty = $currentProdQty - $remainingQty;
                        mysqli_query($conn, "UPDATE production_planning SET produce_qty = $newProdQty WHERE id = $planId");
                        $remainingQty = 0;
                    }
                }
            }
        }
    }
}

function reduceWorkOrderRawMaterial($conn, $itemId, $qtyUsed, $workOrderId, $setId = 0) {
    $itemId = intval($itemId); $qtyUsed = floatval($qtyUsed);
    $workOrderId = intval($workOrderId); $setId = intval($setId);
    mysqli_query($conn, "UPDATE work_order_raw_matrial SET quantity=quantity-($qtyUsed) WHERE work_order_id=$workOrderId AND product_id=$itemId AND set_id=$setId");
}

/* =====================================================================
 * log_action_assypu
 * ===================================================================== */
if (isset($_REQUEST['action']) && $_REQUEST['action'] == 'log_action_assypu') {
    $workOrderId              = intval($_REQUEST['work_order_id']);
    $assypu_machine_table_id  = intval($_REQUEST['assypu_machine_table_id']);
    $partId                   = intval($_REQUEST['part_id'] ?? 0);
    $machineId                = intval($_SESSION['machine_id'] ?? 0);
    $engineerId               = intval($_SESSION['engineer_id'] ?? 0);
    $actionType               = $conn->real_escape_string($_REQUEST['action_type']);
    $assypuMachineTableId     = intval($_REQUEST['assypu_machine_table_id'] ?? 0);
    $createdOn                = date('Y-m-d H:i:s');

    if (!$workOrderId || !$machineId || !$engineerId || !$actionType) {
        echo json_encode(["success" => false, "message" => "Missing parameters"]); exit;
    }

    if ($actionType === 'break') {
        $breakDuration = isset($_REQUEST['break_duration']) ? floatval($_REQUEST['break_duration']) : (20/60);
        $breakReason = isset($_REQUEST['break_reason']) ? mysqli_real_escape_string($conn, $_REQUEST['break_reason']) : 'Scheduled Break';
        if ($breakDuration < (5/60) || $breakDuration > 10) $breakDuration = (20/60);

        mysqli_autocommit($conn, false);
        $error = false; $errorMessage = '';

        $priorityResult = mysqli_query($conn, "SELECT id, priority FROM assypu_machine_table WHERE manuf_id = $assypu_machine_table_id LIMIT 1");
        if ($priorityResult && mysqli_num_rows($priorityResult) > 0) {
            $priorityRow = mysqli_fetch_assoc($priorityResult);
            $afterTaskId = $priorityRow['id'];
            $afterPriority = $priorityRow['priority'];
            $breakPriority = $afterPriority + 1;

            if (!mysqli_query($conn, "UPDATE assypu_machine_table SET priority = priority + 1 WHERE machine_id = $machineId AND priority >= $breakPriority")) {
                $error = true; $errorMessage = mysqli_error($conn);
            }
            if (!$error) {
                $taskResult = mysqli_query($conn, "SELECT end_date FROM assypu_machine_table WHERE id = $afterTaskId");
                if ($taskResult && mysqli_num_rows($taskResult) > 0) {
                    $taskRow = mysqli_fetch_assoc($taskResult);
                    $startDate = $taskRow['end_date'];
                    $endDate = date('Y-m-d H:i:s', strtotime($startDate) + ($breakDuration * 3600));
                    if (!mysqli_query($conn, "INSERT INTO assypu_machine_table (machine_id, start_date, end_date, total_hours, remaing_hours, is_break, break_reason, priority, work_order_id, part_id, quantity, product_name)
                                             VALUES ($machineId, '$startDate', '$endDate', $breakDuration, $breakDuration, 1, '$breakReason', $breakPriority, 0, 0, 0, 'BREAK: $breakReason')")) {
                        $error = true; $errorMessage = mysqli_error($conn);
                    }
                } else {
                    $error = true; $errorMessage = "Current task not found";
                }
            }
        } else {
            $error = true; $errorMessage = "Task not found for scheduling break";
        }

        mysqli_query($conn, "INSERT INTO assypu_machine_activity_log (work_order_id, part_id, machine_id, engineer_id, action_type, assypu_machine_table_id, created_at) VALUES ($workOrderId, $partId, $machineId, $engineerId, '$actionType', $assypuMachineTableId, '$createdOn')");

        if ($error) {
            mysqli_rollback($conn); mysqli_autocommit($conn, true);
            echo json_encode(["success" => false, "message" => "Break logged but scheduling failed: " . $errorMessage]);
        } else {
            mysqli_commit($conn); mysqli_autocommit($conn, true);
            assypu_fullReschedule($conn, $machineId);
            echo json_encode(["success" => true, "message" => "Break scheduled and logged successfully"]);
        }
        exit;
    }

    $sql = "INSERT INTO assypu_machine_activity_log (work_order_id, part_id, machine_id, engineer_id, action_type, assypu_machine_table_id, created_at)
            VALUES ($workOrderId, $partId, $machineId, $engineerId, '$actionType', $assypuMachineTableId, '$createdOn')";
    $result = mysqli_query($conn, $sql);

    if ($result) {
        if ($actionType === 'finish') {
            mysqli_query($conn, "UPDATE assypu_machine_table SET finished = 1
                          WHERE work_order_id = $workOrderId AND part_id = $partId
                            AND machine_id = $machineId AND manuf_id = $assypuMachineTableId");
        }
        echo json_encode(["success" => true]);
    } else {
        echo json_encode(["success" => false, "message" => mysqli_error($conn)]);
    }
    exit;
}

/* =====================================================================
 * get_current_task_priority_assypu — already correctly scoped by
 * machine_id, unchanged.
 * ===================================================================== */
elseif (isset($_REQUEST['action']) && $_REQUEST['action'] == 'get_current_task_priority_assypu') {
    $workOrderId = intval($_REQUEST['work_order_id']);
    $machineId   = intval($_REQUEST['machine_id']);
    $partId      = intval($_REQUEST['part_id']);

    $result = mysqli_query($conn, "SELECT id,priority FROM assypu_machine_table WHERE work_order_id=$workOrderId AND machine_id=$machineId AND part_id=$partId LIMIT 1");
    if ($result && mysqli_num_rows($result) > 0) {
        $row = mysqli_fetch_assoc($result);
        echo json_encode(['success'=>true,'task_id'=>$row['id'],'priority'=>$row['priority']]);
    } else {
        echo json_encode(['success'=>false,'message'=>'Task not found']);
    }
    exit;
}

/* =====================================================================
 * insert_schedule_break_assypu
 * ===================================================================== */
elseif (isset($_REQUEST['action']) && $_REQUEST['action'] == 'insert_schedule_break_assypu') {
    $machineId     = intval($_REQUEST['machine_id']);
    $duration      = floatval($_REQUEST['duration']);
    $reason        = mysqli_real_escape_string($conn, $_REQUEST['reason']);
    $afterTaskId   = isset($_REQUEST['after_task_id'])  ? intval($_REQUEST['after_task_id'])  : 0;
    $afterPriority = isset($_REQUEST['after_priority']) ? intval($_REQUEST['after_priority']) : 0;

    if ($machineId <= 0 || $duration < 0.5 || $duration > 10) {
        echo json_encode(['status'=>'error','message'=>'Invalid input values']); exit;
    }

    mysqli_autocommit($conn, false);
    $error = false; $errorMessage = '';
    $breakPriority = $afterPriority + 1;

    if (!mysqli_query($conn, "UPDATE assypu_machine_table SET priority=priority+1 WHERE machine_id=$machineId AND priority>=$breakPriority")) {
        $error = true; $errorMessage = mysqli_error($conn);
    }
    if (!$error) {
        $taskResult = mysqli_query($conn, "SELECT end_date FROM assypu_machine_table WHERE id=$afterTaskId");
        if ($taskResult && mysqli_num_rows($taskResult) > 0) {
            $taskRow   = mysqli_fetch_assoc($taskResult);
            $startDate = $taskRow['end_date'];
            $endDate   = date('Y-m-d H:i:s', strtotime($startDate)+($duration*3600));
            if (!mysqli_query($conn, "INSERT INTO assypu_machine_table(machine_id,start_date,end_date,total_hours,remaing_hours,is_break,break_reason,priority,work_order_id,part_id,quantity,product_name) VALUES($machineId,'$startDate','$endDate',$duration,$duration,1,'$reason',$breakPriority,0,0,0,'BREAK: $reason')")) {
                $error = true; $errorMessage = mysqli_error($conn);
            }
        } else { $error = true; $errorMessage = "Current task not found"; }
    }

    if ($error) { mysqli_rollback($conn); echo json_encode(['status'=>'error','message'=>$errorMessage]); }
    else        { mysqli_commit($conn); assypu_fullReschedule($conn,$machineId); echo json_encode(['status'=>'success','message'=>'Break scheduled successfully']); }
    mysqli_autocommit($conn, true); exit;
}

/* =====================================================================
 * log_breakdown_assypu
 * ===================================================================== */
elseif (isset($_REQUEST['action']) && $_REQUEST['action'] == 'log_breakdown_assypu') {

    $workOrderId          = intval($_REQUEST['work_order_id']);
    $partId               = intval($_REQUEST['part_id']);
    $machineId            = intval($_SESSION['machine_id']);
    $engineerId           = intval($_SESSION['engineer_id']);
    $assypuMachineTableId = intval($_REQUEST['assypu_machine_table_id']);
    $breakdownReason      = isset($_REQUEST['breakdown_reason']) ? intval($_REQUEST['breakdown_reason']) : null;
    $breakdownDuration    = isset($_REQUEST['breakdown_duration']) ? floatval($_REQUEST['breakdown_duration']) : 1.0;
    $createdOn            = date('Y-m-d H:i:s');

    if (!$workOrderId || !$machineId || !$engineerId) { echo json_encode(["success"=>false,"message"=>"Missing parameters"]); exit; }
    if ($breakdownDuration < 0.5 || $breakdownDuration > 24) { echo json_encode(["success"=>false,"message"=>"Invalid breakdown duration."]); exit; }
    if (!$breakdownReason) { echo json_encode(["success"=>false,"message"=>"Breakdown reason is required."]); exit; }

    mysqli_autocommit($conn, false);
    $error = false; $errorMessage = ''; $okCount = 0; $remainingQuantity = 0; $newManufId = null;

    $workOrderResult = mysqli_query($conn, "SELECT id,work_order_id,part_id,quantity,priority,total_hours,setup_hours,product_name,set_id,end_date FROM assypu_machine_table WHERE manuf_id=$assypuMachineTableId AND machine_id=$machineId LIMIT 1");
    if (!$workOrderResult || mysqli_num_rows($workOrderResult) == 0) { mysqli_rollback($conn); mysqli_autocommit($conn,true); echo json_encode(["success"=>false,"message"=>"Work order not found"]); exit; }

    $cwo            = mysqli_fetch_assoc($workOrderResult);
    $currentId      = $cwo['id']; $currentPriority = intval($cwo['priority']);
    $totalQuantity  = intval($cwo['quantity']); $totalHours = floatval($cwo['total_hours']);
    $setupHours     = floatval($cwo['setup_hours']); $endDate = $cwo['end_date'];

    $okRow   = mysqli_fetch_assoc(mysqli_query($conn, "SELECT SUM(quantity) AS ok_count FROM assypu_part_count_log WHERE work_order_id=$workOrderId AND assypu_machine_table_id=$assypuMachineTableId AND part_id=$partId AND type='ok'"));
    $okCount = intval($okRow['ok_count'] ?? 0);
    $remainingQuantity = $totalQuantity - $okCount;

    // FIX: was `SELECT code FROM keycode` -- every other breakdown-reason
    // lookup in this codebase (CNC, PU, VAC, and this module's own
    // admin-side breakdown start) queries keycode_table. See file header.
    $reasonText   = "Unknown";
    $reasonResult = mysqli_query($conn, "SELECT code FROM keycode_table WHERE id = $breakdownReason LIMIT 1");
    if ($reasonResult && mysqli_num_rows($reasonResult) > 0) {
        $reasonRow  = mysqli_fetch_assoc($reasonResult);
        $reasonText = mysqli_real_escape_string($conn, $reasonRow['code']);
    }

    if ($okCount > 0 && $remainingQuantity > 0) {

        $runHoursPerUnit = ($totalHours - $setupHours) / $totalQuantity;
        $completedHours  = ($okCount * $runHoursPerUnit) + $setupHours;
        $remainingHours  = ($remainingQuantity * $runHoursPerUnit) + $setupHours;

        $manufId = $assypuMachineTableId;
        $manufResult = mysqli_query($conn, "SELECT * FROM manufacture_machine_table WHERE id = $manufId LIMIT 1");

        if ($manufResult && mysqli_num_rows($manufResult) > 0) {
            $originalManuf     = mysqli_fetch_assoc($manufResult);
            $manufQuantity     = floatval($originalManuf['quantity']);
            $manufTotalHours   = floatval($originalManuf['total_hours']);
            $manufHoursPerUnit = $manufQuantity > 0 ? ($manufTotalHours / $manufQuantity) : 0;
            $manufCompletedHours = $okCount * $manufHoursPerUnit;
            $manufRemainingHours = $remainingQuantity * $manufHoursPerUnit;

            if (!mysqli_query($conn, "UPDATE manufacture_machine_table SET quantity = $okCount, total_hours = $manufCompletedHours WHERE id = $manufId")) {
                $error = true; $errorMessage = "Failed to update original manufacture record: " . mysqli_error($conn);
            }
            if (!$error) {
                $manufColumns = []; $manufValues = [];
                foreach ($originalManuf as $col => $val) {
                    if ($col === 'id') continue;
                    $manufColumns[] = "`$col`";
                    if ($col === 'quantity')      $manufValues[] = $remainingQuantity;
                    elseif ($col === 'total_hours') $manufValues[] = $manufRemainingHours;
                    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)) {
                    $error = true; $errorMessage = "Failed to create new manufacture record: " . mysqli_error($conn);
                } else {
                    $newManufId = $conn->insert_id;
                }
            }
        }

        if (!$error && !mysqli_query($conn, "UPDATE assypu_machine_table SET quantity = $okCount, total_hours = $completedHours, finished = 1 WHERE id = $currentId")) {
            $error = true; $errorMessage = "Failed to update current work order: " . mysqli_error($conn);
        }
        if (!$error && !mysqli_query($conn, "UPDATE assypu_machine_table SET priority = priority + 2 WHERE machine_id = $machineId AND priority > $currentPriority")) {
            $error = true; $errorMessage = "Failed to shift priorities: " . mysqli_error($conn);
        }
        if (!$error) {
            $bdStartDate = $endDate;
            $bdEndDate = date('Y-m-d H:i:s', strtotime($bdStartDate) + ($breakdownDuration * 3600));
            if (!mysqli_query($conn, "INSERT INTO assypu_machine_table
                    (machine_id, start_date, end_date, total_hours, remaing_hours, is_break, break_reason, priority,
                     work_order_id, part_id, quantity, product_name, setup_hours)
                 VALUES ($machineId, '$bdStartDate', '$bdEndDate', $breakdownDuration, $breakdownDuration, 1,
                         'BREAKDOWN: $reasonText', " . ($currentPriority + 1) . ", 0, 0, 0, 'BREAKDOWN: $reasonText', 0)")) {
                $error = true; $errorMessage = "Failed to insert breakdown task: " . mysqli_error($conn);
            }
        }
        if (!$error) {
            $manufIdForInsert = $newManufId !== null ? intval($newManufId) : 'NULL';
            if (!mysqli_query($conn, "INSERT INTO assypu_machine_table
                    (machine_id, manuf_id, work_order_id, part_id, quantity, total_hours, priority, product_name,
                     set_id, finished, is_break, start_date, end_date, remaing_hours, setup_hours)
                 SELECT machine_id, $manufIdForInsert, work_order_id, part_id, $remainingQuantity, $remainingHours,
                        " . ($currentPriority + 2) . ", product_name, set_id, 0, 0, NULL, NULL, $remainingHours, $setupHours
                 FROM assypu_machine_table WHERE id = $currentId")) {
                $error = true; $errorMessage = "Failed to insert remaining work order: " . mysqli_error($conn);
            }
        }

    } else {
        $bdPriority = $currentPriority + 1;
        if (!mysqli_query($conn, "UPDATE assypu_machine_table SET priority=priority+1 WHERE machine_id=$machineId AND priority>$currentPriority")) { $error=true; $errorMessage=mysqli_error($conn); }
        if (!$error) {
            $bdStartDate=$endDate; $bdEndDate=date('Y-m-d H:i:s',strtotime($bdStartDate)+($breakdownDuration*3600));
            if (!mysqli_query($conn, "INSERT INTO assypu_machine_table(machine_id,start_date,end_date,total_hours,remaing_hours,is_break,break_reason,priority,work_order_id,part_id,quantity,product_name,setup_hours) VALUES($machineId,'$bdStartDate','$bdEndDate',$breakdownDuration,$breakdownDuration,1,'BREAKDOWN: $reasonText',$bdPriority,0,0,0,'BREAKDOWN: $reasonText',0)")) { $error=true; $errorMessage=mysqli_error($conn); }
        }
    }

    mysqli_query($conn, "INSERT INTO assypu_machine_breakdown_log(work_order_id,part_id,machine_id,engineer_id,assypu_machine_table_id,breakdown_reason,breakdown_duration,ok_count_at_breakdown,remaining_quantity,status,created_at) VALUES($workOrderId,$partId,$machineId,$engineerId,$assypuMachineTableId,$breakdownReason,$breakdownDuration,$okCount,$remainingQuantity,'active','$createdOn')");

    if ($error) { mysqli_rollback($conn); mysqli_autocommit($conn,true); echo json_encode(["success"=>false,"message"=>"Breakdown logged but scheduling failed: $errorMessage"]); }
    else {
        mysqli_commit($conn); mysqli_autocommit($conn,true);
        assypu_fullReschedule($conn, $machineId);
        $msg = "Breakdown logged successfully!";
        if ($okCount > 0 && $remainingQuantity > 0) {
            $msg .= " Work order split: $okCount completed, $remainingQuantity remaining.";
            if (!empty($newManufId)) $msg .= " New manufacture record ID: $newManufId.";
        }
        echo json_encode(["success"=>true,"message"=>$msg]);
    }
    exit;
}

/* =====================================================================
 * log_count_assypu — unchanged, no scheduling bugs identified.
 * ===================================================================== */
elseif (isset($_REQUEST['action']) && $_REQUEST['action'] == 'log_count_assypu') {

    $workOrderId          = intval($_REQUEST['work_order_id']);
    $machineId            = intval($_SESSION['machine_id'] ?? 0);
    $engineerId           = intval($_SESSION['engineer_id'] ?? 0);
    $type                 = $conn->real_escape_string($_REQUEST['type']);
    $quantity             = intval($_REQUEST['quantity'] ?? 1);
    $nok_q_scrap_part_ids_part_ids = isset($_REQUEST['nok_q_scrap_part_ids_part_ids']) ? $conn->real_escape_string($_REQUEST['nok_q_scrap_part_ids_part_ids']) : '';
    $reasonId             = isset($_REQUEST['reason_id']) ? intval($_REQUEST['reason_id']) : null;
    $partId               = isset($_REQUEST['part_id'])   ? intval($_REQUEST['part_id'])   : 0;
    $assypuMachineTableId = intval($_REQUEST['assypu_machine_table_id'] ?? 0);
    $createdOn            = date('Y-m-d H:i:s');
    $set_id               = intval($_REQUEST['set_id'] ?? 0);

    if (!$workOrderId || !$machineId || !$engineerId || !$type) {
        echo json_encode(["success"=>false,"message"=>"Missing parameters"]); exit;
    }

    if ($type === 'ok') {

        if ($set_id > 0) {
            $mapResult = $conn->query("SELECT part_id,ratio FROM set_mangmnt_mapping WHERE set_id=$set_id");
            if ($mapResult->num_rows > 0) {
                while ($map = $mapResult->fetch_assoc()) {
                    updateInventoryStock($conn, intval($map['part_id']), floatval($map['ratio']) * $quantity, $workOrderId, $set_id);
                }
            }
            reduceBomChildInventory($conn, $partId, $quantity, $workOrderId, $set_id);

            if ($quantity > 0) {
                if (!mysqli_query($conn, "INSERT INTO assypu_part_count_log(work_order_id,machine_id,part_id,partIds,engineer_id,setId,type,quantity,assypu_machine_table_id,created_at) VALUES($workOrderId,$machineId,$partId,$partId,$engineerId,$set_id,'$type',$quantity,$assypuMachineTableId,'$createdOn')")) {
                    echo json_encode(["success"=>false,"message"=>mysqli_error($conn)]); exit;
                }
            } elseif ($quantity < 0) {
                $checkResult = $conn->query("SELECT id,quantity FROM assypu_part_count_log WHERE work_order_id=$workOrderId AND machine_id=$machineId AND engineer_id=$engineerId AND part_id=$partId AND assypu_machine_table_id=$assypuMachineTableId AND type='$type' ORDER BY id DESC LIMIT 1");
                if ($checkResult->num_rows == 0) { echo json_encode(["success"=>false,"message"=>"Cannot decrement non-existent set record"]); exit; }
                $row = $checkResult->fetch_assoc();
                $newQuantity = $row['quantity'] + $quantity;
                if ($newQuantity < 0) { echo json_encode(["success"=>false,"message"=>"Count cannot be negative for set ID: $set_id"]); exit; }
                if ($newQuantity == 0) {
                    if (!mysqli_query($conn, "DELETE FROM assypu_part_count_log WHERE id={$row['id']}")) { echo json_encode(["success"=>false,"message"=>mysqli_error($conn)]); exit; }
                } else {
                    if (!mysqli_query($conn, "UPDATE assypu_part_count_log SET quantity=$newQuantity WHERE id={$row['id']}")) { echo json_encode(["success"=>false,"message"=>mysqli_error($conn)]); exit; }
                }
            }
            echo json_encode(["success"=>true]); exit;
        }
        if ($quantity > 0) {
            $insertSql = "INSERT INTO assypu_part_count_log(work_order_id,machine_id,part_id,partIds,engineer_id,type,quantity,assypu_machine_table_id,created_at) VALUES($workOrderId,$machineId,$partId,$partId,$engineerId,'$type',$quantity,$assypuMachineTableId,'$createdOn')";
            updateInventoryStock($conn, $partId, $quantity, $workOrderId, $set_id);
            reduceBomChildInventory($conn, $partId, $quantity, $workOrderId, $set_id);
            echo json_encode(mysqli_query($conn, $insertSql) ? ["success"=>true] : ["success"=>false,"message"=>mysqli_error($conn)]);
        } elseif ($quantity < 0) {
            $checkResult = $conn->query("SELECT id,quantity FROM assypu_part_count_log WHERE work_order_id=$workOrderId AND machine_id=$machineId AND engineer_id=$engineerId AND part_id=$partId AND assypu_machine_table_id=$assypuMachineTableId AND type='$type' ORDER BY id DESC LIMIT 1");
            if ($checkResult->num_rows > 0) {
                $row = $checkResult->fetch_assoc();
                $newQuantity = $row['quantity'] + $quantity;
                if ($newQuantity < 0) { echo json_encode(["success"=>false,"message"=>"Count cannot be negative"]); exit; }
                updateInventoryStock($conn, $partId, $quantity, $workOrderId, $set_id);
                reduceBomChildInventory($conn, $partId, $quantity, $workOrderId, $set_id);
                $ok = ($newQuantity == 0)
                    ? mysqli_query($conn, "DELETE FROM assypu_part_count_log WHERE id={$row['id']}")
                    : mysqli_query($conn, "UPDATE assypu_part_count_log SET quantity=$newQuantity WHERE id={$row['id']}");
                echo json_encode($ok ? ["success"=>true] : ["success"=>false,"message"=>mysqli_error($conn)]);
            } else {
                echo json_encode(["success"=>false,"message"=>"Cannot decrement non-existent record"]); exit;
            }
        }
    } else {
        if ($quantity > 0) {
            if (empty($nok_q_scrap_part_ids_part_ids)) { echo json_encode(["success"=>false,"message"=>"No parts selected"]); exit; }
            if (empty($reasonId))                       { echo json_encode(["success"=>false,"message"=>"No reason selected"]); exit; }

            mysqli_autocommit($conn, false);
            $success = true;

            if (!mysqli_query($conn, "INSERT INTO assypu_part_count_log(work_order_id,machine_id,part_id,engineer_id,type,setId,partIds,reason_id,quantity,assypu_machine_table_id,created_at) VALUES($workOrderId,$machineId,$partId,$engineerId,'$type','$set_id','$nok_q_scrap_part_ids_part_ids',$reasonId,$quantity,$assypuMachineTableId,'$createdOn')")) {
                mysqli_rollback($conn); mysqli_autocommit($conn,true);
                echo json_encode(["success"=>false,"message"=>mysqli_error($conn)]); exit;
            }
            $assypuPartCountLogId = mysqli_insert_id($conn);

            $selectedParts   = array_map('intval', array_filter(explode(',', $nok_q_scrap_part_ids_part_ids), 'strlen'));
            $unselectedParts = array_map('intval', array_filter(explode(',', $_REQUEST['nok_q_ok_part_ids'] ?? ''), 'strlen'));

            foreach ($selectedParts as $singlePartId) {
                if ($singlePartId <= 0) continue;
                if (!mysqli_query($conn, "INSERT INTO assypu_selected_ok_parts(part_id,quantity,assypu_machine_table_id,assypu_part_count_log_id,created_at) VALUES($singlePartId,1,$assypuMachineTableId,$assypuPartCountLogId,'$createdOn')")) {
                    $success = false; break;
                }
            }
            if ($success) {
                foreach ($unselectedParts as $singlePartId) {
                    if ($singlePartId <= 0) continue;
                    if (!mysqli_query($conn, "INSERT INTO assypu_unselected_ok_parts(part_id,quantity,assypu_machine_table_id,assypu_part_count_log_id,created_at) VALUES($singlePartId,1,$assypuMachineTableId,$assypuPartCountLogId,'$createdOn')")) {
                        $success = false; break;
                    }
                    updateInventoryStock($conn, $singlePartId, 1, $workOrderId, $set_id);
                }
            }
            if ($success) {
                reduceBomChildInventory($conn, $partId, $quantity, $workOrderId, $set_id);
                mysqli_commit($conn); mysqli_autocommit($conn,true);
                echo json_encode(["success"=>true]);
            } else {
                mysqli_rollback($conn); mysqli_autocommit($conn,true);
                echo json_encode(["success"=>false,"message"=>mysqli_error($conn)]);
            }
            exit;
        } else if ($quantity < 0) {
            mysqli_autocommit($conn, false);
            $success = true;

            $checkResult = $conn->query("SELECT id,quantity FROM assypu_part_count_log WHERE work_order_id=$workOrderId AND machine_id=$machineId AND part_id=$partId AND assypu_machine_table_id=$assypuMachineTableId AND type='$type' ORDER BY id DESC LIMIT 1");

            if ($checkResult->num_rows > 0) {
                $row             = $checkResult->fetch_assoc();
                $existingId      = $row['id'];
                $currentQuantity = $row['quantity'];
                $newQuantity     = $currentQuantity + $quantity;

                if ($newQuantity < 0) { mysqli_autocommit($conn,true); echo json_encode(["success"=>false,"message"=>"Count cannot be negative"]); exit; }

                if ($newQuantity == 0) {
                    $allUnselectedRes = mysqli_query($conn, "SELECT part_id,quantity FROM assypu_unselected_ok_parts WHERE assypu_part_count_log_id=$existingId");
                    $unselectedReversals = [];
                    while ($uRow = mysqli_fetch_assoc($allUnselectedRes)) {
                        $invPartId = intval($uRow['part_id']);
                        if (!isset($unselectedReversals[$invPartId])) $unselectedReversals[$invPartId] = 0;
                        $unselectedReversals[$invPartId] += floatval($uRow['quantity']);
                    }
                    foreach ($unselectedReversals as $invPartId => $totalQty) {
                        updateInventoryStock($conn, $invPartId, -$totalQty, $workOrderId, $set_id);
                    }
                    if (!mysqli_query($conn, "DELETE FROM assypu_selected_ok_parts   WHERE assypu_part_count_log_id=$existingId")) $success = false;
                    if ($success && !mysqli_query($conn, "DELETE FROM assypu_unselected_ok_parts WHERE assypu_part_count_log_id=$existingId")) $success = false;
                    if ($success && !mysqli_query($conn, "DELETE FROM assypu_part_count_log WHERE id=$existingId")) $success = false;
                } else {
                    $lastUnselectedRes = mysqli_query($conn, "SELECT id,part_id,quantity FROM assypu_unselected_ok_parts WHERE assypu_part_count_log_id=$existingId ORDER BY id DESC LIMIT 1");
                    if ($lastUnselectedRes && mysqli_num_rows($lastUnselectedRes) > 0) {
                        $lastRow = mysqli_fetch_assoc($lastUnselectedRes);
                        updateInventoryStock($conn, intval($lastRow['part_id']), -floatval($lastRow['quantity']), $workOrderId, $set_id);
                    }
                    if (!mysqli_query($conn, "DELETE FROM assypu_selected_ok_parts   WHERE assypu_part_count_log_id=$existingId ORDER BY id DESC LIMIT 1")) $success = false;
                    if ($success) mysqli_query($conn, "DELETE FROM assypu_unselected_ok_parts WHERE assypu_part_count_log_id=$existingId ORDER BY id DESC LIMIT 1");
                    if ($success) {
                        if (!mysqli_query($conn, "UPDATE assypu_part_count_log SET quantity=$newQuantity WHERE id=$existingId")) $success = false;
                    }
                }

                if ($success) {
                    reduceBomChildInventory($conn, $partId, $quantity, $workOrderId, $set_id);
                    mysqli_commit($conn); mysqli_autocommit($conn,true);
                    echo json_encode(["success"=>true]);
                } else {
                    mysqli_rollback($conn); mysqli_autocommit($conn,true);
                    echo json_encode(["success"=>false,"message"=>mysqli_error($conn)]);
                }
            } else {
                mysqli_autocommit($conn, true);
                echo json_encode(["success"=>false,"message"=>"No recent $type entry found to decrement"]);
            }
            exit;
        }
    }
    exit;
}

/* =====================================================================
 * start_changeover / end_changeover / first_off_signoff — unchanged.
 * ===================================================================== */
elseif (isset($_REQUEST['action']) && $_REQUEST['action'] == 'start_changeover') {
    $machineId        = intval($_REQUEST['machine_id']);
    $clockNo          = mysqli_real_escape_string($conn, $_REQUEST['clock_no'] ?? '');
    $workOrderTableId = intval($_REQUEST['work_order_table_id'] ?? 0);
    $startTime        = date('Y-m-d H:i:s');
    if (empty($clockNo)) { echo json_encode(["success"=>false,"message"=>"Clock number required"]); exit; }
    $userRes = mysqli_query($conn, "SELECT id FROM admin WHERE clock_no='$clockNo' LIMIT 1");
    if (!$userRes || mysqli_num_rows($userRes) == 0) { echo json_encode(["success"=>false,"message"=>"Invalid Clock Number"]); exit; }
    $employeeId = intval(mysqli_fetch_assoc($userRes)['id']);
    if ($workOrderTableId <= 0) { echo json_encode(["success"=>false,"message"=>"No work order selected"]); exit; }
    if (mysqli_query($conn, "INSERT INTO assypu_machine_changeover(machine_id,employee_id,work_order_table_id,start_time,status) VALUES($machineId,$employeeId,$workOrderTableId,'$startTime','running')")) {
        echo json_encode(["success"=>true]);
    } else { echo json_encode(["success"=>false,"message"=>mysqli_error($conn)]); }
    exit;
}

elseif (isset($_REQUEST['action']) && $_REQUEST['action'] == 'end_changeover') {
    $machineId = intval($_REQUEST['machine_id']); $endTime = date('Y-m-d H:i:s');
    $result = mysqli_query($conn, "SELECT id,start_time FROM assypu_machine_changeover WHERE machine_id=$machineId AND status='running' ORDER BY id DESC LIMIT 1");
    if ($result && mysqli_num_rows($result) > 0) {
        $row = mysqli_fetch_assoc($result); $duration = time() - strtotime($row['start_time']);
        mysqli_query($conn, "UPDATE assypu_machine_changeover SET end_time='$endTime',duration_seconds=$duration,status='completed' WHERE id={$row['id']}");
        echo json_encode(["success"=>true]);
    } else { echo json_encode(["success"=>false]); }
    exit;
}

elseif (isset($_REQUEST['action']) && $_REQUEST['action'] == 'first_off_signoff') {
    $workOrderId          = intval($_REQUEST['work_order_id']); $partId = intval($_REQUEST['part_id']);
    $machineId            = intval($_REQUEST['machine_id']);
    $assypuMachineTableId = intval($_REQUEST['assypu_machine_table_id']); $signedAt = date('Y-m-d H:i:s');
    $qcComment            = mysqli_real_escape_string($conn, $_REQUEST['qc_comment'] ?? '');
    $qcClockNo            = mysqli_real_escape_string($conn, $_REQUEST['qc_clock_no'] ?? '');
    if (empty($qcClockNo)) { echo json_encode(["success"=>false,"message"=>"Clock number required"]); exit; }
    $userRes = mysqli_query($conn, "SELECT id FROM admin WHERE clock_no='$qcClockNo' LIMIT 1");
    if (!$userRes || mysqli_num_rows($userRes) == 0) { echo json_encode(["success"=>false,"message"=>"Invalid Clock Number"]); exit; }
    $qcEmployeeId = intval(mysqli_fetch_assoc($userRes)['id']);
    if (mysqli_query($conn, "INSERT INTO assypu_first_off_signoff(work_order_id,part_id,assypu_machine_table_id,machine_id,qc_employee_id,qc_comment,signed_at) VALUES($workOrderId,$partId,$assypuMachineTableId,$machineId,$qcEmployeeId,'$qcComment','$signedAt')")) {
        echo json_encode(["success"=>true]);
    } else { echo json_encode(["success"=>false,"message"=>mysqli_error($conn)]); }
    exit;
}

/* =====================================================================
 * end_breakdown_assypu
 * ===================================================================== */
elseif (isset($_REQUEST['action']) && $_REQUEST['action'] == 'end_breakdown_assypu') {
    $machineId  = intval($_SESSION['machine_id']  ?? 0);
    $engineerId = intval($_SESSION['engineer_id'] ?? 0);
    $createdOn  = date('Y-m-d H:i:s');
    if (!$machineId || !$engineerId) { echo json_encode(["success"=>false,"message"=>"Missing parameters"]); exit; }

    $res = mysqli_query($conn, "SELECT * FROM assypu_machine_breakdown_log WHERE machine_id=$machineId AND status='active' ORDER BY id DESC LIMIT 1");
    if (!$res || mysqli_num_rows($res) == 0) { echo json_encode(["success"=>false,"message"=>"No active breakdown found"]); exit; }
    $row = mysqli_fetch_assoc($res);

    if (mysqli_query($conn, "INSERT INTO assypu_machine_breakdown_log
        (work_order_id, part_id, machine_id, engineer_id, assypu_machine_table_id, breakdown_reason, breakdown_duration, ok_count_at_breakdown, remaining_quantity, status, created_at)
        VALUES ({$row['work_order_id']}, {$row['part_id']}, $machineId, $engineerId, {$row['assypu_machine_table_id']}, {$row['breakdown_reason']}, {$row['breakdown_duration']}, {$row['ok_count_at_breakdown']}, {$row['remaining_quantity']}, 'completed', '$createdOn')"
    )) {
        // FIX: previously this endpoint stopped here -- is_breakdown_affected
        // was never cleared and no reschedule ever ran. Same fix already
        // applied to CNC's, PU's, and VAC's equivalent endpoints.
        mysqli_query($conn, "UPDATE assypu_machine_table SET is_breakdown_affected = 0 WHERE machine_id = $machineId AND is_breakdown_affected = 1");
        assypu_fullReschedule($conn, $machineId);

        echo json_encode(["success"=>true,"message"=>"Breakdown closed successfully"]);
    } else { echo json_encode(["success"=>false,"message"=>mysqli_error($conn)]); }
    exit;
}

/* =====================================================================
 * get_live_uph_assypu — FIX: now uses the single, correct, shared UPH
 * algorithm (assypu_calculateAndStoreActualUPH) instead of its own
 * broken constant-based calculation. See file header for the full story.
 * ===================================================================== */
/* =====================================================================
 * get_live_uph_assypu — simplified: live UPH = OK qty counted in the
 * last N minutes, divided by that window. No activity-log / session
 * based start-time lookup.
 * ===================================================================== */
elseif (isset($_REQUEST['action']) && $_REQUEST['action'] == 'get_live_uph_assypu') {

    $workOrderId          = intval($_REQUEST['work_order_id']           ?? 0);
    $partId               = intval($_REQUEST['part_id']                 ?? 0);
    $assypuMachineTableId = intval($_REQUEST['assypu_machine_table_id'] ?? 0);
    $machineId            = intval($_SESSION['machine_id']              ?? 0);
    $windowMinutes        = max(5, min(480, intval($_REQUEST['window_minutes'] ?? 60)));

    if (!$workOrderId || !$partId || !$assypuMachineTableId || !$machineId) {
        echo json_encode(['success' => false, 'message' => 'Missing parameters']); exit;
    }

    $windowStart = date('Y-m-d H:i:s', strtotime("-{$windowMinutes} minutes"));

    $okRow = mysqli_fetch_assoc(mysqli_query($conn, "
        SELECT COALESCE(SUM(quantity),0) AS ok_units FROM assypu_part_count_log
        WHERE work_order_id = $workOrderId AND assypu_machine_table_id = $assypuMachineTableId
          AND part_id = $partId AND type = 'ok' AND created_at >= '$windowStart'
    "));
    $okUnits = (int) $okRow['ok_units'];

    // Live UPH over the fixed window (no activity-log lookup)
    $productiveHours = $windowMinutes / 60;
    $liveUph = ($productiveHours > 0) ? round($okUnits / $productiveHours, 2) : 0;

    // Planned UPH: route-based, unchanged from the current fix
    $rowInfo = mysqli_fetch_assoc(mysqli_query($conn, "SELECT part_id, set_id FROM assypu_machine_table WHERE manuf_id = $assypuMachineTableId AND machine_id = $machineId LIMIT 1"));
    $plannedUph = $rowInfo ? assypu_calculateBasicUph($conn, $rowInfo['part_id'], intval($rowInfo['set_id'] ?? 0)) : 0;

    echo json_encode([
        'success'        => true,
        'live_uph'       => $liveUph,
        'planned_uph'    => $plannedUph,
        'ok_units'       => $okUnits,
        'window_minutes' => $windowMinutes,
        'is_green'       => ($plannedUph > 0 && $liveUph >= $plannedUph),
    ]);
    exit;
}

elseif (isset($_REQUEST['action']) && $_REQUEST['action'] == 'logout_assypu') {
    unset($_SESSION['engineer_id']);
    unset($_SESSION['machine_id']);
    echo json_encode(['success' => true]);
    exit;
}
?>