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/SquareController.php
<?php

namespace App\Http\Controllers\api;

use App\Http\Controllers\Controller;
use App\Models\AddonSizeCafeItem;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;
use App\Models\Cafe;
use App\Models\Size as Sizes;
use Illuminate\Support\Facades\DB;
use App\Models\CafeMenuItemSize;
use App\Models\CafeMenuItem;
use App\Models\AddonSize;
use App\Models\CafeMenu;

class SquareController extends Controller
{
    // Step 1: Redirect to Square OAuth
    public function redirectToSquare(Request $request)
    {
        $cafe = Cafe::find($request->user->id);
        if (!$cafe) {
            return response()->json(['error' => 'Cafe not found'], 404);
        }

        $clientId = config('services.square.client_id');
        $redirectUri = route('square.callback');
        $scope = 'MERCHANT_PROFILE_READ PAYMENTS_READ PAYMENTS_WRITE ORDERS_WRITE ORDERS_READ ITEMS_READ ITEMS_WRITE CUSTOMERS_READ CUSTOMERS_WRITE';

        // Encode state with cafe_id
        $state = base64_encode(json_encode(['cafe_id' => $cafe->id]));

        $squareEnv = config('services.square.environment', 'sandbox');
        $authorizeUrl = $squareEnv == 'sandbox'
            ? 'https://connect.squareupsandbox.com/oauth2/authorize'
            : 'https://connect.squareup.com/oauth2/authorize';

        $url = $authorizeUrl . '?' . http_build_query([
            'client_id' => $clientId,
            'scope' => $scope,
            'session' => 'false',
            'state' => $state,
            'redirect_uri' => $redirectUri,
        ]);

        // Make scope space-encoded
        $url = str_replace('+', '%20', $url);

        return response()->json(['url' => $url], 200);
    }

    public function handleCallback(Request $request)
    {
        // Decode the state param to get cafe_id
        $stateData = json_decode(base64_decode($request->state), true);

        if (!$stateData || !isset($stateData['cafe_id'])) {
            return response()->json(['error' => 'Invalid state'], 400);
        }

        $cafe = Cafe::find($stateData['cafe_id']);
        if (!$cafe) {
            return response()->json(['error' => 'Cafe not found'], 404);
        }

        $squareEnv = config('services.square.environment', 'sandbox');
        $tokenUrl = $squareEnv == 'sandbox'
            ? 'https://connect.squareupsandbox.com/oauth2/token'
            : 'https://connect.squareup.com/oauth2/token';

        $response = Http::withHeaders([
            'Content-Type' => 'application/json',
        ])->post($tokenUrl, [
                    'client_id' => config('services.square.client_id'),
                    'client_secret' => config('services.square.client_secret'),
                    'code' => $request->code,
                    'grant_type' => 'authorization_code',
                    'redirect_uri' => route('square.callback'),
                ]);

        if ($response->failed()) {
            \Log::error('Square OAuth token exchange failed', [
                'status' => $response->status(),
                'body' => $response->body(),
            ]);

            return response()->json([
                'error' => 'Square connection failed. Please try again.',
                'details' => $response->json(),
            ], 500);
        }

        $data = $response->json();

        // Now fetch merchant locations using the access token
        $locationsResponse = Http::withHeaders([
            'Authorization' => 'Bearer ' . $data['access_token'],
            'Accept' => 'application/json',
        ])->get(
                $squareEnv == 'sandbox'
                ? 'https://connect.squareupsandbox.com/v2/locations'
                : 'https://connect.squareup.com/v2/locations'
            );

        if ($locationsResponse->failed()) {
            \Log::error('Failed to fetch Square locations', [
                'status' => $locationsResponse->status(),
                'body' => $locationsResponse->body(),
            ]);

            return response()->json([
                'error' => 'Failed to retrieve locations for merchant.',
            ], 500);
        }

        $locations = $locationsResponse->json()['locations'] ?? [];

        // Save the first location ID or all as JSON — adjust as per your DB schema
        $locationIds = collect($locations)->pluck('id')->toArray();
        // Check if this Square merchant is already linked to another cafe
        $existingCafe = Cafe::where('square_merchant_id', $data['merchant_id'])
            ->first();

        if ($existingCafe) {
            return response()->json(
                'This Square account is already connected to another cafe!'
                ,
                409
            ); // Conflict
        }

        $cafe->update([
            'square_access_token' => $data['access_token'],
            'square_refresh_token' => $data['refresh_token'],
            'square_merchant_id' => $data['merchant_id'],
            'square_location_id' => json_encode($locationIds),
            'square_onboarding_completed' => 1
        ]);

        return response()->json([
            'message' => 'Thanks! Your Square account connected successfully.',
        ]);
    }

