HEX
Server: Apache/2.4.52 (Ubuntu)
System: Linux ip-10-0-8-47 6.8.0-1021-aws #23~22.04.1-Ubuntu SMP Tue Dec 10 16:31:58 UTC 2024 aarch64
User: ubuntu (1000)
PHP: 8.1.2-1ubuntu2.22
Disabled: NONE
Upload Files
File: /var/www/javago-portal-updates/app/Http/Controllers/API/CafeOrdersController.php
<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use App\Models\GroupCoffeeRun;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use App\Models\Cafe;
use App\Models\CafeMenu;
use App\Models\CafeMenuItem;
use App\Models\CafeMenuItemSize;
use App\Models\Order;
use App\Models\User;
use App\Models\SuggestedItem;
use App\Services\CafeOrderHistoryService;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;
use Illuminate\Support\Facades\Hash;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Notifications\Notification;
use Kreait\Firebase\Factory;
use Kreait\Firebase\Messaging\CloudMessage;
use Kreait\Firebase\Messaging\Notification as FirebaseNotification;
use App\Models\Notification as NotificationModel;
use Illuminate\Support\Str;

class CafeOrdersController extends Controller
{

    //get orders
    public function getOrders(Request $request)
    {
        try {
            $cafe = Cafe::find($request->user->id);
            if (!$cafe) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'Cafe not found.',
                ], 404);
            }

            $orderFrom = $request->orderFrom ? Carbon::createFromFormat('Y-m-d', $request->orderFrom)->startOfDay()->timestamp : null;
            $orderTo = $request->orderTo ? Carbon::createFromFormat('Y-m-d', $request->orderTo)->endOfDay()->timestamp : null;
            $userId = $request->user_id ?? null;
            $today = $request->today ? true : false;
            $cafeId = $cafe->id;

            // -----------------------------
            // Fetch INDIVIDUAL Orders
            // -----------------------------
            $individualOrders = Order::join('users', 'users.id', 'orders.user_id')
                ->select('orders.id', 'orders.order_number', 'orders.status', 'orders.user_id', 'users.name', 'orders.order_placed_at', 'orders.total_amount', 'orders.order_item_array', 'orders.order_completed', 'orders.is_individual_order', 'orders.order_completed', 'orders.refunded_order_items_array', 'orders.refunded_amount', 'orders.is_full_order_cancelled', 'orders.refund_status')
                ->where('orders.cafe_id', $cafeId)
                ->where('orders.is_individual_order', 1)
                ->when($orderFrom, fn($q) => $q->where('orders.order_placed_at', '>=', $orderFrom))
                ->when($orderTo, fn($q) => $q->where('orders.order_placed_at', '<=', $orderTo))
                ->when($userId, fn($q) => $q->where('orders.user_id', $userId))
                ->when($today, fn($q) => $q->whereBetween('orders.order_placed_at', [Carbon::today()->startOfDay()->timestamp, Carbon::today()->endOfDay()->timestamp]))
                ->orderBy('orders.order_placed_at', 'desc')
                ->get();

            foreach ($individualOrders as $order) {
                $order->order_item_array = $this->formatOrderItems($order->order_item_array);
                $order->order_type = 'individual';
            }

            // -----------------------------
            // Fetch GROUP Coffee Run Orders
            // -----------------------------
            $groupCoffeeRunOrders = DB::table('group_coffee_runs')
                ->leftJoin('orders', 'orders.group_coffee_run_id', '=', 'group_coffee_runs.request_unique_id')
                ->leftJoin('users', 'users.id', '=', 'orders.user_id')
                ->leftJoin('users as creator_users', 'creator_users.id', '=', 'group_coffee_runs.request_created_by') // for request creator
                ->select(
                    'group_coffee_runs.request_unique_id',
                    'group_coffee_runs.created_at as run_created_at',
                    'group_coffee_runs.request_created_by',
                    'creator_users.name as request_created_by_name',
                    'orders.id',
                    'orders.order_number',
                    'orders.status',
                    'orders.user_id',
                    'users.name',
                    'orders.order_placed_at',
                    'orders.total_amount',
                    'orders.order_item_array',
                    'orders.order_completed',
                    'orders.is_individual_order',
                    'orders.order_completed',
                    'orders.refunded_order_items_array',
                    'orders.refunded_amount',
                    'orders.is_full_order_cancelled',
                    'orders.refund_status'
                )
                ->where('group_coffee_runs.cafe_id', $cafeId)
                ->where('group_coffee_runs.type', 1)
                ->where('orders.is_timer_completed', 1)
                ->orderBy('orders.id', 'desc')
                ->get();


            // Group orders by group_coffee_run_id
            $groupedOrders = [];

            foreach ($groupCoffeeRunOrders as $order) {
                // Skip if no valid order ID (i.e., no match from LEFT JOIN)
                if (!$order->id)
                    continue;

                // Format items (avoid null item array)
                $order->order_item_array = $this->formatOrderItems($order->order_item_array ?? '[]');
                $order->order_type = 'group';

                // Group by request_unique_id
                $groupedOrders[$order->request_unique_id][$order->id] = $order;
            }

            // Now reset indexes so the output is clean arrays (not associative)
            foreach ($groupedOrders as &$group) {
                $group = array_values($group);
            }

            return response()->json([
                'status' => 'success',
                'message' => 'Orders retrieved successfully.',
                'individualOrders' => $individualOrders,
                'groupCoffeeRunOrders' => $groupedOrders,
            ], 200);

        } catch (ValidationException $validationException) {
            return response()->json([
                'status' => 'error',
                'message' => 'Validation failed.',
                'errors' => $validationException->errors(),
            ], 422);
        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'message' => 'An error occurred while retrieving orders.',
                'error' => $e->getMessage(),
            ], 500);
        }
    }


    private function formatOrderItems($orderItemArray)
    {
        if (empty($orderItemArray))
            return [];

        $items = is_string($orderItemArray) ? json_decode($orderItemArray, true) : $orderItemArray;

        if (!is_array($items))
            return [];

        return array_map(function ($item) {
            return [
                'item_id' => $item['item_id'] ?? null,
                'item_name' => $item['item_name'] ?? '',
                'item_size' => $item['item_size'] ?? '',
                'item_image' => $item['item_image'] ?? '',
                'item_amount' => number_format((float) ($item['item_amount'] ?? 0), 2),
                'item_quantity' => $item['item_quantity'] ?? 1,
                'item_description' => $item['item_description'] ?? '',
                'addon_sizes' => !empty($item['addon_sizes']) ? array_map(function ($addon) {
                    return [
                        'addon_name' => $addon['addon_name'] ?? '',
                        'addon_size_name' => $addon['addon_size_name'] ?? '',
                        'addon_size_price' => number_format((float) ($addon['addon_size_price'] ?? 0), 2),
                    ];
                }, $item['addon_sizes']) : [],
            ];
        }, $items);
    }


    //get today orders
    public function getTodayOrders(Request $request)
    {
        try {
            $cafe = Cafe::find($request->user->id);
            if (!$cafe) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'Cafe not found.',
                ], 404);
            }

            $userId = $request->user_id ?? null;
            $today = true;
            $cafeId = $cafe->id;

            // -----------------------------
            // Fetch INDIVIDUAL Orders
            // -----------------------------
            $individualOrders = Order::join('users', 'users.id', 'orders.user_id')
                ->select('orders.id', 'orders.order_number', 'orders.status', 'orders.user_id', 'users.name', 'orders.order_placed_at', 'orders.total_amount', 'orders.order_item_array', 'orders.order_completed', 'orders.is_individual_order', 'orders.order_completed', 'orders.estimated_arival_time', 'orders.refunded_order_items_array', 'orders.refunded_amount', 'orders.is_full_order_cancelled', 'orders.refund_status')
                ->where('orders.cafe_id', $cafeId)
                ->where('orders.is_individual_order', 1)
                ->when($userId, fn($q) => $q->where('orders.user_id', $userId))
                ->when($today, fn($q) => $q->whereBetween('orders.order_placed_at', [Carbon::today()->startOfDay()->timestamp, Carbon::today()->endOfDay()->timestamp]))
                ->orderBy('orders.order_placed_at', 'desc')
                ->get();

            foreach ($individualOrders as $order) {
                $order->order_item_array = $this->formatOrderItems($order->order_item_array);
                $order->order_type = 'individual';
            }

            // -----------------------------
            // Fetch GROUP Coffee Run Orders
            // -----------------------------
            $groupCoffeeRunOrders = DB::table('group_coffee_runs')
                ->leftJoin('orders', 'orders.group_coffee_run_id', '=', 'group_coffee_runs.request_unique_id')
                ->leftJoin('users', 'users.id', '=', 'orders.user_id')
                ->leftJoin('users as creator_users', 'creator_users.id', '=', 'group_coffee_runs.request_created_by') // for request creator
                ->leftJoin(DB::raw('(SELECT group_coffee_run_id, COUNT(*) as order_count FROM orders GROUP BY group_coffee_run_id) as order_counts'), function ($join) {
                    $join->on('group_coffee_runs.request_unique_id', '=', 'order_counts.group_coffee_run_id');
                })
                ->select(
                    'group_coffee_runs.request_unique_id',
                    'group_coffee_runs.created_at as run_created_at',
                    'group_coffee_runs.request_created_by',
                    'creator_users.name as request_created_by_name',
                    'orders.id',
                    'orders.order_number',
                    'orders.status',
                    'orders.user_id',
                    'users.name',
                    'orders.order_placed_at',
                    'orders.total_amount',
                    'orders.order_item_array',
                    'orders.order_completed',
                    'orders.is_individual_order',
                    'orders.order_completed',
                    'orders.estimated_arival_time',
                    'orders.refunded_order_items_array',
                    'orders.refunded_amount',
                    'orders.is_full_order_cancelled',
                    'orders.refund_status',
                    'order_counts.order_count'
                )
                ->when($today, fn($q) => $q->whereBetween('orders.order_placed_at', [Carbon::today()->startOfDay()->timestamp, Carbon::today()->endOfDay()->timestamp]))
                ->where('group_coffee_runs.cafe_id', $cafeId)
                ->where('group_coffee_runs.type', 1)
                ->where('orders.is_timer_completed', 1)
                ->orderBy('orders.id', 'desc')
                ->get();


            // Group orders by group_coffee_run_id
            $groupedOrders = [];
            if ($groupCoffeeRunOrders->isEmpty()) {
                $groupedOrders = new \stdClass();

            }

            foreach ($groupCoffeeRunOrders as $order) {
                // Skip if no valid order ID (i.e., no match from LEFT JOIN)
                if (!$order->id)
                    continue;

                // Format items (avoid null item array)
                $order->order_item_array = $this->formatOrderItems($order->order_item_array ?? '[]');
                $order->order_type = 'group';

                // Group by request_unique_id
                $groupedOrders[$order->request_unique_id][$order->id] = $order;
            }

            // Now reset indexes so the output is clean arrays (not associative)
            // foreach ($groupedOrders as &$group) {
            //     $group = array_values($group);
            // }
            foreach ($groupedOrders as $runId => &$group) {
                $group = collect($group)->map(function ($order) {
                    return (object) [
                        'id' => $order->id,
                        'order_number' => $order->order_number,
                        'request_unique_id' => $order->request_unique_id,
                        'request_created_by_name' => $order->request_created_by_name,
                        'status' => $order->status,
                        'user_id' => $order->user_id,
                        'name' => $order->name,
                        'order_placed_at' => $order->order_placed_at,
                        'total_amount' => $order->total_amount,
                        'order_item_array' => $order->order_item_array,
                        'order_completed' => $order->order_completed,
                        'is_individual_order' => $order->is_individual_order,
                        'order_type' => 'group', // unified format
                        'estimated_arival_time' => $order->estimated_arival_time,
                        'refunded_order_items_array' => $order->refunded_order_items_array,
                        'refunded_amount' => $order->refunded_amount,
                        'is_full_order_cancelled' => $order->is_full_order_cancelled,
                        'refund_status' => $order->refund_status,
                        'orders' => $order->order_count,
                    ];
                })->values()->all(); // Ensures indexed array
            }


            return response()->json([
                'status' => 'success',
                'message' => 'Orders retrieved successfully.',
                'individualOrders' => $individualOrders,
                'groupCoffeeRunOrders' => $groupedOrders,
            ], 200);

        } catch (ValidationException $validationException) {
            return response()->json([
                'status' => 'error',
                'message' => 'Validation failed.',
                'errors' => $validationException->errors(),
            ], 422);
        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'message' => 'An error occurred while retrieving orders.',
                'error' => $e->getMessage(),
            ], 500);
        }
    }

    //get order details
    public function getOrderDetailsold(Request $request)
    {
        try {
            $validator = Validator::make($request->all(), [
                'id' => 'required',
                'is_individual_order' => 'required|boolean|in:0,1',
            ]);

            if ($validator->fails()) {
                throw new ValidationException($validator);
            }

            $cafe = Cafe::find($request->user->id);
            if (!$cafe) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'Cafe not found.',
                ], 404);
            }

            // INDIVIDUAL ORDER
            if ($request->is_individual_order == 1) {
                $order = Order::with('user:id,name')->where('id', $request->id)->first();

                if (!$order) {
                    return response()->json(['error' => 'Order not found'], 404);
                }

                $orderItems = json_decode($order->order_item_array, true) ?? [];

                foreach ($orderItems as &$item) {
                    $item['addon_sizes'] = !empty($item['addon_sizes']) ? array_map(function ($addon) {
                        return [
                            'addon_name' => $addon['addon_name'],
                            'addon_size_name' => $addon['addon_size_name'],
                            'addon_size_price' => number_format((float) $addon['addon_size_price'], 2),
                        ];
                    }, $item['addon_sizes']) : [];

                    $item['customer_name'] = $order->user->name ?? 'Unknown';
                    $item['order_number'] = $order->order_number ?? null;
                }

                return response()->json([
                    'status' => 'success',
                    'message' => 'Order details found successfully!',
                    'orders' => $orderItems
                ], 200);
            }

            // GROUP ORDER
            else {
                $orders = Order::with('user:id,name')
                    ->rightJoin('group_coffee_runs', 'group_coffee_runs.request_unique_id', '=', 'orders.group_coffee_run_id')
                    ->leftJoin('users as creator_users', 'creator_users.id', '=', 'group_coffee_runs.request_created_by')
                    ->where('orders.group_coffee_run_id', $request->id)
                    ->groupBy('orders.id')
                    ->get([
                        'orders.*',
                        'group_coffee_runs.request_unique_id',
                        'creator_users.name as creator_name'
                    ]);

                if ($orders->isEmpty()) {
                    return response()->json(['error' => 'Group order not found'], 404);
                }

                $orderItemsList = [];

                foreach ($orders as $order) {
                    $items = json_decode($order->order_item_array, true) ?? [];

                    foreach ($items as &$item) {
                        $item['addon_sizes'] = !empty($item['addon_sizes']) ? array_map(function ($addon) {
                            return [
                                'addon_name' => $addon['addon_name'],
                                'addon_size_name' => $addon['addon_size_name'],
                                'addon_size_price' => number_format((float) $addon['addon_size_price'], 2),
                            ];
                        }, $item['addon_sizes']) : [];

                        $item['customer_name'] = $order->user->name ?? 'Unknown';
                        $item['creator_name'] = $order->creator_name ?? 'Unknown';
                        $item['request_unique_id'] = $order->request_unique_id ?? null;
                    }

                    $orderItemsList[] = $items;
                }

                $flattenedItems = collect($orderItemsList)->collapse()->all();

                return response()->json([
                    'status' => 'success',
                    'message' => 'Group order details found successfully!',
                    'orders' => $flattenedItems
                ], 200);
            }

        } catch (ValidationException $validationException) {
            return response()->json([
                'status' => 'error',
                'message' => 'Validation failed.',
                'errors' => $validationException->errors(),
            ], 422);
        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'message' => 'An error occurred while getting order details!',
                'error' => $e->getMessage(),
            ], 500);
        }
    }

    public function getOrderDetails(Request $request)
    {
        try {
            $validator = Validator::make($request->all(), [
                'id' => 'required',
                'is_individual_order' => 'required|boolean|in:0,1',
            ]);

            if ($validator->fails()) {
                throw new ValidationException($validator);
            }

            $cafe = Cafe::find($request->user->id);
            if (!$cafe) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'Cafe not found.',
                ], 404);
            }

            // INDIVIDUAL ORDER
            if ($request->is_individual_order == 1) {
                $order = Order::with('user:id,name')->where('id', $request->id)->first();

                if (!$order) {
                    return response()->json(['error' => 'Order not found'], 404);
                }

                $processedItems = json_decode($order->processed_order_items_array, true) ?? [];
                $refundedItems = json_decode($order->refunded_order_items_array, true) ?? [];

                $items = [];

                // if (empty($processedItems) && empty($refundedItems)) {
                //     $baseItems = json_decode($order->order_item_array, true) ?? [];
                //     foreach ($baseItems as &$item) {
                //         $item['status_tag'] = 'processed'; // default fallback tag
                //     }
                //     $items = $baseItems;
                // } else {
                //     foreach ($processedItems as &$item) {
                //         $item['status_tag'] = 'processed';
                //     }
                //     foreach ($refundedItems as &$item) {
                //         $item['status_tag'] = 'refunded';
                //     }
                //     $items = array_merge($processedItems, $refundedItems);
                // }

                if (empty($processedItems) && empty($refundedItems)) {
                    $baseItems = json_decode($order->order_item_array, true) ?? [];
                    foreach ($baseItems as &$item) {
                        $item['status_tag'] = 'processed'; // default fallback tag
                    }
                    $items = $baseItems;
                } else {
                    foreach ($processedItems as &$item) {
                        $item['status_tag'] = 'processed';
                    }

                    foreach ($refundedItems as &$item) {
                        $item = [
                            'item_name' => $item['name'] ?? '',
                            'item_amount' => $item['amount'] ?? 0,
                            'item_quantity' => $item['quantity'] ?? 1,
                            'fulfillment_uid' => $order->uid ?? '',
                            'state' => $order->state ?? 'COMPLETED',
                            'status_tag' => 'refunded',
                        ];
                    }

                    $items = array_merge($processedItems, $refundedItems);
                }

                foreach ($items as &$item) {
                    $item['addon_sizes'] = !empty($item['addon_sizes']) ? array_map(function ($addon) {
                        return [
                            'addon_name' => $addon['addon_name'],
                            'addon_size_name' => $addon['addon_size_name'],
                            'addon_size_price' => number_format((float) $addon['addon_size_price'], 2),
                        ];
                    }, $item['addon_sizes']) : [];

                    $item['customer_name'] = $order->user->name ?? 'Unknown';
                    $item['order_number'] = $order->order_number ?? null;
                }

                return response()->json([
                    'status' => 'success',
                    'message' => 'Order details found successfully!',
                    'orders' => $items
                ], 200);
            }

            // GROUP ORDER
            else {
                $orders = Order::with('user:id,name')
                    ->rightJoin('group_coffee_runs', 'group_coffee_runs.request_unique_id', '=', 'orders.group_coffee_run_id')
                    ->leftJoin('users as creator_users', 'creator_users.id', '=', 'group_coffee_runs.request_created_by')
                    ->where('orders.group_coffee_run_id', $request->id)
                    ->groupBy('orders.id')
                    ->get([
                        'orders.*',
                        'group_coffee_runs.request_unique_id',
                        'creator_users.name as creator_name'
                    ]);

                if ($orders->isEmpty()) {
                    return response()->json(['error' => 'Group order not found'], 404);
                }

                $orderItemsList = [];

                foreach ($orders as $order) {
                    $processedItems = json_decode($order->processed_order_items_array, true) ?? [];
                    $refundedItems = json_decode($order->refunded_order_items_array, true) ?? [];

                    $items = [];

                    // if (empty($processedItems) && empty($refundedItems)) {
                    //     $baseItems = json_decode($order->order_item_array, true) ?? [];
                    //     foreach ($baseItems as &$item) {
                    //         $item['status_tag'] = 'processed'; // fallback default
                    //     }
                    //     $items = $baseItems;
                    // } else {
                    //     foreach ($processedItems as &$item) {
                    //         $item['status_tag'] = 'processed';
                    //     }
                    //     foreach ($refundedItems as &$item) {
                    //         $item['status_tag'] = 'refunded';
                    //     }
                    //     $items = array_merge($processedItems, $refundedItems);
                    // }
                    if (empty($processedItems) && empty($refundedItems)) {
                        $baseItems = json_decode($order->order_item_array, true) ?? [];
                        foreach ($baseItems as &$item) {
                            $item['status_tag'] = 'processed'; // default fallback tag
                        }
                        $items = $baseItems;
                    } else {
                        foreach ($processedItems as &$item) {
                            $item['status_tag'] = 'processed';
                        }

                        foreach ($refundedItems as &$item) {
                            $item = [
                                'item_name' => $item['name'] ?? '',
                                'item_amount' => $item['amount'] ?? 0,
                                'item_quantity' => $item['quantity'] ?? 1,
                                'fulfillment_uid' => $order->uid ?? '',
                                'state' => $order->state ?? 'COMPLETED',
                                'status_tag' => 'refunded',
                            ];
                        }

                        $items = array_merge($processedItems, $refundedItems);
                    }

                    foreach ($items as &$item) {
                        $item['addon_sizes'] = !empty($item['addon_sizes']) ? array_map(function ($addon) {
                            return [
                                'addon_name' => $addon['addon_name'],
                                'addon_size_name' => $addon['addon_size_name'],
                                'addon_size_price' => number_format((float) $addon['addon_size_price'], 2),
                            ];
                        }, $item['addon_sizes']) : [];

                        $item['customer_name'] = $order->user->name ?? 'Unknown';
                        $item['creator_name'] = $order->creator_name ?? 'Unknown';
                        $item['request_unique_id'] = $order->request_unique_id ?? null;
                        $item['order_number'] = $order->order_number ?? null;
                    }

                    $orderItemsList[] = $items;
                }

                $flattenedItems = collect($orderItemsList)->collapse()->all();

                return response()->json([
                    'status' => 'success',
                    'message' => 'Group order details found successfully!',
                    'orders' => $flattenedItems
                ], 200);
            }

        } catch (ValidationException $validationException) {
            return response()->json([
                'status' => 'error',
                'message' => 'Validation failed.',
                'errors' => $validationException->errors(),
            ], 422);
        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'message' => 'An error occurred while getting order details!',
                'error' => $e->getMessage(),
            ], 500);
        }
    }



    //mark order completed
    public function markOrderCompleted(Request $request)
    {
        try {
            $validator = Validator::make($request->all(), [
                'id' => 'required',
                'is_individual_order' => 'required|boolean|in:0,1',
            ]);

            if ($validator->fails()) {
                throw new ValidationException($validator);
            }

            $messageText = 'Your order has been completed! Please collect it.';

            if ($request->is_individual_order == 1) {
                $order = Order::find($request->id);
                if (!$order) {
                    return response()->json(['error' => 'Order not found'], 404);
                }

                $order->order_completed = 1;

                // Example: Save processed items (just for example; adjust as needed)
                $processedItems = $order->items ? json_decode($order->items, true) : [];
                $order->processed_order_items_array = json_encode($processedItems);

                $order->save();

                $user = $order->user;

                // if ($user) {
                //     $this->sendOrderCompleteNotification($order);
                // }

                // Mark pickup on Square
                $squareResult = $this->pickupItemsOnSquare($order);
                if (!$squareResult['success']) {
                    \Log::error("Square pickup update failed for Order ID {$order->id}", ['error' => $squareResult['message']]);
                }

                // Check if first order for awarding stamps
                $existingOrders = Order::where('user_id', $user->id)->where('id', '!=', $order->id)->count();
                // if ($existingOrders == 0) {
                //     $user->remaining_awarded_stamps = 2;
                //     $user->save();

                //     if (!empty($user->referral_code_used)) {
                //         $referrer = User::where('referral_code', $user->referral_code_used)->first();
                //         if ($referrer) {
                //             $referrer->remaining_awarded_stamps += 2;
                //             $referrer->save();
                //         }
                //     }
                // }

            } else {
                $orders = Order::where('group_coffee_run_id', $request->id)->get();

                if ($orders->isEmpty()) {
                    return response()->json(['error' => 'Group orders not found'], 404);
                }

                foreach ($orders as $order) {
                    $order->order_completed = 1;
                    $order->save();

                    $user = $order->user;
                    $groupCoffeeRun = GroupCoffeeRun::where('request_unique_id', $order->group_coffee_run_id)
                        ->where('request_created_by', $user->id)
                        ->first();

                    // if ($groupCoffeeRun) {
                    //     \Log::info("Sending group coffee run completion notification", [
                    //         'group_coffee_run_id' => $order->group_coffee_run_id,
                    //         'user_id' => $user->id,
                    //         'order_id' => $order->id
                    //     ]);

                    //     $this->sendOrderCompleteNotification($order);
                    // }


                    // Mark pickup on Square
                    $squareResult = $this->pickupItemsOnSquare($order);
                    if (!$squareResult['success']) {
                        \Log::error("Square pickup update failed for Order ID {$order->id}", ['error' => $squareResult['message']]);
                    }

                    $existingOrders = Order::where('user_id', $user->id)->where('id', '!=', $order->id)->count();
                    // if ($existingOrders == 0) {
                    //     $user->remaining_awarded_stamps = 2;
                    //     $user->save();

                    //     if (!empty($user->referral_code_used)) {
                    //         $referrer = User::where('referral_code', $user->referral_code_used)->first();
                    //         if ($referrer) {
                    //             $referrer->remaining_awarded_stamps += 2;
                    //             $referrer->save();
                    //         }
                    //     }
                    // }
                }
                // Group orders — implement similar logic as above for group if needed
                // return response()->json(['error' => 'Group orders not implemented yet'], 501);
            }

            return response()->json([
                'status' => 'success',
                'message' => 'Order marked completed successfully!',
            ], 200);

        } catch (ValidationException $validationException) {
            return response()->json([
                'status' => 'error',
                'message' => 'Validation failed.',
                'errors' => $validationException->errors(),
            ], 422);
        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'message' => 'An error occurred while completing the order!',
                'error' => $e->getMessage(),
            ], 500);
        }
    }


    private function pickupItemsOnSquare($order)
    {
        try {
            $cafe = Cafe::find($order->cafe_id);
            if (!$cafe || !$cafe->square_access_token || !$order->square_order_id) {
                return ['success' => false, 'message' => 'Cafe or access token or Square order ID missing'];
            }

            $orderResponse = Http::withToken($cafe->square_access_token)
                ->get("https://connect.squareup.com/v2/orders/{$order->square_order_id}");

            if (!$orderResponse->successful()) {
                return ['success' => false, 'message' => 'Failed to fetch Square order'];
            }

            $squareOrder = $orderResponse->json('order');
            $version = $squareOrder['version'];
            $locationId = $squareOrder['location_id'];

            if (!isset($squareOrder['fulfillments'])) {
                return ['success' => false, 'message' => 'No fulfillments found'];
            }

            foreach ($squareOrder['fulfillments'] as &$fulfillment) {
                if ($fulfillment['type'] === 'PICKUP') {
                    // $fulfillment['state'] = "COMPLETED";
                    $fulfillment['state'] = "PREPARED";
                }
            }

            $updatePayload = [
                "order" => [
                    "version" => $version,
                    "location_id" => $locationId,
                    "fulfillments" => $squareOrder['fulfillments']
                ]
            ];

            $updateResponse = Http::withToken($cafe->square_access_token)
                ->put("https://connect.squareup.com/v2/orders/{$order->square_order_id}", $updatePayload);

            if (!$updateResponse->successful()) {
                return ['success' => false, 'message' => 'Failed to update Square order state', 'errors' => $updateResponse->json()];
            }

            // return ['success' => true, 'message' => 'Square order marked as picked up'];
            return ['success' => true, 'message' => 'Square order marked as Ready for Pickup'];

        } catch (\Exception $e) {
            return ['success' => false, 'message' => 'Exception: ' . $e->getMessage()];
        }
    }



    //mark order completed
    public function markOrderProcessed(Request $request)
    {
        try {
            // Validate the incoming request
            $validator = Validator::make($request->all(), [
                'id' => 'required',
                'is_individual_order' => 'required|boolean|in:0,1',
            ]);

            if ($validator->fails()) {
                throw new ValidationException($validator);
            }

            if ($request->is_individual_order == 1) {
                // Individual order processing
                $order = Order::find($request->id);
                if (!$order) {
                    return response()->json(['error' => 'Order not found'], 404);
                }
                //from cafeMenuItems check item status where item id is in order item array
                $orderItemArray = json_decode($order->order_item_array, true);
                $itemIds = array_column($orderItemArray, 'item_id');
                $cafeMenuItems = CafeMenuItem::whereIn('id', $itemIds)->get();
                $isItemAvailable = true;
                $notAvailableItems = [];
                //check if any item is not available
                foreach ($cafeMenuItems as $cafeMenuItem) {
                    if ($cafeMenuItem->status != 1) {
                        $isItemAvailable = false;
                        $notAvailableItems[] = $cafeMenuItem;
                    }
                }
                //if any item is not available then set order status to 2
                if (!$isItemAvailable) {
                    return response()->json([
                        'status' => 'success',
                        'unavailable_items' => $notAvailableItems,
                        'message' => 'Order cannot be processed as some items are not available!',
                    ], 200);
                }

                $order->status = 3;
                $order->save();

                return response()->json([
                    'status' => 'success',
                    'message' => 'Order marked processed successfully!',
                ], 200);
            } else {
                // Group coffee run orders processing
                $orders = Order::where('group_coffee_run_id', $request->id)->get();

                if ($orders->isEmpty()) {
                    return response()->json(['error' => 'Group orders not found'], 404);
                }

                foreach ($orders as $order) {
                    //from cafeMenuItems check item status where item id is in order item array
                    $orderItemArray = json_decode($order->order_item_array, true);
                    $itemIds = array_column($orderItemArray, 'item_id');
                    $cafeMenuItems = CafeMenuItem::whereIn('id', $itemIds)->get();
                    $isItemAvailable = true;
                    $notAvailableItems = [];
                    //check if any item is not available
                    foreach ($cafeMenuItems as $cafeMenuItem) {
                        if ($cafeMenuItem->status != 1) {
                            $isItemAvailable = false;
                            $notAvailableItems[] = $cafeMenuItem;
                        }
                    }
                    //if any item is not available then set order status to 2
                    if (!$isItemAvailable) {
                        return response()->json([
                            'status' => 'success',
                            'unavailable_items' => $notAvailableItems,
                            'message' => 'Order cannot be processed as some items are not available!',
                        ], 200);
                    }
                    $order->status = 3;
                    $order->save();
                }

                return response()->json([
                    'status' => 'success',
                    'message' => 'Order marked processed successfully!',
                ], 200);
            }

        } catch (ValidationException $validationException) {
            return response()->json([
                'status' => 'error',
                'message' => 'Validation failed.',
                'errors' => $validationException->errors(),
            ], 422);
        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'message' => 'An error occurred while processing the order!',
                'error' => $e->getMessage(),
            ], 500);
        }
    }

    //send order refund
    public function oldrefundOrder(Request $request)
    {
        try {
            $validator = Validator::make($request->all(), [
                'id' => 'required',
                'is_individual_order' => 'required|boolean|in:0,1',
                'item_ids' => 'required|string',
            ]);

            if ($validator->fails()) {
                throw new ValidationException($validator);
            }

            $itemIds = array_map('intval', explode(',', $request->item_ids));
            $orders = collect();
            $initiator = null;

            if ($request->is_individual_order == 1) {
                $order = Order::find($request->id);
                if (!$order) {
                    return response()->json(['error' => 'Order not found'], 404);
                }
                $orders->push($order);
            } else {
                $orders = Order::where('group_coffee_run_id', $request->id)->get();
                if ($orders->isEmpty()) {
                    return response()->json(['error' => 'Group orders not found'], 404);
                }

                $group = GroupCoffeeRun::find($request->id);
                $initiator = $group ? $group->initiator : null;
            }

            $cafeMenuItems = CafeMenuItem::whereIn('id', $itemIds)->get()->keyBy('id');

            $refundAmount = 0;
            $refundedItems = [];
            $processedItems = [];

            foreach ($orders as $order) {
                $unavailableItemMessages = [];
                $orderItems = json_decode($order->order_item_array, true);

                foreach ($orderItems as $item) {
                    $itemId = (int) $item['item_id'];

                    if (in_array($itemId, $itemIds)) {
                        $amount = $item['item_amount'] * $item['item_quantity'];
                        $refundAmount += $amount;
                        $refundedItems[] = $item;

                        $itemName = $cafeMenuItems[$itemId]->item_name ?? 'Unknown Item';
                        $unavailableItemMessages[] = "Sorry, {$itemName} is unavailable. You’ve been refunded £" . number_format($amount, 2) . ". For questions, please contact the cafe directly.";
                    } else {
                        $processedItems[] = $item;
                    }
                }

                if ($refundAmount > 0) {
                    $user = $order->user;

                    if ($user && $user->fcm_token) {
                        $refundMessage = implode(' ', $unavailableItemMessages);

                        NotificationModel::create([
                            'sender_id' => $order->cafe_id,
                            'receiver_id' => $user->id,
                            'reference_id' => $order->id,
                            'notification_type' => 9,
                            'is_read' => 0,
                            'message' => $refundMessage,
                            'created_at' => now()->timestamp,
                            'updated_at' => now()->timestamp
                        ]);

                        try {
                            $firebase = (new Factory)->withServiceAccount(
                                storage_path('app/java-go-380509-firebase-adminsdk-236oo-44f6190a2d.json')
                            );

                            $messaging = $firebase->createMessaging();
                            $message = CloudMessage::withTarget('token', $user->fcm_token)
                                ->withNotification(FirebaseNotification::create('Refund Processed', $refundMessage))
                                ->withData([
                                    'order_id' => (string) $order->id,
                                    'type' => 'refund'
                                ]);

                            $messaging->send($message);
                            \Log::info("Refund notification sent to User ID: {$user->id}");
                        } catch (\Exception $e) {
                            \Log::error("FCM notification failed for User ID: {$user->id} - " . $e->getMessage());
                        }
                    }

                    if ($initiator && $initiator->id !== $user->id && $initiator->fcm_token) {
                        $refundMessage = implode(' ', $unavailableItemMessages);

                        NotificationModel::create([
                            'sender_id' => $order->cafe_id,
                            'receiver_id' => $initiator->id,
                            'reference_id' => $order->id,
                            'notification_type' => 9,
                            'is_read' => 0,
                            'message' => $refundMessage,
                            'created_at' => now()->timestamp,
                            'updated_at' => now()->timestamp
                        ]);

                        try {
                            $firebase = (new Factory)->withServiceAccount(
                                storage_path('app/java-go-380509-firebase-adminsdk-236oo-44f6190a2d.json')
                            );

                            $messaging = $firebase->createMessaging();
                            $message = CloudMessage::withTarget('token', $initiator->fcm_token)
                                ->withNotification(FirebaseNotification::create('Refund Processed (Your Group)', $refundMessage))
                                ->withData([
                                    'order_id' => (string) $order->id,
                                    'type' => 'refund'
                                ]);

                            $messaging->send($message);
                            \Log::info("Refund notification sent to Initiator ID: {$initiator->id}");
                        } catch (\Exception $e) {
                            \Log::error("FCM to initiator failed: " . $e->getMessage());
                        }
                    }
                }
            }

            $order->processed_order_items_array = json_encode($processedItems);
            $order->refunded_order_items_array = json_encode($refundedItems);
            $order->refunded_amount = $refundAmount;
            $order->save();

            return response()->json([
                'status' => 'success',
                'message' => 'Refund processed and notifications sent.',
            ], 200);

        } catch (ValidationException $validationException) {
            return response()->json([
                'status' => 'error',
                'message' => 'Validation failed.',
                'errors' => $validationException->errors(),
            ], 422);
        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'message' => 'An error occurred while processing the refund!',
                'error' => $e->getMessage(),
            ], 500);
        }
    }

    //send order refund
    public function refundOrder(Request $request)
    {
        try {
            $validator = Validator::make($request->all(), [
                'id' => 'required',
                'is_individual_order' => 'required|boolean|in:0,1',
                'items' => 'required|string'
            ]);

            if ($validator->fails()) {
                throw new ValidationException($validator);
            }

            $itemsString = $request->items; // e.g., "9823742-[12,13],928374-[15]"
            $parsedItems = [];
            $pattern = '/(\d+)-\[(.*?)\]/';

            preg_match_all($pattern, $itemsString, $matches, PREG_SET_ORDER);

            foreach ($matches as $match) {
                $orderNumber = $match[1];
                $itemIdsString = $match[2];
                $itemIds = array_map('intval', explode(',', $itemIdsString));

                $parsedItems[] = [
                    'order_number' => $orderNumber,
                    'item_ids' => $itemIds
                ];
            }


            $squareCancellationResults = [];

            foreach ($parsedItems as $itemGroup) {
                $orderNumber = $itemGroup['order_number'];
                $itemIds = $itemGroup['item_ids'];
                \Log::info("Processing refund for order number: {$orderNumber} with item IDs: " . implode(',', $itemIds));

                $order = Order::where('order_number', $orderNumber)->first();
                if (!$order) {
                    \Log::warning("Order number {$orderNumber} not found for refund");
                    continue;
                }

                $cafeMenuItems = CafeMenuItem::whereIn('id', $itemIds)->get()->keyBy('id');

                $orderItems = json_decode($order->order_item_array, true);
                $remainingItems = [];
                $refundedItemsThisOrder = [];
                $refundAmountThisOrder = 0;
                $itemsToCancel = [];
                $unavailableItemMessages = [];

                foreach ($orderItems as $item) {
                    $itemId = (int) $item['item_id'];

                    if (in_array($itemId, $itemIds)) {
                        $amount = $item['item_amount'] * $item['item_quantity'];
                        $refundAmountThisOrder += $amount;
                        $refundedItemsThisOrder[] = $item;
                        $itemsToCancel[] = $item;

                        $itemName = $cafeMenuItems[$itemId]->item_name ?? 'Unknown Item';
                        $unavailableItemMessages[] = "Sorry, {$itemName} is unavailable. You've been refunded £" . number_format($amount, 2) . ".";
                    } else {
                        $remainingItems[] = $item;
                    }
                }
                \Log::info("Refunding order number {$orderNumber} for items: " . implode(', ', array_column($itemsToCancel, 'item_name')));

                if (!empty($itemsToCancel) && !empty($order->square_order_id)) {
                    $squareResult = $this->cancelSquareOrderItems($order, array_column($itemsToCancel, 'item_name'));
                    \Log::info("Square cancellation result for order number {$orderNumber}: " . json_encode($squareResult));
                    $squareCancellationResults[] = $squareResult;
                }

                // $order->processed_order_items_array = json_encode($remainingItems);
                // $order->refunded_order_items_array = json_encode($refundedItemsThisOrder);
                // $order->refunded_amount = $refundAmountThisOrder;

                // if (count($remainingItems) === 0) {
                //     $order->refund_status = 'full_refund';
                //     $order->is_full_order_cancelled = 1;
                // } else {
                //     $order->refund_status = 'partial_refund';
                //     $order->is_full_order_cancelled = 0;
                // }

                // $order->save();
            }

            return response()->json([
                'status' => 'success',
                'message' => 'Refund(s) processed successfully.',
                'square_cancellation_results' => $squareCancellationResults
            ], 200);

        } catch (ValidationException $validationException) {
            return response()->json([
                'status' => 'error',
                'message' => 'Validation failed.',
                'errors' => $validationException->errors(),
            ], 422);
        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'message' => 'An error occurred while processing the refund!',
                'error' => $e->getMessage(),
            ], 500);
        }
    }


    public function prevrefundOrder(Request $request)
    {
        try {
            $validator = Validator::make($request->all(), [
                'id' => 'required',
                'is_individual_order' => 'required|boolean|in:0,1',
                'item_ids' => 'required|string',
            ]);

            if ($validator->fails()) {
                throw new ValidationException($validator);
            }

            $itemIds = array_map('intval', explode(',', $request->item_ids));
            $orders = collect();
            $initiator = null;

            if ($request->is_individual_order == 1) {
                $order = Order::find($request->id);
                if (!$order) {
                    return response()->json(['error' => 'Order not found'], 404);
                }
                $orders->push($order);
            } else {
                $orders = Order::where('group_coffee_run_id', $request->id)->get();
                if ($orders->isEmpty()) {
                    return response()->json(['error' => 'Group orders not found'], 404);
                }

                $group = GroupCoffeeRun::find($request->id);
                $initiator = $group ? $group->initiator : null;
            }

            $cafeMenuItems = CafeMenuItem::whereIn('id', $itemIds)->get()->keyBy('id');

            $refundAmount = 0;
            $refundedItems = [];
            $processedItems = [];
            $squareCancellationResults = [];

            foreach ($orders as $order) {
                $unavailableItemMessages = [];
                $orderItems = json_decode($order->order_item_array, true);
                $itemsToCancel = [];

                foreach ($orderItems as $item) {
                    $itemId = (int) $item['item_id'];

                    if (in_array($itemId, $itemIds)) {
                        $amount = $item['item_amount'] * $item['item_quantity'];
                        $refundAmount += $amount;
                        $refundedItems[] = $item;
                        $itemsToCancel[] = $item;

                        $itemName = $cafeMenuItems[$itemId]->item_name ?? 'Unknown Item';
                        $unavailableItemMessages[] = "Sorry, {$itemName} is unavailable. You've been refunded £" . number_format($amount, 2) . ". For questions, please contact the cafe directly.";
                    } else {
                        $processedItems[] = $item;
                    }
                }

                $itemIdsOnly = array_column($itemsToCancel, 'item_name');
                \Log::info("Refunding order ID: {$order->id} for items: " . implode(', ', $itemIdsOnly));

                // Cancel items on Square if order has square_order_id
                if (!empty($order->square_order_id) && !empty($itemsToCancel)) {
                    $squareResult = $this->cancelSquareOrderItems($order, $itemIdsOnly);
                    \Log::info("Square cancellation result for order ID {$order->id}: " . json_encode($squareResult));
                    $squareCancellationResults[] = $squareResult;
                }

                // if ($refundAmount > 0) {
                //     $user = $order->user;

                //     if ($user && $user->fcm_token) {
                //         $refundMessage = implode(' ', $unavailableItemMessages);

                //         NotificationModel::create([
                //             'sender_id' => $order->cafe_id,
                //             'receiver_id' => $user->id,
                //             'reference_id' => $order->id,
                //             'notification_type' => 9,
                //             'is_read' => 0,
                //             'message' => $refundMessage,
                //             'created_at' => now()->timestamp,
                //             'updated_at' => now()->timestamp
                //         ]);

                //         try {
                //             $firebase = (new Factory)->withServiceAccount(
                //                 storage_path('app/java-go-380509-firebase-adminsdk-236oo-44f6190a2d.json')
                //             );

                //             $messaging = $firebase->createMessaging();
                //             $message = CloudMessage::withTarget('token', $user->fcm_token)
                //                 ->withNotification(FirebaseNotification::create('Refund Processed', $refundMessage))
                //                 ->withData([
                //                     'order_id' => (string) $order->id,
                //                     'type' => 'refund'
                //                 ]);

                //             $messaging->send($message);
                //             \Log::info("Refund notification sent to User ID: {$user->id}");
                //         } catch (\Exception $e) {
                //             \Log::error("FCM notification failed for User ID: {$user->id} - " . $e->getMessage());
                //         }
                //     }

                //     if ($initiator && $initiator->id !== $user->id && $initiator->fcm_token) {
                //         $refundMessage = implode(' ', $unavailableItemMessages);

                //         NotificationModel::create([
                //             'sender_id' => $order->cafe_id,
                //             'receiver_id' => $initiator->id,
                //             'reference_id' => $order->id,
                //             'notification_type' => 9,
                //             'is_read' => 0,
                //             'message' => $refundMessage,
                //             'created_at' => now()->timestamp,
                //             'updated_at' => now()->timestamp
                //         ]);

                //         try {
                //             $firebase = (new Factory)->withServiceAccount(
                //                 storage_path('app/java-go-380509-firebase-adminsdk-236oo-44f6190a2d.json')
                //             );

                //             $messaging = $firebase->createMessaging();
                //             $message = CloudMessage::withTarget('token', $initiator->fcm_token)
                //                 ->withNotification(FirebaseNotification::create('Refund Processed (Your Group)', $refundMessage))
                //                 ->withData([
                //                     'order_id' => (string) $order->id,
                //                     'type' => 'refund'
                //                 ]);

                //             $messaging->send($message);
                //             \Log::info("Refund notification sent to Initiator ID: {$initiator->id}");
                //         } catch (\Exception $e) {
                //             \Log::error("FCM to initiator failed: " . $e->getMessage());
                //         }
                //     }
                // }

                // Update order with refund information
                // $order->processed_order_items_array = json_encode($processedItems);
                // $order->refunded_order_items_array = json_encode($refundedItems);
                // $order->refunded_amount = $refundAmount;

                // // Check if all items are refunded for full cancellation
                // $totalItems = count($orderItems);
                // $refundedItemsCount = count($refundedItems);

                // if ($refundedItemsCount === $totalItems) {
                //     $order->refund_status = 'full_refund';
                //     $order->is_full_order_cancelled = 1;
                //     \Log::info("Full order cancellation detected", [
                //         'order_id' => $order->id,
                //         'total_items' => $totalItems,
                //         'refunded_items' => $refundedItemsCount
                //     ]);
                // } else {
                //     $order->refund_status = 'partial_refund';
                //     \Log::info("Partial order cancellation detected", [
                //         'order_id' => $order->id,
                //         'total_items' => $totalItems,
                //         'refunded_items' => $refundedItemsCount
                //     ]);
                // }

                // $order->save();
            }

            return response()->json([
                'status' => 'success',
                'message' => 'Refund processed and notifications sent.',
                'square_cancellation_results' => $squareCancellationResults
            ], 200);

        } catch (ValidationException $validationException) {
            return response()->json([
                'status' => 'error',
                'message' => 'Validation failed.',
                'errors' => $validationException->errors(),
            ], 422);
        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'message' => 'An error occurred while processing the refund!',
                'error' => $e->getMessage(),
            ], 500);
        }
    }

    /**
     * Cancel specific items on Square
     */
    private function cancelSquareOrderItems($order, array $itemsToCancel)
    {
        try {
            \Log::info('Starting Square order cancellation', [
                'order_id' => $order->id,
                'square_order_id' => $order->square_order_id,
                'items_to_cancel' => $itemsToCancel
            ]);

            $cafe = Cafe::find($order->cafe_id);
            if (!$cafe || !$cafe->square_access_token) {
                return ['success' => false, 'message' => 'Cafe or access token not found'];
            }

            // ✅ Fetch latest order
            $orderResponse = Http::withToken($cafe->square_access_token)
                ->get("https://connect.squareup.com/v2/orders/{$order->square_order_id}");

            if (!$orderResponse->successful()) {
                return ['success' => false, 'message' => 'Failed to fetch Square order'];
            }

            $squareOrder = $orderResponse->json('order');
            $lineItems = collect($squareOrder['line_items'] ?? []);
            $version = $squareOrder['version'];

            $totalAmountToRefund = 0;
            $itemsRefunded = [];

            foreach ($lineItems as $item) {
                $itemName = strtolower(trim($item['name']));
                if (in_array($itemName, array_map('strtolower', $itemsToCancel))) {
                    $amount = (int) $item['base_price_money']['amount'];
                    $quantity = (int) $item['quantity'];
                    $lineAmount = $amount * $quantity;
                    $totalAmountToRefund += $lineAmount;

                    $itemsRefunded[] = [
                        'name' => $item['name'],
                        'amount' => $lineAmount / 100,
                        'quantity' => $quantity,
                        'uid' => $item['uid'] ?? null
                    ];
                }
            }

            if (empty($itemsRefunded)) {
                return ['success' => false, 'message' => 'No matching items found to cancel'];
            }

            // ✅ Refund logic
            $paymentId = $squareOrder['tenders'][0]['payment_id'] ?? $order->square_payment_id ?? null;
            if (!$paymentId) {
                return ['success' => false, 'message' => 'Payment ID not found'];
            }

            $refundPayload = [
                "idempotency_key" => "refund-{$order->id}-" . time(),
                "amount_money" => [
                    "amount" => $totalAmountToRefund,
                    "currency" => "GBP"
                ],
                "payment_id" => $paymentId,
                "reason" => "Partial item cancel: " . implode(', ', array_column($itemsRefunded, 'name'))
            ];

            $refundResponse = Http::withToken($cafe->square_access_token)
                ->post("https://connect.squareup.com/v2/refunds", $refundPayload);

            if (!$refundResponse->successful()) {
                return ['success' => false, 'message' => 'Failed to process refund', 'errors' => $refundResponse->json()];
            }

            \Log::info("Partial refund completed", [
                'order_id' => $order->id,
                'refund_response' => $refundResponse->json()
            ]);

            // ✅ Check if all items were refunded
            $remainingItems = $lineItems->reject(function ($item) use ($itemsToCancel) {
                return in_array(strtolower(trim($item['name'])), array_map('strtolower', $itemsToCancel));
            })->values();

            $isFullCancel = $remainingItems->isEmpty();

            if ($isFullCancel && isset($squareOrder['fulfillments'])) {
                // ✅ Refetch latest order to get latest version
                $latestOrderResponse = Http::withToken($cafe->square_access_token)
                    ->get("https://connect.squareup.com/v2/orders/{$order->square_order_id}");

                if ($latestOrderResponse->successful()) {
                    $latestSquareOrder = $latestOrderResponse->json('order');
                    $newVersion = $latestSquareOrder['version'];

                    // Build updated fulfillments with CANCELED state
                    $updatedFulfillments = collect($latestSquareOrder['fulfillments'])
                        ->map(function ($fulfillment) {
                            return [
                                'uid' => $fulfillment['uid'],
                                'state' => 'CANCELED',
                            ];
                        })->toArray();

                    $updatePayload = [
                        "order" => [
                            "version" => $newVersion,
                            "fulfillments" => $updatedFulfillments,
                        ],
                        "idempotency_key" => "cancel-{$order->id}-" . time(),
                    ];

                    $updateResponse = Http::withToken($cafe->square_access_token)
                        ->put("https://connect.squareup.com/v2/orders/{$order->square_order_id}", $updatePayload);

                    if (!$updateResponse->successful()) {
                        \Log::error("Failed to update fulfillments to CANCELED", [
                            'order_id' => $order->id,
                            'errors' => $updateResponse->json(),
                        ]);

                        return [
                            'success' => true,
                            'message' => 'Items refunded, but failed to update fulfillments to canceled',
                            'errors' => $updateResponse->json(),
                        ];
                    }
                } else {
                    \Log::error("Failed to refetch order for fulfillment cancellation", [
                        'order_id' => $order->id,
                        'errors' => $latestOrderResponse->json(),
                    ]);

                    return [
                        'success' => true,
                        'message' => 'Items refunded, but failed to update fulfillments (could not refetch order)',
                    ];
                }
            }

            // ✅ Update local DB
            $order->refunded_order_items_array = json_encode($itemsRefunded);
            $order->refunded_amount = $totalAmountToRefund / 100;
            $order->refund_status = $isFullCancel ? 'full_refund' : 'partial_refund';
            $order->is_full_order_cancelled = $isFullCancel ? 1 : 0;
            $order->save();

            $this->sendFullRefundNotification($order, $order->refunded_amount);

            return [
                'success' => true,
                'message' => $isFullCancel ? 'Full order canceled and refunded' : 'Partial refund completed',
                'canceled_items' => array_column($itemsRefunded, 'name'),
                'refund_amount' => $totalAmountToRefund / 100,
                'refund_status' => $order->refund_status,
                'is_full_cancellation' => $isFullCancel
            ];

        } catch (\Exception $e) {
            \Log::error("Exception during Square cancel", [
                'order_id' => $order->id ?? 'unknown',
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);

            return ['success' => false, 'message' => 'Exception: ' . $e->getMessage()];
        }
    }




    private function sendFullRefundNotification($order, $totalRefund)
    {
        try {
            $user = $order->user;
            if (!$user) {
                \Log::warning("User not found for full refund notification", ['order_id' => $order->id]);
                return;
            }

            $title = 'Order Cancelled & Refunded';
            $body = "Your order has been cancelled and a refund of £" . number_format($totalRefund, 2) . " has been processed.";

            // Create notification in database
            NotificationModel::create([
                'sender_id' => $order->cafe_id,
                'receiver_id' => $user->id,
                'reference_id' => $order->id,
                'notification_type' => 9, // refund
                'is_read' => 0,
                'message' => $body,
                'created_at' => now()->timestamp,
                'updated_at' => now()->timestamp
            ]);

            // Send FCM notification
            $this->sendFCMNotification($user, $title, $body, [
                'order_id' => (string) $order->id,
                'type' => 'full_refund',
                'refund_amount' => $totalRefund
            ]);

            \Log::info("📱 Full refund notification sent", [
                'user_id' => $user->id,
                'order_id' => $order->id,
                'refund_amount' => $totalRefund
            ]);

        } catch (\Exception $e) {
            \Log::error("Full refund notification failed", [
                'order_id' => $order->id,
                'error' => $e->getMessage()
            ]);
        }
    }


    private function sendOrderCompleteNotification($order)
    {
        try {
            $user = $order->user;
            if (!$user) {
                \Log::warning("User not found for order complete notification", ['order_id' => $order->id]);
                return;
            }

            $title = 'Order Completed';
            $body = 'Your order has been completed! Please collect it.';

            // Create notification in database
            NotificationModel::create([
                'sender_id' => $order->cafe_id,
                'receiver_id' => $user->id,
                'reference_id' => $order->id,
                'notification_type' => 10, // example type: order completed
                'is_read' => 0,
                'message' => $body,
                'created_at' => now()->timestamp,
                'updated_at' => now()->timestamp
            ]);

            // Send FCM notification
            $this->sendFCMNotification($user, $title, $body, [
                'order_id' => (string) $order->id,
                'type' => 'order_completed'
            ]);

            \Log::info("📱 Order completed notification sent", [
                'user_id' => $user->id,
                'order_id' => $order->id
            ]);

        } catch (\Exception $e) {
            \Log::error("Order complete notification failed", [
                'order_id' => $order->id,
                'error' => $e->getMessage()
            ]);
        }
    }


    private function sendFCMNotification($user, $title, $body, $data = [])
    {
        try {
            if (!$user->fcm_token) {
                \Log::info("No FCM token for user", ['user_id' => $user->id]);
                return;
            }

            $firebaseConfig = config('firebase.credentials.file') ?: storage_path('app/java-go-380509-firebase-adminsdk-236oo-44f6190a2d.json');

            $messaging = (new Factory)->withServiceAccount($firebaseConfig)->createMessaging();

            $messageBuilder = CloudMessage::withTarget('token', $user->fcm_token)
                ->withNotification(FirebaseNotification::create($title, $body));

            if (!empty($data)) {
                $messageBuilder = $messageBuilder->withData($data);
            }

            $repsomes = $messaging->send($messageBuilder);
            \log::info("FCM RESPONSE", [$repsomes]);
            \Log::info("FCM notification sent successfully", [
                'title' => $title,
                'user_id' => $user->id,
                'data' => $data
            ]);

        } catch (\Throwable $e) {
            \Log::error("FCM notification failed", [
                'title' => $title,
                'user_id' => $user->id ?? 'unknown',
                'error' => $e->getMessage()
            ]);
        }
    }

    public function getCafeStats(Request $request)
    {
        try {
            $cafe = Cafe::find($request->user->id);
            if (!$cafe) {
                return response()->json([
                    'status' => 'error',
                    'message' => 'Cafe not found.',
                ], 404);
            }

            $todayStart = Carbon::today()->startOfDay()->timestamp;
            $todayEnd = Carbon::today()->endOfDay()->timestamp;

            // All orders
            $allOrders = Order::where('cafe_id', $cafe->id)->get();
            $allOrderCount = $allOrders->count();
            $allRevenue = $allOrders->sum(function ($order) {
                return $order->total_amount - ($order->refunded_amount ?? 0);
            });

            // Today's orders
            $todayOrders = $allOrders->filter(function ($order) use ($todayStart, $todayEnd) {
                return $order->created_at >= $todayStart && $order->created_at <= $todayEnd;
            });
            $todayOrderCount = $todayOrders->count();
            $todayRevenue = $todayOrders->sum(function ($order) {
                return $order->total_amount - ($order->refunded_amount ?? 0);
            });

            return response()->json([
                'status' => 'success',
                'message' => 'Cafe stats found successfully!',
                'all_order' => $allOrderCount,
                'today_order' => $todayOrderCount,
                'all_revenue' => number_format((float) $allRevenue, 2),
                'today_revenue' => number_format((float) $todayRevenue, 2),
            ], 200);

        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'message' => 'An error occurred while getting cafe stats!',
                'error' => $e->getMessage(),
            ], 500);
        }
    }


    public function markOrderPickup(Request $request)
    {
        try {
            $validator = Validator::make($request->all(), [
                'id' => 'required',
                'is_individual_order' => 'required|boolean|in:0,1',
            ]);

            if ($validator->fails()) {
                throw new ValidationException($validator);
            }


            if ($request->is_individual_order == 1) {
                $order = Order::find($request->id);
                if (!$order) {
                    return response()->json(['error' => 'Order not found'], 404);
                }

                $order->order_completed = 2;
                $order->save();

                $user = $order->user;



                // Mark pickup on Square
                $squareResult = $this->markpickupItemsOnSquare($order);
                if (!$squareResult['success']) {
                    \Log::error("Square pickup update failed for Order ID {$order->id}", ['error' => $squareResult['message']]);
                }
                return response()->json([
                    'status' => 'success',
                    'message' => 'Order marked Picked up successfully!',
                ], 200);

            } else {
                $orders = Order::where('group_coffee_run_id', $request->id)->get();

                if ($orders->isEmpty()) {
                    return response()->json(['error' => 'Group orders not found'], 404);
                }

                foreach ($orders as $order) {
                    $order->order_completed = 2;
                    $order->save();

                    $user = $order->user;


                    // Mark pickup on Square
                    $squareResult = $this->markpickupItemsOnSquare($order);
                    if (!$squareResult['success']) {
                        \Log::error("Square pickup update failed for Order ID {$order->id}", ['error' => $squareResult['message']]);
                    }
                }

                return response()->json([
                    'status' => 'success',
                    'message' => 'Order marked Picked up successfully!',
                ], 200);
            }

        } catch (ValidationException $validationException) {
            return response()->json([
                'status' => 'error',
                'message' => 'Validation failed.',
                'errors' => $validationException->errors(),
            ], 422);
        } catch (\Exception $e) {
            return response()->json([
                'status' => 'error',
                'message' => 'An error occurred while completing the order!',
                'error' => $e->getMessage(),
            ], 500);
        }
    }


    private function markpickupItemsOnSquare($order)
    {
        try {
            $cafe = Cafe::find($order->cafe_id);
            if (!$cafe || !$cafe->square_access_token || !$order->square_order_id) {
                return ['success' => false, 'message' => 'Cafe or access token or Square order ID missing'];
            }

            $orderResponse = Http::withToken($cafe->square_access_token)
                ->get("https://connect.squareup.com/v2/orders/{$order->square_order_id}");

            if (!$orderResponse->successful()) {
                return ['success' => false, 'message' => 'Failed to fetch Square order'];
            }

            $squareOrder = $orderResponse->json('order');
            $version = $squareOrder['version'];
            $locationId = $squareOrder['location_id'];

            if (!isset($squareOrder['fulfillments'])) {
                return ['success' => false, 'message' => 'No fulfillments found'];
            }

            foreach ($squareOrder['fulfillments'] as &$fulfillment) {
                if ($fulfillment['type'] === 'PICKUP') {
                    $fulfillment['state'] = "COMPLETED";
                }
            }

            $updatePayload = [
                "order" => [
                    "version" => $version,
                    "location_id" => $locationId,
                    "fulfillments" => $squareOrder['fulfillments']
                ]
            ];

            $updateResponse = Http::withToken($cafe->square_access_token)
                ->put("https://connect.squareup.com/v2/orders/{$order->square_order_id}", $updatePayload);

            if (!$updateResponse->successful()) {
                return ['success' => false, 'message' => 'Failed to update Square order state', 'errors' => $updateResponse->json()];
            }

            // return ['success' => true, 'message' => 'Square order marked as picked up'];
            return ['success' => true, 'message' => 'Square order marked as Pickup'];

        } catch (\Exception $e) {
            return ['success' => false, 'message' => 'Exception: ' . $e->getMessage()];
        }
    }

}