<?php

namespace App\Http\Controllers\API;

use App\Http\Controllers\Controller;
use App\Http\Requests\FoodicsOrderRequest;
use App\Models\FoodicsListOrder;
use App\Models\FoodicsMapping;
use App\Models\FoodicsOrderToken;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Facades\DB;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Request as GuzzleRequest;
use GuzzleHttp\Exception\RequestException;
use Psr\Http\Message\ResponseInterface;

class FoodicsOrderController extends Controller
{

    public function getOrdersFromFoodicsx(FoodicsOrderRequest $request)
    {
        $validator = Validator::make($request->all(), $request->rulesForGetOrdersFromFoodics($request));

        if ($validator->fails()) {
            return $validator->errors()->messages();
        }

        $mode = $request->mode;
        $brand_id = $request->brand_id ?? 0;

        switch ($mode) {
            case 'list_orders':
                return $this->foodicsListOrders($brand_id);
                break;

            case 'extract_orders':
                return $this->extractFoodicsOrderData();
                break;

            case 'post_orders':
                return $this->postFoodicsOrderData();
                break;

            default:
                return [];
                break;
        }
    }

    public function getOrdersFromFoodics($mode)
    {
        // $validator = Validator::make($request->all(), $request->rulesForGetOrdersFromFoodics($request));

        // if ($validator->fails()) {
        //     return $validator->errors()->messages();
        // }
        # $mode = $request->mode;
        $brand_id = 0;

        switch ($mode) {
            case 'list_orders':
                return $this->foodicsListOrders($brand_id);
                break;

            case 'extract_orders':
                return $this->extractFoodicsOrderData();
                break;

            case 'post_orders':
                return $this->postFoodicsOrderData();
                break;

            default:
                return [];
                break;
        }
    }

    private function buildApiUrl(string $branchIds, int $page): string
    {
        $baseUrl = env('FOODICS_API_URL') . "/orders";
        $queryParams = [
            'page' => $page,
            'filter' => [
                'branch_id' => $branchIds,
                'status' => 4,
                // 'created_on'=> '2021-09-01',
            ],
            'sort' => '-reference',
            'include' => 'branch,products.product,products.options.modifier_option,combos.products',
        ];

        return $baseUrl . '?' . http_build_query($queryParams);
    }

    private function makeRetryableRequest(Client $client, string $apiUrl, string $accessToken, int $maxRetries, int $initialDelay, int $brand_id): ?ResponseInterface
    {
        $retryCount = 0;
        $delayBetweenRetries = $initialDelay;

        while ($retryCount < $maxRetries) {
            try {
                // Make the request
                $response = $client->request('GET', $apiUrl, [
                    'headers' => [
                        'Authorization' => 'Bearer ' . $accessToken,
                        'Accept' => 'application/json',
                        'Content-Type' => 'application/json',
                    ],
                    'http_errors' => false, // Prevent Guzzle from throwing exceptions for HTTP errors
                ]);

                // Check the status code
                $statusCode = $response->getStatusCode();

                // If successful, return the response
                if ($statusCode === 200) {
                    return $response;
                } elseif ($statusCode === 500) {
                    // Log the 500 error and retry
                    echo "Server error (500), retrying...\n";
                    $retryCount++;
                    if ($retryCount < $maxRetries) {
                        sleep($delayBetweenRetries); // Delay before retrying
                        $delayBetweenRetries *= 2; // Exponential backoff
                    } else {
                        echo "Max retry attempts reached. Response code: 500\n";
                        return null;
                    }
                } elseif ($statusCode === 401) {
                    // Handle 401 Unauthorized
                    #FoodicsOrderToken::deleteBrandToken($brand_id);
                    # echo "Unauthorized (401), please check your credentials.\n";
                    return null;
                } else {
                    // Handle non-500 and non-401 status codes
                    echo "Unexpected response code: $statusCode\n";
                    return null;
                }
            } catch (RequestException $e) {
                // Log the request exception and break the loop
                echo "Request failed: " . $e->getMessage() . "\n";
                return null;
            }
        }

        return null; // Return null if all retries fail
    }

