<?php

namespace App\Http\Controllers\Manager;

use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use App\Models\RouteCard;
use App\Models\RouteCardProcess;
use App\Models\CustomerVendor;
use App\Models\Machines;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Log;
use Exception;
use Illuminate\Support\Facades\DB;
use App\Models\ProductType;
use App\Models\Process;
use App\Models\Grn;
use Carbon\Carbon;
use App\Models\EmployeeDetails;


class ManagerRouteCardController extends Controller
{
    protected $officeStart = '09:00';
    protected $officeEnd = '18:30';
    public function readyToPlan()
    {
        // Logic: Fetch Sales Orders that are approved but do not have a corresponding Route Card (Plan) yet.
        // Assuming order_no links them.
        $plannedOrders = RouteCard::pluck('order_no')->toArray();
        $orders = \App\Models\SalesOrder::with('customer')
                    // ->where('status', 'Approved') // Enable if sales order has status
                    ->whereNotIn('sales_order_no', $plannedOrders)
                    ->get();
        
        return view('manager.manufacturing.ready_to_plan', compact('orders'));
    }

    public function planningDashboard()
    {
        $stats = [
            'high' => RouteCard::where('priority', 'High')->where('status', 'Planned')->count(),
            'medium' => RouteCard::where('priority', 'Medium')->where('status', 'Planned')->count(),
            'low' => RouteCard::where('priority', 'Low')->where('status', 'Planned')->count(),
        ];

        // Fetch Timeline: Flattened list of processes for planned route cards
        // Order by Priority then Start Time
        $timeline = DB::table('route_card_processes')
            ->join('route_cards', 'route_card_processes.route_card_id', '=', 'route_cards.id')
            ->leftJoin('machines', 'route_card_processes.machine', '=', 'machines.id')
            ->select(
                'route_cards.priority',
                'route_cards.route_no',
                'route_cards.project_start_date',
                'route_card_processes.process',
                'route_card_processes.start_date',
                'route_card_processes.end_date',
                'machines.machine_name'
            )
            ->where('route_cards.status', 'Planned')
            ->orderByRaw("FIELD(route_cards.priority, 'High', 'Medium', 'Low')")
            ->orderBy('route_card_processes.start_date')
            ->get();

        return view('manager.manufacturing.planning_dashboard', compact('stats', 'timeline'));
    }

    public function reschedule()
    {
        try {
            $service = new \App\Services\PlanningService();
            $service->recalculateSchedule();
            return back()->with('success', 'Schedule Recalculated Successfully!');
        } catch (\Exception $e) {
            return back()->withErrors('Error rescheduling: ' . $e->getMessage());
        }
    }