    public function syncMenuToSquare(Request $request)
    {
        try {
            $cafe = Cafe::find($request->user->id);
            if (!$cafe) {
                return response()->json(['error' => 'Cafe not found'], 404);
            }
            if (empty($cafe->square_access_token)) {
                return response()->json(['error' => 'Square account not connected'], 400);
            }

            $menuItemIds = DB::table('cafe_menu_items')
                ->where('cafe_id', $cafe->id)
                ->where('item_deleted_at', 0)
                ->pluck('id');

            if ($menuItemIds->isEmpty()) {
                return response()->json(['error' => 'No menu items to sync'], 404);
            }

            $itemObjects = [];
            $objectIdMap = [];
            $modifierListIdMap = [];

            foreach ($menuItemIds as $itemId) {
                $itemData = $this->getItemDetail($itemId);
                if (!$itemData) {
                    continue;
                }

                // Get Square Category ID
                $menu = DB::table('cafe_menus')->where('id', $itemData['cafe_menu_id'])->first();
                $squareCategoryId = $menu->square_category_id ?? null;

                // Validate category exists
                if (!empty($squareCategoryId)) {
                    $catResponse = Http::withToken($cafe->square_access_token)
                        ->get("https://connect.squareup.com/v2/catalog/object/{$squareCategoryId}");

                    if (!$catResponse->successful() || isset($catResponse->json()['errors'])) {
                        \Log::warning("Invalid Square category for menu ID {$menu->id}");
                        $squareCategoryId = null;
                    } else {
                        $catData = $catResponse->json()['object'] ?? null;
                        if (!isset($catData['type']) || $catData['type'] !== 'CATEGORY') {
                            \Log::warning("Fetched object is not a CATEGORY", ['object' => $catData]);
                            $squareCategoryId = null;
                        }
                    }
                }

                $squareItemId = $itemData['square_item_id'] ?? null;
                $validSquareId = null;
                $itemVersion = null;

                if ($squareItemId) {
                    $existingSquare = $this->getSquareObjectNew($squareItemId, $cafe->square_access_token);
                    if ($existingSquare && empty($existingSquare['errors'])) {
                        $validSquareId = $squareItemId;
                        $itemVersion = $existingSquare['object']['version'] ?? null;
                    }
                }

                $itemObjectId = "#ITEM_{$itemId}_" . uniqid();
                $variationObjects = [];

                if (!empty($itemData['item_size_prices'])) {
                    foreach ($itemData['item_size_prices'] as $size) {
                        $price = floatval($size->item_size_price ?? 0);
                        if ($price <= 0)
                            continue;

                        $variationId = "#VARIATION_{$itemId}_{$size->size_id}_" . uniqid();
                        $sizeData = Sizes::find($size->size_id);
                        $sizeName = $sizeData->size_name ?? 'Regular';

                        $variationObjects[] = [
                            'type' => 'ITEM_VARIATION',
                            'id' => $variationId,
                            'item_variation_data' => [
                                'item_id' => $validSquareId ?: $itemObjectId,
                                'name' => $sizeName,
                                'pricing_type' => 'FIXED_PRICING',
                                'price_money' => [
                                    'amount' => (int) ($price * 100),
                                    'currency' => 'GBP',
                                ],
                                'present_at_all_locations' => true,
                            ],
                        ];
                    }
                } else {
                    $defaultPrice = floatval($itemData['item_price'] ?? 0);
                    if ($defaultPrice <= 0)
                        continue;

                    $variationObjects[] = [
                        'type' => 'ITEM_VARIATION',
                        'id' => "#VARIATION_{$itemId}_default_" . uniqid(),
                        'item_variation_data' => [
                            'item_id' => $validSquareId ?: $itemObjectId,
                            'name' => 'Regular',
                            'pricing_type' => 'FIXED_PRICING',
                            'price_money' => [
                                'amount' => (int) ($defaultPrice * 100),
                                'currency' => 'GBP',
                            ],
                            'present_at_all_locations' => true,
                        ],
                    ];
                }

                // Modifier setup
                $existingModListId = $itemData['square_modifier_list_id'] ?? null;
                $itemModifierListId = $existingModListId ?: "#MODLIST_{$itemId}_" . uniqid();
                $modifierListVersion = null;

                if ($existingModListId) {
                    $modObj = $this->getSquareObjectNew($existingModListId, $cafe->square_access_token);
                    if ($modObj && empty($modObj['errors'])) {
                        $modifierListVersion = $modObj['object']['version'] ?? null;
                    } else {
                        $itemModifierListId = "#MODLIST_{$itemId}_" . uniqid();
                    }
                }

                $modifiers = [];
                foreach ($itemData['optionSizeCafeItems'] as $addon) {
                    $addonSizeId = $addon['addon_size_id'];
                    $addonPrice = floatval($addon['addon_size_price'] ?? 0);

                    $modifiers[] = [
                        'type' => 'MODIFIER',
                        'id' => "#OPTION_{$addonSizeId}_" . uniqid(),
                        'modifier_data' => [
                            'name' => $addon['addon_size_name'] ?? "Addon {$addonSizeId}",
                            'price_money' => [
                                'amount' => (int) ($addonPrice * 100),
                                'currency' => 'GBP',
                            ],
                        ],
                    ];
                }

                $modifierListInfo = [];
                if (!empty($modifiers)) {
                    $modifierListObject = [
                        'type' => 'MODIFIER_LIST',
                        'id' => $itemModifierListId,
                        'modifier_list_data' => [
                            'name' => $itemData['item_name'] . " Addons",
                            'modifiers' => $modifiers,
                            'present_at_all_locations' => true,
                        ],
                    ];

                    if ($modifierListVersion) {
                        $modifierListObject['version'] = $modifierListVersion;
                    }

                    $modifierListInfo[] = [
                        'modifier_list_id' => $itemModifierListId,
                        'enabled' => true,
                    ];

                    $itemObjects[] = $modifierListObject;
                    $modifierListIdMap[$itemId] = $itemModifierListId;
                }

                // Preserve existing description if missing locally
                $finalDescription = $itemData['item_description'];
                if (!$finalDescription && $validSquareId) {
                    $existingItem = $this->getSquareObjectNew($validSquareId, $cafe->square_access_token);
                    $finalDescription = $existingItem['object']['item_data']['description'] ?? '';
                }

                $itemPayload = [
                    'type' => 'ITEM',
                    'id' => $validSquareId ?: $itemObjectId,
                    'item_data' => [
                        'name' => $itemData['item_name'],
                        'description' => $finalDescription,
                        'abbreviation' => strtoupper(substr($itemData['item_name'], 0, 3)),
                        'product_type' => 'REGULAR',
                        'present_at_all_locations' => true,
                        'modifier_list_info' => $modifierListInfo,
                    ],
                ];

                // NEW: Set categories using the complete format Square expects
                if (!empty($squareCategoryId)) {
                    $itemPayload['item_data']['categories'] = [
                        [
                            'id' => $squareCategoryId
                        ]
                    ];

                    // Also set the reporting category
                    $itemPayload['item_data']['reporting_category'] = [
                        'id' => $squareCategoryId
                    ];

                    \Log::info('Setting categories array for item', [
                        'item_id' => $itemId,
                        'item_name' => $itemData['item_name'],
                        'category_id' => $squareCategoryId
                    ]);
                }

                if ($itemVersion) {
                    $itemPayload['version'] = $itemVersion;
                }

                $itemObjects = array_merge($itemObjects, $variationObjects);
                $itemObjects[] = $itemPayload;
                $objectIdMap[$itemId] = $validSquareId ?: $itemObjectId;
            }

            // Chunked Square upsert
            $chunks = array_chunk($itemObjects, 500);
            $allMappings = [];

            foreach ($chunks as $chunkIndex => $chunk) {
                $payload = [
                    'idempotency_key' => uniqid('menu_sync_', true),
                    'batches' => [['objects' => $chunk]],
                ];

                \Log::info('Sending batch to Square', [
                    'chunk_index' => $chunkIndex,
                    'objects_count' => count($chunk),
                    'idempotency_key' => $payload['idempotency_key']
                ]);

                $response = Http::withToken($cafe->square_access_token)
                    ->post("https://connect.squareup.com/v2/catalog/batch-upsert", $payload);

                if (!$response->successful()) {
                    $errorMessage = "Square sync failed for chunk {$chunkIndex}: " . $response->body();
                    \Log::error($errorMessage, [
                        'response' => $response->json(),
                        'status' => $response->status()
                    ]);
                    throw new \Exception($errorMessage);
                }

                $responseData = $response->json();
                \Log::info('Square batch response', [
                    'chunk_index' => $chunkIndex,
                    'id_mappings_count' => count($responseData['id_mappings'] ?? []),
                    'has_errors' => isset($responseData['errors'])
                ]);

                if (isset($responseData['errors'])) {
                    \Log::error('Square API returned errors', ['errors' => $responseData['errors']]);
                    throw new \Exception("Square API errors: " . json_encode($responseData['errors']));
                }

                $allMappings = array_merge($allMappings, $responseData['id_mappings'] ?? []);
            }

            // Update local database with Square IDs
            foreach ($allMappings as $map) {
                if (!empty($map['client_object_id']) && !empty($map['object_id'])) {
                    foreach ($objectIdMap as $localId => $clientId) {
                        if ($clientId === $map['client_object_id']) {
                            DB::table('cafe_menu_items')->where('id', $localId)->update([
                                'square_item_id' => $map['object_id'],
                            ]);
                            \Log::info('Updated item Square ID', [
                                'local_id' => $localId,
                                'square_id' => $map['object_id']
                            ]);
                        }
                    }

                    foreach ($modifierListIdMap as $localId => $modClientId) {
                        if ($modClientId === $map['client_object_id']) {
                            DB::table('cafe_menu_items')->where('id', $localId)->update([
                                'square_modifier_list_id' => $map['object_id'],
                            ]);
                            \Log::info('Updated modifier list Square ID', [
                                'local_id' => $localId,
                                'square_modifier_id' => $map['object_id']
                            ]);
                        }
                    }
                }
            }

            return response()->json([
                'success' => true,
                'message' => 'Menu synced to Square successfully',
                'items_processed' => count($objectIdMap),
                'id_mappings' => count($allMappings)
            ]);

        } catch (\Exception $e) {
            \Log::error('Sync to Square failed', [
                'exception' => $e->getMessage(),
                'trace' => $e->getTraceAsString(),
                'cafe_id' => $cafe->id ?? null
            ]);
            return response()->json(['error' => 'Failed to sync menu: ' . $e->getMessage()], 500);
        }
    }