    public function foodicsListOrders($brand_id)
    {
        $client = new Client();
        $maxRetries = 3;  // Set the maximum number of retries
        $initialDelay = 2; // Delay between retries in seconds

        // Fetch tokens from API
        if ($brand_id > 0) {
            $foodicsTokens = $this->getTokensFromAPI('brand_id', $brand_id);
            // Add any additional logic here that should only run if $brand_id > 0
        } else {
            // Handle the case where $brand_id is not greater than 0, if needed
            $foodicsTokens = $this->getTokensFromAPI();
        }

        // Iterate through each token and make requests
        $responseMessages = [];
        foreach ($foodicsTokens as $foodicsToken) {
            $branchIds = $foodicsToken['branch_ids'];
            $accessToken = $foodicsToken['access_token'];
            $brand_id = $foodicsToken['brand_id'];

            // Create the API URL with the branch IDs (start with the first page)
            $page = 1;
            $payload = []; // Initialize the payload to store all records

            #  do {
            // Call the retryable request function with the page parameter
            $apiUrl = $this->buildApiUrl($branchIds, $page);
            $response = $this->makeRetryableRequest($client, $apiUrl, $accessToken, $maxRetries, $initialDelay, $brand_id);

            // Process the response if needed
            if ($response) {
                $requestData = json_decode($response->getBody(), true);

                // Check if 'data' key exists and is an array
                if (isset($requestData['data']) && is_array($requestData['data'])) {
                    foreach ($requestData['data'] as $order) {
                        $order_id = $order['id'];
                        $reference_id = $order['reference'];
                        $branch_id = $order['branch']['id'];

                        // Create an order payload array and append it to the payload array
                        $payload[] = [
                            'order_id' => $order_id,
                            'brand_id' => $brand_id,
                            'reference_id' => $reference_id,
                            'branch_id' => $branch_id,
                            'list_order_payload' => json_encode($order, JSON_PRETTY_PRINT),
                            'created_at' => now(),
                            'updated_at' => now(),
                        ];
                    }

                    // Check pagination meta to see if there are more pages
                    $meta = $requestData['meta'];

                    // Ensure the meta field has the correct pagination values
                    if (!isset($meta['last_page'])) {
                        // \Log::channel('orders')->warning("Pagination meta missing from response on page $page");
                        $responseMessages[] = "Error: Pagination meta missing.";
                    }

                    # $last_page = $meta['last_page'];

                    // Move to the next page
                    $page++;
                } else {
                    // Handle case where 'data' is not available or not an array
                    // \Log::channel('orders')->warning("No orders found on page $page for brand ID " . $brand_id . ".\n");
                    $responseMessages[] = "No orders found on page $page for brand ID " . $brand_id . ".\n";
                }
            } else {
                // Handle case where API response fails
                $responseMessages[] = "Failed to fetch data from the API.";
                // \Log::channel('orders')->error("Failed to fetch data from the API.");
            }
            #  } while ($page <= $last_page); // Continue fetching until the last page

            // Use upsert to insert or update records in bulk once all pages are processed
            FoodicsListOrder::upsert($payload, ['branch_id', 'order_id'], ['reference_id', 'branch_id', 'list_order_payload', 'updated_at']);

            // Return success message
            if (count($payload) > 0) {
                // \Log::channel('orders')->info(count($payload) . " records processed for brand ID " . $brand_id);
                $responseMessages[] = count($payload) . " records processed for brand ID " . $brand_id;
            }
        }
        return json_encode($responseMessages, JSON_PRETTY_PRINT);
    }