    public function routeCardPage()
    {
        try {
            $last = RouteCard::orderBy('id', 'desc')->first();
            if ($last && preg_match('/(\d{4})$/', $last->route_no, $matches)) {
                $number = intval($matches[1]) + 1;
            } else {
                $number = 1;
            }
            $nextRouteNo = 'UEPL/RC/' . str_pad($number, 4, '0', STR_PAD_LEFT);
            $companies = CustomerVendor::all();
            $machines = Machines::all();
            // Calculate machine availability
            $machineAvailability = [];
            foreach ($machines as $machine) {
                // Get the latest end_date for this machine (if ongoing, you could use start_date or current time)
                $latest = DB::table('route_card_processes')
                    ->where('machine', $machine->machine_id)
                    ->orderByDesc('end_date')
                    ->first();

                if ($latest) {
                    if (!$latest->end_date || Carbon::parse($latest->end_date)->isFuture()) {
                        $availDate = 'In Use till ' . Carbon::parse($latest->end_date)->format('d-m-Y h:i A');
                    } else {
                        $availDate = Carbon::parse($latest->end_date)->format('d-m-Y h:i A');
                    }
                }
                $machineAvailability[$machine->machine_id] = $availDate;
            }

            // Get used GRN numbers
            $usedGrnNos = RouteCard::pluck('grn_no')->toArray();

            // Only GRNs not used in RouteCard
            $availableGrns = Grn::whereNotIn('grn_no', $usedGrnNos)->get();

            // Fetch product types from DB
            $productTypes = ProductType::all();
            $employees = EmployeeDetails::all();
            $operatorAvailability = [];
            foreach ($employees as $operator) {
                $latest = DB::table('route_card_processes')
                    ->where('operator', $operator->id)
                    ->orderByDesc('end_date')
                    ->first();

                if ($latest) {
                    if (!$latest->end_date || Carbon::parse($latest->end_date)->isFuture()) {
                        $availDate = 'In Use till ' . Carbon::parse($latest->end_date)->format('d-m-Y h:i A');
                    } else {
                        $availDate = Carbon::parse($latest->end_date)->format('d-m-Y h:i A');
                    }
                }
                $operatorAvailability[$operator->id] = $availDate;
            }
            // Pass productTypes to the view
            return view('manager.routecard.routecardorder', compact('nextRouteNo', 'companies', 'machines', 'productTypes', 'employees', 'availableGrns', 'machineAvailability', 'operatorAvailability'));
        } catch (Exception $e) {
            return back()->withErrors('Error loading route card page.');
        }
    }
    public function approvedList()
    {
        $cards = RouteCard::where('status', 'Approved')->orderByDesc('route_date')->get();
        return response()->json($cards);
    }
    public function approve(Request $request, $id)
    {
        $validated = $request->validate([
            'status' => 'required|in:Approved,Rejected,Pending',
            'approved_by' => 'nullable|string|max:255',
        ]);
        $card = RouteCard::findOrFail($id);
        $card->status = $validated['status'];
        $card->approved_by = $validated['approved_by'];
        $card->approved_at = in_array($validated['status'], ['Approved', 'Rejected']) ? now() : null;
        $card->save();

        return response()->json(['success' => true, 'status' => $card->status, 'approved_by' => $card->approved_by, 'approved_at' => $card->approved_at]);
    }
    public function routeCardDetails()
    {
        try {
            $cards = RouteCard::with('processes')->latest()->get();
            return view('manager.routecard.routecarddetails', compact('cards'));
        } catch (Exception $e) {
            return back()->withErrors('Error loading route card details.');
        }
    }
    public function getProcessList(Request $request)
    {
        $type = $request->query('product_type');
        $productType = ProductType::where('name', $type)->first();
        if (!$productType) return response()->json([]);
        $processes = Process::where('product_type_id', $productType->id)->pluck('name');
        return response()->json($processes);
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'route_no' => 'required',
            'route_date' => 'required|date',
            'project_start_date' => 'required|date',
            'priority' => 'required|in:High,Medium,Low',
        ]);

        DB::beginTransaction();
        try {
            // Create Route Card
            $card = RouteCard::create([
                'route_no' => $request->route_no,
                'route_date' => $request->route_date,
                'grn_no' => $request->grn_no,
                'grn_date' => $request->grn_date,
                'order_no' => $request->order_no,
                'order_date' => $request->order_date,
                'company_name' => $request->company_name,
                'company_address' => $request->company_address,
                'company_gstn' => $request->company_gstn,
                'company_email' => $request->company_email,
                'company_phone' => $request->company_phone,
                'order_ref_no' => $request->order_ref_no,
                'quotation_no' => $request->quotation_no,
                'quotation_date' => $request->quotation_date,
                'rfq_no' => $request->rfq_no,
                'rfq_date' => $request->rfq_date,
                'part_no' => $request->part_no,
                'project_material_no' => $request->project_material_no,
                'drawing_no' => $request->drawing_no,
                'drawing_rev' => $request->drawing_rev,
                'description' => $request->description,
                'product_type' => $request->product_type,
                'project_start_date' => $request->project_start_date,
                'priority' => $request->priority,
                'status' => 'Planned', // Default to planned
                'approval_status' => 'Pending', // Needs approval
                'created_by' => auth()->id() ?? 'system',
            ]);

            // Create processes map to ID for linking
            $processIds = [];
            
            if ($request->processes && is_array($request->processes)) {
                // First Pass: Create all processes
                foreach ($request->processes as $idx => $proc) {
                    $record = $card->processes()->create([
                        'process' => $proc['process'] ?? ($proc['process_other'] ?? ''),
                        'machine' => $proc['machine'] ?? '',
                        'operator' => $proc['operator'] ?? '',
                        'cycle_hours' => intval($proc['cycle_hours'] ?? 0),
                        'cycle_minutes' => intval($proc['cycle_minutes'] ?? 0),
                        'source' => $proc['source'] ?? 'inhouse',
                        'description' => $proc['description'] ?? '',
                        // Dates will be calculated by Service
                        'start_date' => now(), 
                        'end_date' => now(),
                    ]);
                    $processIds[$idx + 1] = $record->id;
                }

                // Second Pass: Link dependencies
                foreach ($request->processes as $idx => $proc) {
                    $previous_process_idx = isset($proc['previous_process']) && $proc['previous_process'] !== ''
                        ? intval($proc['previous_process'])
                        : null;
                    
                    if ($previous_process_idx && isset($processIds[$previous_process_idx])) {
                        $p = RouteCardProcess::find($processIds[$idx + 1]);
                        $p->previous_process_id = $processIds[$previous_process_idx];
                        $p->save();
                    }
                }
            }

            DB::commit();

            // Trigger Smart Scheduler
            // Only if it's approved? User requirement says "Create plan logic... High priority plans...".
            // Since we set approval_status=Pending, maybe we don't schedule yet? 
            // BUT user wants to see conflicts. Let's schedule it temporarily or Draft?
            // "All the approved & Unplanned SO’s should be displayed... Create plan logic..."
            // Let's assume creating the Plan puts it in queue.
            // For now, let's run rescheduling to get initial dates.
            $service = new \App\Services\PlanningService();
            $service->recalculateSchedule();

            return redirect()->route('manager.routecard.routecardorder')->with('success', 'Production Plan created and scheduled!');
        } catch (Exception $e) {
            DB::rollBack();
            return back()->withErrors('Error creating Plan: ' . $e->getMessage())->withInput();
        }
    }

    public function show($id)
    {
        $card = RouteCard::with('processes')->findOrFail($id);
        $machines = Machines::all();    // For machine name display
        $employees = EmployeeDetails::all();   // For operator name display
        return view('manager.routecard.show', compact('card', 'machines', 'employees'));
    }

    public function edit($id)
    {
        $card = RouteCard::with('processes')->findOrFail($id);
        $companies = CustomerVendor::all();
        $machines = Machines::all();
        return view('manager.routecard.edit', compact('card', 'companies', 'machines'));
    }

    public function update(Request $request, $id)
    {
        $validated = $request->validate([
            'route_no' => 'required',
            'route_date' => 'required|date',
        ]);

        $card = RouteCard::findOrFail($id);
        $card->update($request->except('processes'));

        // Remove old processes
        $card->processes()->delete();

        // Add new/edited processes (including previous_process_id, description, start_date, end_date)
        if ($request->processes && is_array($request->processes)) {
            foreach ($request->processes as $proc) {
                $card->processes()->create([
                    'process' => $proc['process'] ?? '',
                    'machine' => $proc['machine'] ?? '',
                    'operator' => $proc['operator'] ?? '',
                    'cycle_hours' => $proc['cycle_hours'] ?? 0,
                    'cycle_minutes' => $proc['cycle_minutes'] ?? 0,
                    'source' => $proc['source'] ?? 'inhouse',
                    'description' => $proc['description'] ?? '',
                    'start_date' => $proc['start_date'] ?? null,
                    'end_date' => $proc['end_date'] ?? null,
                    'previous_process_id' => $proc['previous_process_id'] ?? null,
                ]);
            }
        }

        return redirect()->route('manager.routecard.routecarddetails')->with('success', 'Route Card updated successfully!');
    }


    public function destroy($id)
    {
        $card = RouteCard::findOrFail($id);
        $card->processes()->delete();
        $card->delete();
        return response()->json([
            'success' => true,
            'message' => 'Route Card deleted successfully!',
        ]);
    }
    // In ManagerRouteCardController

    public function updateProcess(Request $request, $id)
    {
        $process = RouteCardProcess::findOrFail($id);
        $data = $request->all();
        $process->update($data);
        return response()->json(['success' => true, 'process' => $process]);
    }

    public function storeProcess(Request $request)
{
    // Parse cycle hours/minutes
    $hours = $request->ct_hours ?? $request->cycle_hours ?? 0;
    $minutes = $request->ct_minutes ?? $request->cycle_minutes ?? 0;
    $durationMinutes = (int)$hours * 60 + (int)$minutes;

    $startDate = $request->start_date
        ? \Carbon\Carbon::parse($request->start_date)
        : now();
    $endDate = $startDate->copy()->addMinutes($durationMinutes);

    // --- Conflict check: block if operator/machine double-booked ---
    $conflict = $this->checkProcessConflicts(
        $request->machine,
        $request->operator,
        $startDate,
        $endDate
    );
    if ($conflict) {
        \Log::warning($conflict['message']);
        return response()->json(['success' => false, 'message' => $conflict['message']], 422);
    }

    // --- Create the process ---
    $data = $request->all();
    $data['cycle_hours'] = $hours;
    $data['cycle_minutes'] = $minutes;
    $data['start_date'] = $startDate->format('Y-m-d H:i');
    $data['end_date'] = $endDate->format('Y-m-d H:i');

    $process = \App\Models\RouteCardProcess::create($data);
    \Log::info('Process created', ['process' => $process->toArray()]);

    // Reschedule logic (if needed, like in update)
    $this->globalRescheduleFrom($process->id);

    return response()->json(['success' => true, 'process' => $process]);
}



    public function deleteProcess($id)
    {
        $process = RouteCardProcess::findOrFail($id);
        $process->delete();
        return response()->json(['success' => true]);
    }
    /**
     * Get the next available time for a machine across all route card processes.
     */
    protected function getMachineAvailableTime($machine, $proposedStart, $durationMinutes, $skipProcessId = null)
    {
        $conflicts = RouteCardProcess::where('machine', $machine)
            ->when($skipProcessId, function ($q) use ($skipProcessId) {
                $q->where('id', '!=', $skipProcessId);
            })
            ->where(function ($q) use ($proposedStart, $durationMinutes) {
                $start = Carbon::parse($proposedStart);
                $end = $start->copy()->addMinutes($durationMinutes);
                $q->where(function ($w) use ($start, $end) {
                    $w->where('start_date', '<', $end)
                        ->where('end_date', '>', $start);
                });
            })
            ->orderBy('end_date', 'desc')
            ->first();

        if ($conflicts) {
            return Carbon::parse($conflicts->end_date);
        }
        return Carbon::parse($proposedStart);
    }

    /**
     * Adjust to office hours (returns Carbon).
     */
    protected function adjustToOfficeHours(Carbon $start)
    {
        $officeStart = $start->copy()->setTimeFromTimeString($this->officeStart);
        $officeEnd = $start->copy()->setTimeFromTimeString($this->officeEnd);

        if ($start->lt($officeStart)) {
            return $officeStart;
        }
        if ($start->gte($officeEnd)) {
            return $start->copy()->addDay()->setTimeFromTimeString($this->officeStart);
        }
        return $start;
    }

    /**
     * Schedules an end time within office hours.
     */
    protected function scheduleWithinOfficeHours(Carbon $start, $durationMinutes)
    {
        $current = $this->adjustToOfficeHours($start->copy());
        $minutesLeft = $durationMinutes;

        while ($minutesLeft > 0) {
            $officeEndToday = $current->copy()->setTimeFromTimeString($this->officeEnd);
            $minutesThisDay = min($minutesLeft, $current->diffInMinutes($officeEndToday, false));
            if ($minutesThisDay <= 0) {
                $current = $current->copy()->addDay()->setTimeFromTimeString($this->officeStart);
                continue;
            }
            $current = $current->copy()->addMinutes($minutesThisDay);
            $minutesLeft -= $minutesThisDay;
            if ($minutesLeft > 0) {
                $current = $current->copy()->addDay()->setTimeFromTimeString($this->officeStart);
            }
        }
        return [$start, $current];
    }
    /**
 * Check if there is a machine or operator conflict for a given time slot.
 * Returns null if no conflict, or an array with conflict details.
 */