    public function oldsyncMenuToSquare(Request $request)
    {
        try {
            $cafe = Cafe::find($request->user->id);
            if (!$cafe) {
                return response()->json(['error' => 'Cafe not found'], 404);
            }
            if (empty($cafe->square_access_token)) {
                return response()->json(['error' => 'Square account not connected'], 400);
            }

            $menuItemIds = DB::table('cafe_menu_items')
                ->where('cafe_id', $cafe->id)
                ->where('item_deleted_at', 0)
                ->pluck('id');

            if ($menuItemIds->isEmpty()) {
                return response()->json(['error' => 'No menu items to sync'], 404);
            }

            $itemObjects = [];
            $objectIdMap = [];
            $modifierListIdMap = [];

            foreach ($menuItemIds as $itemId) {
                $itemData = $this->getItemDetail($itemId);
                if (!$itemData) {
                    continue;
                }

                $menu = DB::table('cafe_menus')->where('id', $itemData['cafe_menu_id'])->first();
                $squareCategoryId = $menu->square_category_id ?? null;
                // $squareCategoryId = $itemData['square_category_id'] ?? null;

                if (!empty($squareCategoryId)) {
                    $squareCatResponse = Http::withToken($cafe->square_access_token)
                        ->get("https://connect.squareup.com/v2/catalog/object/{$squareCategoryId}");

                    if (!$squareCatResponse->successful() || isset($squareCatResponse->json()['errors'])) {
                        \Log::warning("Category not found on Square for menu ID {$menu->id}, ignoring category for this item.");
                        $squareCategoryId = null;
                    }
                    \Log::warning("Category found on Square for menu ID {$menu->id}, {$squareCatResponse->body()}");
                }

                $squareItemId = $itemData['square_item_id'] ?? null;
                $validSquareId = null;
                $itemVersion = null;

                if ($squareItemId) {
                    $squareObj = $this->getSquareObjectNew($squareItemId, $cafe->square_access_token);
                    if ($squareObj && empty($squareObj['errors'])) {
                        $validSquareId = $squareItemId;
                        $itemVersion = $squareObj['object']['version'] ?? null;
                    }
                }

                $itemObjectId = "#ITEM_{$itemId}_" . uniqid();
                $variationObjects = [];

                if (!empty($itemData['item_size_prices'])) {
                    foreach ($itemData['item_size_prices'] as $size) {
                        $price = floatval($size->item_size_price ?? 0);
                        if ($price <= 0)
                            continue;

                        $variationId = "#VARIATION_{$itemId}_{$size->size_id}_" . uniqid();
                        $sizeData = Sizes::find($size->size_id);
                        if (!$sizeData) {
                            \Log::warning("Size not found for ID {$size->size_id}, skipping variation.");
                            $sizeName = 'Regular';
                        } else {
                            $sizeName = $sizeData->size_name ?? "Size {$size->size_id}";
                        }

                        $variationObjects[] = [
                            'type' => 'ITEM_VARIATION',
                            'id' => $variationId,
                            'item_variation_data' => [
                                'item_id' => $validSquareId ?: $itemObjectId,
                                'name' => $sizeName,
                                'pricing_type' => 'FIXED_PRICING',
                                'price_money' => [
                                    'amount' => (int) ($price * 100),
                                    'currency' => 'GBP',
                                ],
                                'present_at_all_locations' => true,
                            ],
                        ];
                    }
                } else {
                    $defaultPrice = floatval($itemData['item_price'] ?? 0);
                    if ($defaultPrice <= 0)
                        continue;

                    $variationId = "#VARIATION_{$itemId}_default_" . uniqid();

                    $variationObjects[] = [
                        'type' => 'ITEM_VARIATION',
                        'id' => $variationId,
                        'item_variation_data' => [
                            'item_id' => $validSquareId ?: $itemObjectId,
                            'name' => 'Regular',
                            'pricing_type' => 'FIXED_PRICING',
                            'price_money' => [
                                'amount' => (int) ($defaultPrice * 100),
                                'currency' => 'GBP',
                            ],
                            'present_at_all_locations' => true,
                        ],
                    ];
                }

                // ✅ Create separate modifier list per item
                $existingModifierListId = $itemData['square_modifier_list_id'] ?? null;
                \Log::info("existingModifierListId", ['existingModifierListId' => $existingModifierListId]);

                $modifierListVersion = null;

                if (!empty($existingModifierListId)) {
                    $squareObj = $this->getSquareObjectNew($existingModifierListId, $cafe->square_access_token);
                    if ($squareObj && empty($squareObj['errors'])) {
                        $modifierListVersion = $squareObj['object']['version'] ?? null;
                        $itemModifierListId = $existingModifierListId;
                    } else {
                        $itemModifierListId = "#MODLIST_{$itemId}_" . uniqid();
                    }
                } else {
                    $itemModifierListId = "#MODLIST_{$itemId}_" . uniqid();
                }
                $modifiers = [];
                if (!empty($itemData['optionSizeCafeItems'])) {
                    foreach ($itemData['optionSizeCafeItems'] as $addon) {
                        $addonSizeId = $addon['addon_size_id'];
                        $addonPrice = floatval($addon['addon_size_price'] ?? 0);

                        $modifierOptionId = "#OPTION_{$addonSizeId}_" . uniqid();
                        $modifiers[] = [
                            'type' => 'MODIFIER',
                            'id' => $modifierOptionId,
                            'modifier_data' => [
                                'name' => $addon['addon_size_name'] ?? "Addon {$addonSizeId}",
                                'price_money' => [
                                    'amount' => (int) ($addonPrice * 100),
                                    'currency' => 'GBP',
                                ],
                            ],
                        ];
                    }
                }

                $modifierListObject = null;
                $modifierListInfo = [];

                if (!empty($modifiers)) {
                    $modifierListObject = [
                        'type' => 'MODIFIER_LIST',
                        'id' => $itemModifierListId,
                        'modifier_list_data' => [
                            'name' => $itemData['item_name'] . " Addons",
                            'modifiers' => $modifiers,
                            'present_at_all_locations' => true,
                        ],
                    ];

                    if (!empty($modifierListVersion)) {
                        $modifierListObject['version'] = $modifierListVersion;
                    }

                    $modifierListInfo[] = [
                        'modifier_list_id' => $itemModifierListId,
                        'enabled' => true,
                    ];

                    $modifierListIdMap[$itemId] = $itemModifierListId;
                }
                // Try to preserve the existing Square description if local one is null
                $finalDescription = '';
                if (isset($itemData['item_description']) && $itemData['item_description'] !== null) {
                    $finalDescription = $itemData['item_description'];
                } elseif ($validSquareId) {
                    // fetch existing description from Square
                    $existingSquareItem = $this->getSquareObjectNew($validSquareId, $cafe->square_access_token);
                    if (!empty($existingSquareItem['object']['item_data']['description'])) {
                        $finalDescription = $existingSquareItem['object']['item_data']['description'];
                    }
                }

                $itemObject = [
                    'type' => 'ITEM',
                    'id' => $validSquareId ?: $itemObjectId,
                    'version' => $itemVersion ?? null,
                    'item_data' => [
                        'name' => $itemData['item_name'],
                        'description' => $finalDescription,
                        'abbreviation' => strtoupper(substr($itemData['item_name'], 0, 3)),
                        'product_type' => 'REGULAR',
                        'present_at_all_locations' => true,
                        'modifier_list_info' => $modifierListInfo,
                        'category_id' => $squareCategoryId,
                    ],
                ];

                if ($modifierListObject) {
                    $itemObjects[] = $modifierListObject;
                }

                $itemObjects = array_merge($itemObjects, $variationObjects);
                $itemObjects[] = $itemObject;
                $objectIdMap[$itemId] = $validSquareId ?: $itemObjectId;
            }

            \Log::info("Final catalog object count before chunking", ['count' => count($itemObjects)]);

            $chunks = array_chunk($itemObjects, 500);
            $allMappings = [];

            foreach ($chunks as $chunk) {
                $payload = [
                    'idempotency_key' => uniqid('menu_sync_', true),
                    'batches' => [
                        ['objects' => $chunk],
                    ],
                ];

                \Log::info("Sending chunk", ['count' => count($chunk)]);

                $response = Http::withToken($cafe->square_access_token)
                    ->post("https://connect.squareup.com/v2/catalog/batch-upsert", $payload);

                \Log::info("Square API Response", [
                    'status' => $response->status(),
                    'successful' => $response->successful(),
                    'body' => $response->body(),
                ]);

                if (!$response->successful()) {
                    throw new \Exception("Square batch upsert failed: " . $response->body());
                }

                $mappings = $response->json()['id_mappings'] ?? [];
                $allMappings = array_merge($allMappings, $mappings);
            }

            foreach ($allMappings as $map) {
                if (!empty($map['client_object_id']) && !empty($map['object_id'])) {
                    foreach ($objectIdMap as $localId => $clientId) {
                        if ($clientId == $map['client_object_id']) {
                            DB::table('cafe_menu_items')->where('id', $localId)->update([
                                'square_item_id' => $map['object_id'],
                            ]);
                        }
                    }

                    foreach ($modifierListIdMap as $localId => $modClientId) {
                        if ($modClientId == $map['client_object_id']) {
                            DB::table('cafe_menu_items')->where('id', $localId)->update([
                                'square_modifier_list_id' => $map['object_id'],
                            ]);
                        }
                    }
                }
            }

            return response()->json(['success' => true, 'message' => 'Menu synced to Square successfully']);
        } catch (\Exception $e) {
            return response()->json(['error' => 'Failed to sync menu: ' . $e->getMessage()], 500);
        }
    }