    public function extractFoodicsOrderData()
    {
        $foodicsOrders = FoodicsListOrder::where('status', 1)->get();

        if ($foodicsOrders->isEmpty()) {
            #  echo "No orders found for extraction\n";
            #  // \Log::channel('orders')->info("No orders found for extraction\n");
        } else {

            foreach ($foodicsOrders as $foodicsOrder) {

                // Decode the order payload into an array
                $data = json_decode($foodicsOrder->list_order_payload, true);
                #dd($data);
                // Extract order details
                $brand_id = $foodicsOrder->brand_id;
                $order_id = $foodicsOrder->order_id;
                $branch_id = $foodicsOrder->branch_id;
                $reference = $foodicsOrder->reference_id;
                $branch_name = $data['branch']['name'];
                $order_total_price = $data['total_price'];
                $order_subtotal_price = $data['subtotal_price'];

                // Initialize an array to store sale items
                $sale_items = $this->extractSaleItems($data, $brand_id);

                // If there are sale items, format the extractData array, otherwise mark the order as invalid
                if (!empty($sale_items)) {
                    $extractData = [
                        'brand_id' => $brand_id,
                        'order_id' => $order_id,
                        'reference_order_id' => $reference,
                        'branch_reference_id' => $branch_id,
                        'branch_name' => $branch_name,
                        'total_price' => $order_total_price,
                        'subtotal_price' => $order_subtotal_price,
                        'sale_items' => $sale_items,
                    ];

                    // Output the extracted data as JSON for debugging
                    $updateData = json_encode($extractData, JSON_PRETTY_PRINT);
                    // update the extract_order_payload column with the extracted data
                    FoodicsListOrder::where('order_id', $order_id)->update(
                        [
                            'extract_order_payload' => $updateData,
                            'status' => 2
                        ]
                    );
                    echo "Data extracted for order ID: $order_id\n";
                    // \Log::channel('orders')->info("Data extracted for order ID: $order_id\n");
                } else {

                    FoodicsListOrder::where('order_id', $order_id)->update(
                        [
                            'extract_order_payload' => 'data not found in foodics mapping',
                            'status' => 99
                        ]
                    );
                    # // \Log::channel('orders')->info("No sale items found for order ID: $order_id\n");
                    # echo "No sale items found for order ID: $order_id\n";
                }
            }
        }
    }

    public function extractSaleItems(array $data, $brand_id)
    {
        $sale_items = [];
        #dd($data);
        // Handle products
        if (!empty($data['products'])) {
            foreach ($data['products'] as $product) {
                $item_id = $product['product']['id'];
                $item_name = $product['product']['name'];
                $cost = $product['product']['cost'];
                $price = $product['product']['price'];
                $quantity = $product['quantity'];
                $total_cost = $product['total_cost'];
                $total_price = $product['total_price'];
                $modifiers = $product['options'];

                $product_data = [
                    'item_id' => $item_id,
                    'item_name' => $item_name,
                    'cost' => $cost,
                    'price' => $price,
                    'quantity' => $quantity,
                    'total_cost' => $total_cost,
                    'total_price' => $total_price,
                    'modifiers' => $modifiers,
                    'brand_id' => $brand_id,
                ];

                $formattedItem = $this->formatOrderItems($product_data, 'recipe');
                if (!empty($formattedItem)) {
                    $sale_items[] = $formattedItem;
                }
            }
        }

        // Add handling for combos (if needed)
        if (!empty($data['combos'])) {
            foreach ($data['combos'] as $combo) {
            }
        }

        return $sale_items;
    }

    public function formatOrderItems($product_data, $mode)
    {

        $foodics_id = $product_data['item_id'];
        $item_name = $product_data['item_name'];
        $cost = $product_data['cost'];
        $price = $product_data['price'];
        $total_cost = $product_data['total_cost'];
        $total_price = $product_data['total_price'];
        $quantity = $product_data['quantity'];
        $modifiers = $product_data['modifiers'];
        $brand_id = $product_data['brand_id'];

        if ($mode == 'recipe') {
            $mapping = FoodicsMapping::where('foodics_id', $foodics_id)
                ->where('type_chefadmin', 'active_menu_item')
                ->where('brand_id', $brand_id)
                ->limit(1)
                ->select([
                    'chefadmin_id',
                    DB::raw('SUBSTRING_INDEX(chefadmin_id, "-", 1) AS prefix'),
                    DB::raw('SUBSTRING_INDEX(chefadmin_id, "-", -1) AS suffix'),
                ])
                ->first();

            if ($mapping === null) {
                return [];
            }

            $chefadmin_id = $mapping->suffix;
        } else if ($mode == 'combo') {
            $mapping = FoodicsMapping::where('foodics_id', $foodics_id)
                ->where('type_chefadmin', 'create_combo')
                ->where('brand_id', $brand_id)
                ->limit(1)
                ->select([
                    'chefadmin_id',
                    DB::raw('SUBSTRING_INDEX(chefadmin_id, "-", 1) AS prefix'),
                    DB::raw('SUBSTRING_INDEX(chefadmin_id, "-", -1) AS suffix'),
                ])
                ->first();

            if ($mapping === null) {
                return [];
            }

            $chefadmin_id = $mapping->suffix;
        }

        return [
            'product_id' => $chefadmin_id,
            'item_name' => $item_name,
            'quantity' => $quantity,
            'mode' => $mode,
            'cost' => $cost,
            'price' => $price,
            'total_cost' => $total_cost,
            'total_price' => $total_price,
            'sale_modifiers' => $this->extractModifiers($modifiers)
        ];
    }