private function checkProcessConflicts($machine, $operator, $start, $end, $ignoreId = null)
{
    // 1. Machine conflict
    if ($machine) {
        $machineConflict = \App\Models\RouteCardProcess::where('machine', $machine)
            ->when($ignoreId, function ($q) use ($ignoreId) {
                $q->where('id', '!=', $ignoreId);
            })
            ->where(function ($q) use ($start, $end) {
                $q->where('start_date', '<', $end)
                  ->where('end_date', '>', $start);
            })->first();
        if ($machineConflict) {
            return [
                'type' => 'machine',
                'conflict' => $machineConflict,
                'message' => "Conflict: Machine '{$machine}' is already scheduled for process '{$machineConflict->process}' (Route Card: ".($machineConflict->route_card_id).") from {$machineConflict->start_date} to {$machineConflict->end_date}."
            ];
        }
    }

    // 2. Operator conflict
    if ($operator) {
        $operatorConflict = \App\Models\RouteCardProcess::where('operator', $operator)
            ->when($ignoreId, function ($q) use ($ignoreId) {
                $q->where('id', '!=', $ignoreId);
            })
            ->where(function ($q) use ($start, $end) {
                $q->where('start_date', '<', $end)
                  ->where('end_date', '>', $start);
            })->first();
        if ($operatorConflict) {
            $operatorName = \App\Models\EmployeeDetails::find($operator)->name ?? $operator;
            return [
                'type' => 'operator',
                'conflict' => $operatorConflict,
                'message' => "Conflict: Operator '{$operatorName}' is already scheduled for process '{$operatorConflict->process}' (Route Card: ".($operatorConflict->route_card_id).") from {$operatorConflict->start_date} to {$operatorConflict->end_date}."
            ];
        }
    }

    return null;
}


    /**
     * Reschedule this process and all downstream processes recursively.
     */
    protected function rescheduleProcesses($process, $visited = [])
    {
        if ($process->source === 'outsourced') return;
        if (in_array($process->id, $visited)) return;
        $visited[] = $process->id;

        // Find when this process can start:
        $prevEnd = null;
        if ($process->previous_process_id) {
            $prevProc = RouteCardProcess::find($process->previous_process_id);
            $prevEnd = $prevProc ? Carbon::parse($prevProc->end_date) : null;
        }
        $proposedStart = $prevEnd ?: Carbon::parse($process->start_date);

        // Machine check: find when this machine is free (any RouteCardProcess, except this one)
        $duration = ((int)$process->cycle_hours * 60) + (int)$process->cycle_minutes;
        $machineAvailable = $this->getMachineAvailableTime($process->machine, $proposedStart, $duration, $process->id);

        // Office hours: can't start before 09:00, can't end after 18:30
        $start = max($proposedStart, $machineAvailable);
        $start = $this->adjustToOfficeHours($start);

        // End time (office hours aware)
        [$realStart, $realEnd] = $this->scheduleWithinOfficeHours($start, $duration);

        // Save changes
        $process->start_date = $realStart->format('Y-m-d H:i');
        $process->end_date = $realEnd->format('Y-m-d H:i');
        $process->save();

        // Now reschedule all processes where this process is the previous_process
        $children = RouteCardProcess::where('previous_process_id', $process->id)->get();
        foreach ($children as $child) {
            if ($child->source !== 'outsourced') {
                $this->rescheduleProcesses($child, $visited);
            }
        }
    }


    /**
     * Update a process and reschedule downstream.
     */
    // public function updateProcessAndReschedule(Request $request, $id)
    // {
    //     $process = RouteCardProcess::findOrFail($id);

    //     // Update the process being edited
    //     $process->fill([
    //         'process' => $request->process,
    //         'description' => $request->description,
    //         'machine' => $request->machine,
    //         'operator' => $request->operator,
    //         'cycle_hours' => $request->ct_hours ?? $request->cycle_hours,
    //         'cycle_minutes' => $request->ct_minutes ?? $request->cycle_minutes,
    //         'source' => $request->source,
    //         'previous_process_id' => $request->previous_process_id,
    //         'start_date' => $request->start_date,
    //     ]);
    //     $process->save();

    //     // Reschedule this process and downstream within the same RC
    //     //$this->rescheduleProcesses($process);

    //     // Call the global rescheduler to ensure *all* planned, approved RCs are scheduled to avoid conflicts!
    //     $this->globalRescheduleFrom($process->id);

    //     // ---- NEW LOGIC STARTS HERE ----
    //     // Find all subsequent Planned RouteCards, in order
    //     $currentRC = $process->route_card_id;

    //     // Only reschedule for "Planned" Route Cards, ordered by route_date
    //     $plannedRCs = RouteCard::where('status', 'Planned')
    //         ->whereNotNull('approved_at')
    //         ->orderBy('route_date')
    //         ->get();

    //     $rcIds = $plannedRCs->pluck('id')->toArray();
    //     $currentIdx = array_search($currentRC, $rcIds);

    //     // For every RC after the current one, replan all its processes
    //     for ($i = $currentIdx + 1; $i < count($rcIds); $i++) {
    //         $rcId = $rcIds[$i];
    //         // Only reschedule if still Planned (avoid any others)
    //         $rc = RouteCard::find($rcId);
    //         if ($rc && $rc->status === 'Planned' && $rc->approved_at) {
    //             $this->rescheduleEntireRouteCard($rcId);
    //         }
    //     }

    //     return response()->json(['success' => true]);
    // }
    public function updateProcessAndReschedule(Request $request, $id)
{
     try {
    \Log::info("updateProcessAndReschedule called for process ID: $id", ['input' => $request->all()]);
    $process = \App\Models\RouteCardProcess::findOrFail($id);

    // Parse cycle hours/minutes
    $hours = $request->ct_hours ?? $request->cycle_hours ?? 0;
    $minutes = $request->ct_minutes ?? $request->cycle_minutes ?? 0;
    $durationMinutes = (int)$hours * 60 + (int)$minutes;

    // Get requested start date (if empty, fallback to DB value)
    $startDate = $request->start_date
        ? \Carbon\Carbon::parse($request->start_date)
        : \Carbon\Carbon::parse($process->start_date);

    $endDate = $startDate->copy()->addMinutes($durationMinutes);

    // --- Conflict check: block if operator/machine double-booked ---
    $conflict = $this->checkProcessConflicts(
        $request->machine,
        $request->operator,
        $startDate,
        $endDate,
        $id
    );
    if ($conflict) {
        \Log::warning($conflict['message']);
        return response()->json(['success' => false, 'message' => $conflict['message']], 422);
    }

    // --- Update the process ---
    $process->fill([
        'process' => $request->process,
        'description' => $request->description,
        'machine' => $request->machine,
        'operator' => $request->operator,
        'cycle_hours' => $hours,
        'cycle_minutes' => $minutes,
        'source' => $request->source,
        'previous_process_id' => $request->previous_process_id,
        'start_date' => $startDate->format('Y-m-d H:i'),
        'end_date' => $endDate->format('Y-m-d H:i'),
    ]);
    $process->save();
    \Log::info('Process updated', ['updated_process' => $process->toArray()]);
    
    // Reschedule logic (as you already have)
    $this->globalRescheduleFrom($process->id);


    \Log::info("Rescheduling done for process $id");
    return response()->json([
            'success' => true,
            'message' => 'Process updated and schedule recalculated successfully!'
        ]);
    } catch (\Exception $e) {
        \Log::error("Process update failed: ".$e->getMessage());
        return response()->json([
            'success' => false,
            'message' => 'Error updating process: '.$e->getMessage()
        ], 500);
    }
}

    protected function rescheduleEntireRouteCard($rcId)
    {
        $rc = RouteCard::with(['processes' => function ($q) {
            $q->orderBy('id'); // Or order by your process order
        }])->find($rcId);

        if (!$rc) return;

        // For each process in this RC, reschedule based on machine availability after previous_process (or start of RC)
        foreach ($rc->processes as $proc) {

            if ($proc->source === 'outsourced') continue;
            $prevEnd = null;
            if ($proc->previous_process_id) {
                $prevProc = $rc->processes->where('id', $proc->previous_process_id)->first();
                $prevEnd = $prevProc ? Carbon::parse($prevProc->end_date) : null;
            }

            // If first process, use either its current start_date or the end of last job on its machine
            $proposedStart = $prevEnd ?: Carbon::parse($rc->project_start_date ?? $proc->start_date);

            // Machine availability check: this will push process start to after last booked process (any route card!)
            $duration = ((int)$proc->cycle_hours * 60) + (int)$proc->cycle_minutes;
            $machineAvailable = $this->getMachineAvailableTime($proc->machine, $proposedStart, $duration, $proc->id);

            // Office hours adjustment
            $start = max($proposedStart, $machineAvailable);
            $start = $this->adjustToOfficeHours($start);

            [$realStart, $realEnd] = $this->scheduleWithinOfficeHours($start, $duration);

            $proc->start_date = $realStart->format('Y-m-d H:i');
            $proc->end_date = $realEnd->format('Y-m-d H:i');
            $proc->save();
        }
    }

    public function globalRescheduleFrom($changedProcessId)
{
    // 1. Get all RCs in status 'Planned' and approved_at != null, sorted by route_date/project_start_date
    $rcs = RouteCard::with(['processes' => function ($q) {
        $q->orderBy('id');
    }])
        ->where('status', 'Planned')
        ->whereNotNull('approved_at')
        ->orderBy('project_start_date')
        ->orderBy('route_date')
        ->get();

    $machineTimeline = [];
    $operatorTimeline = [];

    foreach ($rcs as $rc) {
        $projectStart = Carbon::parse($rc->project_start_date ?? $rc->route_date);

        foreach ($rc->processes as $proc) {
            if (strtolower($proc->source) === 'outsourced') continue;

            // Determine earliest allowed start (by dependency and project start)
            $start = null;
            if ($proc->previous_process_id) {
                $prev = RouteCardProcess::find($proc->previous_process_id);
                if ($prev) $start = Carbon::parse($prev->end_date);
            } else {
                $start = $projectStart->copy();
            }

            // Also block by latest machine/operator
            if ($proc->machine && isset($machineTimeline[$proc->machine])) {
                $start = $start ? max($start, $machineTimeline[$proc->machine]) : $machineTimeline[$proc->machine];
            }
            if ($proc->operator && isset($operatorTimeline[$proc->operator])) {
                $start = $start ? max($start, $operatorTimeline[$proc->operator]) : $operatorTimeline[$proc->operator];
            }

            // Ensure process never starts before RC project start
            $start = max($start, $projectStart);

            // Adjust for office hours
            $start = $this->adjustToOfficeHours($start);
            $duration = ((int)$proc->cycle_hours * 60) + (int)$proc->cycle_minutes;
            [$realStart, $realEnd] = $this->scheduleWithinOfficeHours($start, $duration);

            if (
                $proc->start_date != $realStart->format('Y-m-d H:i') ||
                $proc->end_date != $realEnd->format('Y-m-d H:i')
            ) {
                $proc->start_date = $realStart->format('Y-m-d H:i');
                $proc->end_date = $realEnd->format('Y-m-d H:i');
                $proc->save();
            }

            // Update timelines
            if ($proc->machine) $machineTimeline[$proc->machine] = $realEnd->copy();
            if ($proc->operator) $operatorTimeline[$proc->operator] = $realEnd->copy();
        }
    }
}


    public function markPlanned($id)
    {
        $card = RouteCard::findOrFail($id);
        $card->status = 'Planned';
        $card->save();

        Log::info("RouteCard {$card->route_no} marked as Planned");
        return response()->json(['success' => true]);
    }

    public function plannedList()
    {
        $planned = RouteCard::where('status', 'Planned')->orderByDesc('route_date')->get();
        return response()->json($planned);
    }
    public function markReadyProduction($id)
    {
        $card = RouteCard::findOrFail($id);
        $card->status = 'Ready to Production';
        $card->save();

        Log::info("RouteCard {$card->route_no} marked as Ready to Production");
        return response()->json(['success' => true]);
    }

    public function readyToProductionList()
    {
        $cards = RouteCard::with(['processes' => function ($q) {
            $q->orderBy('id');
        }])->where('status', 'Ready to Production')->orderByDesc('route_date')->get();

        // Optionally: Attach operator names
        $operatorMap = EmployeeDetails::pluck('name', 'id')->toArray();
        foreach ($cards as $rc) {
            foreach ($rc->processes as $p) {
                $p->operator_name = $p->operator && isset($operatorMap[$p->operator]) ? $operatorMap[$p->operator] : '-';
            }
        }
        return response()->json($cards);
    }

    // Mark as In Production
    public function markInProduction($id)
    {
        $rc = RouteCard::findOrFail($id);
        $rc->status = 'In Production';
        $rc->save();

        \Log::info("RouteCard {$rc->route_no} marked as In Production");
        return response()->json(['success' => true]);
    }
}