    public function deleteItemAndModifiersOnSquare(Request $request, $itemId)
    {
        $cafe = Cafe::find($request->user->id);
        if (!$cafe) {
            return response()->json(['error' => 'Cafe not found'], 404);
        }
        if (empty($cafe->square_access_token)) {
            return response()->json(['error' => 'Square account not connected'], 400);
        }

        $itemDetails = $this->getItemDetail($itemId);

        if (!$itemDetails || !$itemDetails['square_item_id']) {
            return ['success' => false, 'message' => 'Item or Square item ID not found'];
        }

        $squareItemId = $itemDetails['square_item_id'];

        $squareToken = $cafe->square_access_token;
        // --- Use your logic for modifier list ID ---
        $existingModifierListId = $itemDetails['square_modifier_list_id'] ?? null;
        \Log::info("existingModifierListId", ['existingModifierListId' => $existingModifierListId]);

        $modifierListVersion = null;
        $itemModifierListId = null;

        if (!empty($existingModifierListId)) {
            $squareObj = $this->getSquareObjectNew($existingModifierListId, $cafe->square_access_token);
            if ($squareObj && empty($squareObj['errors'])) {
                $modifierListVersion = $squareObj['object']['version'] ?? null;
                $itemModifierListId = $existingModifierListId;
            }
            $deleteModifierResponse = Http::withToken($squareToken)
                ->delete("https://connect.squareup.com/v2/catalog/object/{$itemModifierListId}");

            if (!$deleteModifierResponse->successful()) {
                \Log::error("Failed to delete modifier list {$itemModifierListId}", ['response' => $deleteModifierResponse->body()]);
            } else {
                \Log::info("Deleted modifier list {$itemModifierListId}");
            }
        }



        // Delete item
        $deleteItemResponse = Http::withToken($squareToken)
            ->delete("https://connect.squareup.com/v2/catalog/object/{$squareItemId}");

        if (!$deleteItemResponse->successful()) {
            \Log::error("Failed to delete item {$squareItemId}", ['response' => $deleteItemResponse->body()]);
            return ['success' => false, 'message' => 'Failed to delete item on Square'];
        }

        return ['success' => true, 'message' => 'Item and modifier list deleted successfully from Square'];
    }





    private function getSquareObject($objectId, $accessToken)
    {
        $response = Http::withToken($accessToken)
            ->get("https://connect.squareup.com/v2/catalog/object/{$objectId}");

        if ($response->successful()) {
            return $response->json();
        }

        return null;
    }

    private function sendSquareBatchUpsert(array $catalogObjects, $cafe)
    {
        $payload = [
            'idempotency_key' => uniqid('sync_', true),
            'batches' => [['objects' => $catalogObjects]],
        ];

        $response = Http::withToken($cafe->square_access_token)
            ->post("https://connect.squareup.com/v2/catalog/batch-upsert", $payload);

        \Log::info("Square API Response", [
            'status' => $response->status(),
            'successful' => $response->successful(),
            'body' => $response->body(),
        ]);

        if (!$response->successful()) {
            throw new \Exception("Square batch upsert failed: " . $response->body());
        }

        return $response;
    }