    public function extractModifiers($modifiers)
    {
        if (empty($modifiers)) {
            return [];
        }

        $response = [];

        foreach ($modifiers as $modifier) {
            $modifierOption = $modifier['modifier_option'];
            $modifier_id = $modifierOption['id'];
            $modifier_name = $modifierOption['name'];
            $modifier_price = $modifierOption['price'];
            $modifier_cost = $modifierOption['cost'];
            $modifier_total_price = $modifier['total_price'];
            $modifier_total_cost = $modifier['total_cost'];
            $modifier_quantity = $modifier['quantity'];

            // Execute a query to find the relevant mapping for the modifier
            $query = DB::table('foodics_mappings')
                ->select([
                    'chefadmin_id',
                    DB::raw('SUBSTRING_INDEX(chefadmin_id, "-", 1) AS col1'),
                    DB::raw('SUBSTRING_INDEX(SUBSTRING_INDEX(chefadmin_id, "-", 2), "-", -1) AS modifier_id'),
                    DB::raw('SUBSTRING_INDEX(SUBSTRING_INDEX(chefadmin_id, "-", 3), "-", -1) AS col3'),
                    DB::raw('SUBSTRING_INDEX(SUBSTRING_INDEX(chefadmin_id, "-", -1), "-", -1) AS col4'),
                ])
                ->where('foodics_id', $modifier_id)
                ->where('chefadmin_id', 'LIKE', '%modg%')
                ->groupBy('chefadmin_id')
                ->first();

            // If query result is found, add the modifier data to the response
            if ($query) {
                $response[] = [
                    #'query_modifier' => $query,
                    'modifier_id' => $query->modifier_id,
                    'modifier_ingredient_id' => $query->col4,
                    'modifier_name' => $modifier_name,
                    'modifier_price' => $modifier_price,
                    'modifier_cost' => $modifier_cost,
                    'modifier_total_price' => $modifier_total_price,
                    'modifier_total_cost' => $modifier_total_cost,
                    'modifier_quantity' => $modifier_quantity,
                ];
            }
        }

        return $response;
    }

    public function postFoodicsOrderData()
    {
        $foodicsOrders = FoodicsListOrder::where('status', 2)->limit(500)->get();

        // Separate array for extract_order_payload values
        $extractOrderPayloadArray = $foodicsOrders->pluck('extract_order_payload')->toArray();

        // Convert each JSON string to JSON
        $body = array_map('json_decode', $extractOrderPayloadArray);

        $client = new Client();
        $headers = [
            'Content-Type' => 'application/json',
            # 'Authorization' => 'Bearer 107|oSj80RkLgkLp3RXgn0qVQf61MLmlNS1YAW00x5Nc'
        ];

        $request = new GuzzleRequest('POST', env('CHEFADMIN_URL') . '/api/orders', $headers, json_encode($body));
        $res = $client->sendAsync($request)->wait();


        // Update status to 0 for all records
        FoodicsListOrder::whereIn('id', $foodicsOrders->pluck('id')->toArray())
            ->update(['status' => 0]);

        echo $res->getBody();
    }

    public function getTokensFromAPI($column = null, $value = null)
    {
        $data = FoodicsOrderToken::getAccessToken(true, $column, $value)['data'];
        foreach ($data as &$item) {
            $branchIds = array_map(function ($shop) {
                return $shop['foodics_branch_id'];
            }, $item['shops']);
            $item['branch_ids'] = implode(',', $branchIds);
        }
        return $data;
    }
}