    public function syncModifiersToSquare(Request $request)
    {
        $cafe = Cafe::find($request->user->id);
        if (!$cafe) {
            return response()->json(['error' => 'Cafe not found'], 404);
        }
        if (empty($cafe->square_access_token)) {
            return response()->json(['error' => 'Square account not connected'], 400);
        }

        $allAddonSizes = DB::table('addon_sizes')
            ->where('cafe_id', $cafe->id)
            ->get(['id', 'addon_size_name', 'square_modifier_id']);

        $modifierObjects = [];
        $modifierIdMap = [];

        foreach ($allAddonSizes as $addon) {
            $addonPrice = AddonSizeCafeItem::where('addon_size_id', $addon->id)->value('addon_size_price');

            $modifierOptionId = "#OPTION_{$addon->id}_" . uniqid();
            $existingSquareId = $addon->square_modifier_id;
            $version = null;

            // ✅ Check if modifier exists on Square
            if (!empty($existingSquareId)) {
                $squareObjResponse = Http::withToken($cafe->square_access_token)
                    ->get("https://connect.squareup.com/v2/catalog/object/{$existingSquareId}");

                if ($squareObjResponse->successful() && empty($squareObjResponse->json()['errors'])) {
                    $version = $squareObjResponse->json()['object']['version'] ?? null;
                } else {
                    // Object not found → force create new
                    $existingSquareId = null;
                }
            }

            // ✅ Build modifier option
            $modifierOption = [
                'type' => 'MODIFIER',
                'id' => $modifierOptionId,
                'modifier_data' => [
                    'name' => $addon->addon_size_name,
                    'price_money' => [
                        'amount' => (int) ($addonPrice * 100),
                        'currency' => 'GBP',
                    ],
                ],
            ];

            // ✅ Build modifier list
            $modifierListObject = [
                'type' => 'MODIFIER_LIST',
                'id' => $existingSquareId ?: "#MODLIST_{$addon->id}_" . uniqid(),
                'modifier_list_data' => [
                    'name' => $addon->addon_size_name . " Addons",
                    'modifiers' => [$modifierOption],
                    'present_at_all_locations' => true,
                ],
            ];

            if ($version) {
                $modifierListObject['version'] = $version;
            }

            $modifierObjects[] = $modifierListObject;
            $modifierIdMap[$addon->id] = $modifierListObject['id'];
        }

        $chunks = array_chunk($modifierObjects, 500);

        foreach ($chunks as $chunk) {
            $payload = [
                'idempotency_key' => uniqid('mod_sync_', true),
                'batches' => [
                    ['objects' => $chunk],
                ],
            ];

            $response = Http::withToken($cafe->square_access_token)
                ->post("https://connect.squareup.com/v2/catalog/batch-upsert", $payload);

            \Log::info("Square Modifier API Response", [
                'status' => $response->status(),
                'successful' => $response->successful(),
                'body' => $response->body(),
            ]);

            if (!$response->successful()) {
                throw new \Exception("Square modifier batch upsert failed: " . $response->body());
            }

            // ✅ Update DB with returned Square IDs
            $mappings = $response->json()['id_mappings'] ?? [];
            foreach ($mappings as $map) {
                foreach ($modifierIdMap as $addonId => $clientModifierId) {
                    if ($clientModifierId === $map['client_object_id']) {
                        DB::table('addon_sizes')->where('id', $addonId)->update([
                            'square_modifier_id' => $map['object_id'],
                        ]);
                    }
                }
            }
        }

        \Log::info("Modifier sync completed successfully (update or create).");
        return response()->json(['success' => true, 'message' => 'Modifiers synced to Square successfully']);
    }

    public function syncCategoriesToSquare(Request $request)
    {
        $cafe = Cafe::find($request->user->id);
        if (!$cafe) {
            return response()->json(['error' => 'Cafe not found'], 404);
        }
        if (empty($cafe->square_access_token)) {
            return response()->json(['error' => 'Square account not connected'], 400);
        }

        $menus = DB::table('cafe_menus')
            ->where('cafe_id', $cafe->id)
            ->get(['id', 'menu_name', 'square_category_id']);

        // 🟡 Step 1: Fetch all existing Square categories
        $squareCategories = [];
        $cursor = null;

        do {
            $url = "https://connect.squareup.com/v2/catalog/list?types=CATEGORY";
            if ($cursor) {
                $url .= "&cursor={$cursor}";
            }

            $res = Http::withToken($cafe->square_access_token)->get($url);
            if (!$res->successful()) {
                throw new \Exception("Failed to fetch categories from Square: " . $res->body());
            }

            $data = $res->json();
            foreach ($data['objects'] ?? [] as $obj) {
                $name = strtolower(trim($obj['category_data']['name']));
                $squareCategories[$name] = [
                    'id' => $obj['id'],
                    'version' => $obj['version'] ?? null
                ];
            }

            $cursor = $data['cursor'] ?? null;
        } while ($cursor);

        $categoryObjects = [];
        $categoryIdMap = [];

        foreach ($menus as $menu) {
            $nameKey = strtolower(trim($menu->menu_name));
            $existingSquareId = $menu->square_category_id;
            $version = null;

            // 🟢 Try to match by name if ID is missing
            if (empty($existingSquareId) && isset($squareCategories[$nameKey])) {
                $existingSquareId = $squareCategories[$nameKey]['id'];
                $version = $squareCategories[$nameKey]['version'];

                // 🔁 Update local DB with matched ID
                DB::table('cafe_menus')->where('id', $menu->id)->update([
                    'square_category_id' => $existingSquareId,
                ]);

                continue; // No need to recreate
            }

            // 🟠 If still no ID, generate temporary ID for upsert
            $categoryId = $existingSquareId ?: "#CAT_{$menu->id}_" . uniqid();

            // 🟡 If existing, fetch version (only if not already set above)
            if ($existingSquareId && !$version) {
                $squareObjResponse = Http::withToken($cafe->square_access_token)
                    ->get("https://connect.squareup.com/v2/catalog/object/{$existingSquareId}");

                if ($squareObjResponse->successful() && empty($squareObjResponse->json()['errors'])) {
                    $version = $squareObjResponse->json()['object']['version'] ?? null;
                }
            }

            $categoryObject = [
                'type' => 'CATEGORY',
                'id' => $categoryId,
                'category_data' => [
                    'name' => $menu->menu_name,
                ],
            ];

            if ($version) {
                $categoryObject['version'] = $version;
            }

            $categoryObjects[] = $categoryObject;
            $categoryIdMap[$menu->id] = $categoryId;
        }

        // 🔄 Batch upsert only if needed
        if (count($categoryObjects)) {
            $chunks = array_chunk($categoryObjects, 500);
            foreach ($chunks as $chunk) {
                $payload = [
                    'idempotency_key' => uniqid('cat_sync_', true),
                    'batches' => [['objects' => $chunk]],
                ];

                \Log::info("Final category chunk payload", $payload);

                $response = Http::withToken($cafe->square_access_token)
                    ->post("https://connect.squareup.com/v2/catalog/batch-upsert", $payload);

                \Log::info("Square Category API Response", [
                    'status' => $response->status(),
                    'successful' => $response->successful(),
                    'body' => $response->body(),
                ]);

                if (!$response->successful()) {
                    throw new \Exception("Square category batch upsert failed: " . $response->body());
                }

                // 🔁 Update local DB with new object IDs
                $mappings = $response->json()['id_mappings'] ?? [];
                foreach ($mappings as $map) {
                    foreach ($categoryIdMap as $menuId => $clientCategoryId) {
                        if ($clientCategoryId === $map['client_object_id']) {
                            DB::table('cafe_menus')->where('id', $menuId)->update([
                                'square_category_id' => $map['object_id'],
                            ]);
                        }
                    }
                }
            }
        }

        \Log::info("Category sync completed successfully.");
        return response()->json(['success' => true, 'message' => 'Categories synced to Square successfully']);
    }

    public function oldsyncCategoriesToSquare(Request $request)
    {
        $cafe = Cafe::find($request->user->id);
        if (!$cafe) {
            return response()->json(['error' => 'Cafe not found'], 404);
        }
        if (empty($cafe->square_access_token)) {
            return response()->json(['error' => 'Square account not connected'], 400);
        }

        $menus = DB::table('cafe_menus')
            ->where('cafe_id', $cafe->id)
            ->get(['id', 'menu_name', 'square_category_id']);

        $categoryObjects = [];
        $categoryIdMap = [];

        foreach ($menus as $menu) {
            $existingSquareId = $menu->square_category_id;
            $version = null;

            if (!empty($existingSquareId)) {
                // ✅ Fetch latest version from Square
                $squareObjResponse = Http::withToken($cafe->square_access_token)
                    ->get("https://connect.squareup.com/v2/catalog/object/{$existingSquareId}");

                if ($squareObjResponse->successful() && empty($squareObjResponse->json()['errors'])) {
                    $version = $squareObjResponse->json()['object']['version'] ?? null;
                }
            }

            if (!empty($existingSquareId)) {
                $categoryId = $existingSquareId;
            } else {
                $categoryId = "#CAT_{$menu->id}_" . uniqid();
            }

            $categoryObject = [
                'type' => 'CATEGORY',
                'id' => $categoryId,
                'category_data' => [
                    'name' => $menu->menu_name,
                ],
            ];

            if ($version) {
                $categoryObject['version'] = $version;
            }

            $categoryObjects[] = $categoryObject;
            $categoryIdMap[$menu->id] = $categoryId;
        }

        $chunks = array_chunk($categoryObjects, 500);

        foreach ($chunks as $chunk) {
            $payload = [
                'idempotency_key' => uniqid('cat_sync_', true),
                'batches' => [
                    ['objects' => $chunk],
                ],
            ];

            \Log::info("Final category chunk payload", $payload);

            $response = Http::withToken($cafe->square_access_token)
                ->post("https://connect.squareup.com/v2/catalog/batch-upsert", $payload);

            \Log::info("Square Category API Response", [
                'status' => $response->status(),
                'successful' => $response->successful(),
                'body' => $response->body(),
            ]);

            if (!$response->successful()) {
                throw new \Exception("Square category batch upsert failed: " . $response->body());
            }

            $mappings = $response->json()['id_mappings'] ?? [];
            foreach ($mappings as $map) {
                foreach ($categoryIdMap as $menuId => $clientCategoryId) {
                    if ($clientCategoryId === $map['client_object_id']) {
                        DB::table('cafe_menus')->where('id', $menuId)->update([
                            'square_category_id' => $map['object_id'],
                        ]);
                    }
                }
            }
        }

        \Log::info("Category sync completed successfully.");
        return response()->json(['success' => true, 'message' => 'Categories synced to Square successfully']);
    }


    public function syncMenuToSquareNew(Request $request)
    {
        try {
            $cafe = Cafe::find($request->user->id);
            if (!$cafe) {
                return response()->json(['error' => 'Cafe not found'], 404);
            }
            if (empty($cafe->square_access_token)) {
                return response()->json(['error' => 'Square account not connected'], 400);
            }

            $addonSizes = DB::table('addon_sizes')
                ->where('status', 1)
                ->whereNotNull('square_modifier_id')
                ->get(['id', 'square_modifier_id']);

            $modifierIdMap = [];
            foreach ($addonSizes as $addon) {
                $modifierIdMap[$addon->id] = $addon->square_modifier_id;
            }

            $menuItemIds = DB::table('cafe_menu_items')
                ->where('cafe_id', $cafe->id)
                ->where('item_deleted_at', 0)
                ->pluck('id');

            if ($menuItemIds->isEmpty()) {
                return response()->json(['error' => 'No menu items to sync'], 404);
            }

            $itemObjects = [];
            $objectIdMap = [];

            foreach ($menuItemIds as $itemId) {
                $itemData = $this->getItemDetail($itemId);
                if (!$itemData) {
                    continue;
                }

                // ✅ Refresh category ID from DB
                $menu = DB::table('cafe_menus')->where('id', $itemData['cafe_menu_id'])->first();
                $squareCategoryId = $menu->square_category_id ?? null;

                // ✅ Check if category exists on Square
                if (!empty($squareCategoryId)) {
                    $squareCatResponse = Http::withToken($cafe->square_access_token)
                        ->get("https://connect.squareup.com/v2/catalog/object/{$squareCategoryId}");

                    if (!$squareCatResponse->successful() || isset($squareCatResponse->json()['errors'])) {
                        \Log::warning("Category not found on Square for menu ID {$menu->id}, ignoring category for this item.");
                        $squareCategoryId = null; // fallback to no category
                    }
                }

                \Log::info("Using category ID for item {$itemData['item_name']}", [
                    'category_id' => $squareCategoryId
                ]);

                $squareItemId = $itemData['square_item_id'] ?? null;
                $validSquareId = null;
                $itemVersion = null;

                if ($squareItemId) {
                    $squareObj = $this->getSquareObjectNew($squareItemId, $cafe->square_access_token);
                    if ($squareObj && empty($squareObj['errors'])) {
                        $validSquareId = $squareItemId;
                        $itemVersion = $squareObj['object']['version'] ?? null;
                    }
                }

                $itemObjectId = $validSquareId ?: "#ITEM_{$itemId}_" . uniqid();
                $variationObjects = [];

                if (!empty($itemData['item_size_prices'])) {
                    foreach ($itemData['item_size_prices'] as $size) {
                        $price = floatval($size->item_size_price ?? 0);
                        if ($price <= 0)
                            continue;

                        $variationId = "#VARIATION_{$itemId}_{$size->size_id}_" . uniqid();

                        $variationObjects[] = [
                            'type' => 'ITEM_VARIATION',
                            'id' => $variationId,
                            'item_variation_data' => [
                                'item_id' => $itemObjectId,
                                'name' => "Size {$size->size_id}",
                                'pricing_type' => 'FIXED_PRICING',
                                'price_money' => [
                                    'amount' => (int) ($price * 100),
                                    'currency' => 'GBP',
                                ],
                                'present_at_all_locations' => true,
                            ],
                        ];
                    }
                } else {
                    $defaultPrice = floatval($itemData['item_price'] ?? 0);
                    if ($defaultPrice <= 0)
                        continue;

                    $variationId = "#VARIATION_{$itemId}_default_" . uniqid();

                    $variationObjects[] = [
                        'type' => 'ITEM_VARIATION',
                        'id' => $variationId,
                        'item_variation_data' => [
                            'item_id' => $itemObjectId,
                            'name' => 'Regular',
                            'pricing_type' => 'FIXED_PRICING',
                            'price_money' => [
                                'amount' => (int) ($defaultPrice * 100),
                                'currency' => 'GBP',
                            ],
                            'present_at_all_locations' => true,
                        ],
                    ];
                }

                $assignedModifierLists = [];
                if (!empty($itemData['optionSizeCafeItems'])) {
                    foreach ($itemData['optionSizeCafeItems'] as $addon) {
                        $addonSizeId = $addon['addon_size_id'];
                        if (!empty($modifierIdMap[$addonSizeId])) {
                            $assignedModifierLists[] = [
                                'modifier_list_id' => $modifierIdMap[$addonSizeId],
                                'enabled' => true,
                            ];
                        }
                    }
                }

                $itemObject = [
                    'type' => 'ITEM',
                    'id' => $validSquareId ?: $itemObjectId,
                    'version' => $itemVersion ?? null,
                    'item_data' => [
                        'name' => $itemData['item_name'],
                        'description' => $itemData['item_description'] ?? '',
                        'abbreviation' => strtoupper(substr($itemData['item_name'], 0, 3)),
                        'present_at_all_locations' => true,
                        'modifier_list_info' => $assignedModifierLists,
                        'category_id' => $squareCategoryId,
                    ],
                ];

                $itemObjects = array_merge($itemObjects, $variationObjects);
                $itemObjects[] = $itemObject;
                $objectIdMap[$itemId] = $validSquareId ?: $itemObjectId;
            }

            \Log::info("Final catalog object count before chunking", ['count' => count($itemObjects)]);

            $chunks = array_chunk($itemObjects, 500);
            $allMappings = [];

            foreach ($chunks as $chunk) {
                $payload = [
                    'idempotency_key' => uniqid('menu_sync_', true),
                    'batches' => [
                        ['objects' => $chunk],
                    ],
                ];

                \Log::info("Sending chunk", ['count' => count($chunk)]);

                $response = Http::withToken($cafe->square_access_token)
                    ->post("https://connect.squareup.com/v2/catalog/batch-upsert", $payload);

                \Log::info("Square API Response", [
                    'status' => $response->status(),
                    'successful' => $response->successful(),
                    'body' => $response->body(),
                ]);

                if (!$response->successful()) {
                    throw new \Exception("Square batch upsert failed: " . $response->body());
                }

                $mappings = $response->json()['id_mappings'] ?? [];
                $allMappings = array_merge($allMappings, $mappings);
            }

            foreach ($allMappings as $map) {
                if (!empty($map['client_object_id']) && !empty($map['object_id'])) {
                    foreach ($objectIdMap as $localId => $clientId) {
                        if ($clientId == $map['client_object_id']) {
                            DB::table('cafe_menu_items')->where('id', $localId)->update([
                                'square_item_id' => $map['object_id'],
                            ]);
                        }
                    }
                }
            }

            return response()->json(['success' => true, 'message' => 'Menu synced to Square successfully']);
        } catch (\Exception $e) {
            return response()->json(['error' => 'Failed to sync menu: ' . $e->getMessage()], 500);
        }
    }



    private function sendSquareBatchUpsertNew(array $chunk, $cafe)
    {
        if (count($chunk) > 1000) {
            throw new \Exception("Chunk too large before sending: " . count($chunk));
        }

        $payload = [
            'idempotency_key' => uniqid('sync_', true),
            'batches' => [
                [
                    'objects' => $chunk,
                ],
            ],
        ];

        \Log::info("Sending SINGLE API REQUEST with chunk", ['count' => count($chunk)]);

        $response = Http::withToken($cafe->square_access_token)
            ->post("https://connect.squareup.com/v2/catalog/batch-upsert", $payload);

        \Log::info("Square API Response", [
            'status' => $response->status(),
            'successful' => $response->successful(),
            'body' => $response->body(),
        ]);

        if (!$response->successful()) {
            throw new \Exception("Square batch upsert failed: " . $response->body());
        }

        return $response->json()['id_mappings'] ?? [];
    }


    private function getItemDetail($itemId)
    {
        $model = DB::table('cafe_menu_items')
            ->join('pre_defined_item_images', 'pre_defined_item_images.id', '=', 'cafe_menu_items.item_image_id')
            ->join('cafe_menus', 'cafe_menus.id', '=', 'cafe_menu_items.cafe_menu_id')
            ->where('cafe_menu_items.id', $itemId)
            ->where('cafe_menu_items.item_deleted_at', 0)
            ->select(
                'cafe_menu_items.*',
                'pre_defined_item_images.item_image',
                'cafe_menus.menu_name as item_category',
                'cafe_menus.square_category_id',
                'cafe_menu_items.item_description',         // ✅ explicitly select
                'cafe_menu_items.cafe_menu_id'              // ✅ ensure this is available
            )
            ->first();

        if (!$model) {
            return null;
        }

        $optionSizeCafeItems = DB::table('addon_size_cafe_items')
            ->join('addon_sizes', 'addon_sizes.id', '=', 'addon_size_cafe_items.addon_size_id')
            ->where('item_id', $model->id)
            ->get([
                'addon_size_cafe_items.addon_size_id',
                'addon_size_cafe_items.addon_size_price',
                'addon_sizes.addon_size_name',
                'addon_sizes.status',
            ])
            ->map(function ($item) {
                return [
                    'addon_size_id' => $item->addon_size_id,
                    'addon_size_name' => $item->addon_size_name,
                    'addon_size_price' => $item->addon_size_price,
                    'status' => $item->status,
                ];
            })->toArray();

        $addonItems = DB::table('suggested_items')
            ->where('item_id', $model->id)
            ->pluck('suggested_item_id');

        $itemPriceSize = DB::table('cafe_menu_item_sizes')
            ->where('item_id', $model->id)
            ->get();

        return [
            'square_item_id' => $model->square_item_id,
            'square_modifier_list_id' => $model->square_modifier_list_id,
            'item_image_id' => $model->item_image_id ?? 1,
            'item_name' => $model->item_name,
            'item_category' => $model->item_category,               // From cafe_menus.menu_name
            'item_type' => $model->item_type,
            'item_description' => $model->item_description,         // ✅ now available
            'item_price' => $model->item_price,
            'cafe_menu_id' => $model->cafe_menu_id,                 // ✅ now available
            'square_category_id' => $model->square_category_id,
            'item_size_prices' => $itemPriceSize,
            'optionSizeCafeItems' => $optionSizeCafeItems,
            'addon_items' => $addonItems,
        ];
    }

    private function oldgetItemDetail($itemId)
    {
        $model = DB::table('cafe_menu_items')
            ->join('pre_defined_item_images', 'pre_defined_item_images.id', '=', 'cafe_menu_items.item_image_id')
            ->join('cafe_menus', 'cafe_menus.id', '=', 'cafe_menu_items.cafe_menu_id')
            ->where('cafe_menu_items.id', $itemId)
            ->where('cafe_menu_items.item_deleted_at', 0)
            ->select('cafe_menu_items.*', 'pre_defined_item_images.item_image', 'cafe_menus.menu_name as item_category', 'cafe_menus.square_category_id')
            ->first();

        if (!$model)
            return null;

        $optionSizeCafeItems = DB::table('addon_size_cafe_items')
            ->join('addon_sizes', 'addon_sizes.id', '=', 'addon_size_cafe_items.addon_size_id')
            ->where('item_id', $model->id)
            ->get(['addon_size_cafe_items.addon_size_id', 'addon_size_cafe_items.addon_size_price', 'addon_sizes.addon_size_name', 'addon_sizes.status'])
            ->map(function ($item) {
                return [
                    'addon_size_id' => $item->addon_size_id,
                    'addon_size_name' => $item->addon_size_name,
                    'addon_size_price' => $item->addon_size_price,
                    'status' => $item->status,
                ];
            })->toArray();

        $addonItems = DB::table('suggested_items')
            ->where('item_id', $model->id)
            ->pluck('suggested_item_id');

        $itemPriceSize = DB::table('cafe_menu_item_sizes')
            ->where('item_id', $model->id)
            ->get();


        return [
            'square_item_id' => $model->square_item_id,
            'square_modifier_list_id' => $model->square_modifier_list_id,
            'item_image_id' => $model->item_image_id ?? 1,
            'item_name' => $model->item_name,
            'item_category' => $model->cafe_menu_id,
            'item_type' => $model->item_type,
            'item_description' => $model->item_description,
            'item_price' => $model->item_price,
            'cafe_menu_id' => $model->cafe_menu_id,
            'square_category_id' => $model->square_category_id,
            'item_size_prices' => $itemPriceSize,
            'optionSizeCafeItems' => $optionSizeCafeItems,
            'addon_items' => $addonItems,
        ];
    }

    private function getSquareObjectNew($objectId, $accessToken)
    {
        $response = Http::withToken($accessToken)
            ->get("https://connect.squareup.com/v2/catalog/object/{$objectId}");

        if ($response->successful()) {
            return $response->json();
        }

        return null;
    }


    public function syncMenuFromSquare(Request $request)
    {
        $cafe = Cafe::find($request->user->id);
        if (!$cafe || empty($cafe->square_access_token)) {
            return response()->json(['error' => 'Cafe not found or Square not connected'], 404);
        }

        // 1. Fetch Items from Square
        $squareItemsResp = Http::withToken($cafe->square_access_token)
            ->get('https://connect.squareup.com/v2/catalog/list?types=ITEM');
//dd($squareItemsResp);
        if (!$squareItemsResp->successful()) {
            return response()->json(['error' => 'Failed to fetch Square items'], 500);
        }

        $squareItems = $squareItemsResp->json()['objects'] ?? [];

        // 2. Fetch all Square categories (for name mapping)
        $squareCatalogResp = Http::withToken($cafe->square_access_token)
            ->get('https://connect.squareup.com/v2/catalog/list?types=CATEGORY');
        $squareFetchedData = $squareCatalogResp->json();

        $squareCategories = [];
        foreach ($squareFetchedData['objects'] ?? [] as $obj) {
            if ($obj['type'] === 'CATEGORY') {
                $id = $obj['id'];
                $name = $obj['category_data']['name'] ?? null;
                if ($name) {
                    $squareCategories[$id] = [
                        'id' => $id,
                        'name' => $name,
                    ];
                }
            }
        }

        // 3. Local reference maps
        $categoryMap = CafeMenu::where('cafe_id', $cafe->id)
            ->pluck('id', 'square_category_id')
            ->toArray();

        $categoryNameMap = CafeMenu::where('cafe_id', $cafe->id)
            ->get()
            ->mapWithKeys(function ($menu) {
                return [strtolower(trim($menu->menu_name)) => $menu->id];
            })->toArray();

        $othersCategory = CafeMenu::where('menu_name', 'Other')
            ->where('cafe_id', $cafe->id)
            ->first();

        $othersCategoryId = $othersCategory ? $othersCategory->id : null;

        // 4. Prepare size and addon references
        $sizes = Sizes::all();
        $sizeMap = [];
        foreach ($sizes as $sz)
            $sizeMap[strtolower($sz->size_name)] = $sz->id;
        $mediumSizeId = $sizeMap['medium'] ?? null;

        $addons = AddonSize::where('cafe_id', $cafe->id)->get();
        $addonMap = [];
        foreach ($addons as $ad)
            $addonMap[$ad->square_modifier_id] = $ad->id;

        // 5. Process each item
        foreach ($squareItems as $item) {
            $itemData = $item['item_data'] ?? [];
            $squareItemId = $item['id'];
            $itemName = $itemData['name'] ?? 'Unnamed';
            $itemDescription = $itemData['description'] ?? $itemData['name'];
            $categoryId = $itemData['category_id'] ?? ($itemData['categories'][0]['id'] ?? null);

            // Get category name from Square
            $categoryName = null;
            if (!empty($categoryId) && isset($squareCategories[$categoryId])) {
                $categoryName = $squareCategories[$categoryId]['name'];
            }

            // Match to local menu category
            $cafeMenuId = null;

            // Step 1: Match by square_category_id
            if (!empty($categoryId) && isset($categoryMap[$categoryId])) {
                $cafeMenuId = $categoryMap[$categoryId];
            }
            // Step 2: Match by name if ID fails
            elseif (!empty($categoryName)) {
                $normalizedCategoryName = strtolower(trim($categoryName));
                if (isset($categoryNameMap[$normalizedCategoryName])) {
                    $cafeMenuId = $categoryNameMap[$normalizedCategoryName];

                    // Save Square category ID to local menu if missing
                    DB::table('cafe_menus')
                        ->where('id', $cafeMenuId)
                        ->whereNull('square_category_id')
                        ->update(['square_category_id' => $categoryId]);
                }
            }

            // Step 3: Fallback to "Other" if no match
            if (empty($cafeMenuId)) {
                $cafeMenuId = $othersCategoryId;
            }

            // Get main price (first variation)
            $firstVariationPrice = 0;
            if (!empty($itemData['variations'][0]['item_variation_data']['price_money']['amount'])) {
                $firstVariationPrice = $itemData['variations'][0]['item_variation_data']['price_money']['amount'] / 100;
            }

            // Upsert item
            $localItem = CafeMenuItem::where('square_item_id', $squareItemId)
                ->where('cafe_id', $cafe->id)
                ->first();

            $itemAttrs = [
                'item_name' => $itemName,
                'item_description' => $itemDescription,
                'cafe_id' => $cafe->id,
                'item_image_id' => 1,
                'cafe_menu_id' => $cafeMenuId,
                'item_price' => $firstVariationPrice,
                'item_type' => 1,
                'status' => 1,
                'updated_at' => now(),
            ];

            if (!$localItem) {
                $itemAttrs['created_at'] = now();
                $itemAttrs['square_item_id'] = $squareItemId;
                $localItem = CafeMenuItem::create($itemAttrs);
            } else {
                $localItem->update($itemAttrs);
            }
            $itemId = $localItem->id;

            // Sync sizes
            if (!empty($itemData['variations'])) {
                foreach ($itemData['variations'] as $var) {
                    $varData = $var['item_variation_data'] ?? [];
                    $sizeName = strtolower($varData['name'] ?? 'medium');
                    $sizeId = $sizeMap[$sizeName] ?? $mediumSizeId;
                    if (!$sizeId)
                        continue;

                    CafeMenuItemSize::updateOrCreate(
                        ['item_id' => $itemId, 'size_id' => $sizeId],
                        [
                            'item_size_price' => ($varData['price_money']['amount'] ?? 0) / 100,
                            'updated_at' => now(),
                        ]
                    );
                }
            }

            // Sync addons
            if (!empty($itemData['modifier_list_info'])) {
                foreach ($itemData['modifier_list_info'] as $modInfo) {
                    $modifierListId = $modInfo['modifier_list_id'] ?? null;
                    if ($modifierListId && isset($addonMap[$modifierListId])) {
                        AddonSizeCafeItem::updateOrCreate(
                            ['addon_size_id' => $addonMap[$modifierListId], 'item_id' => $itemId],
                            [
                                'addon_size_price' => 0,
                                'created_at' => now(),
                                'updated_at' => now(),
                            ]
                        );
                    }
                }
            }
        }

        return response()->json(['message' => 'Menu items synced from Square.']);
    }




}