<?php

namespace App\Http\Controllers\Api\V1;

use App\Models\Item;
use App\Models\ItemCampaign;
use App\Models\Order;
use App\Models\OrderDetail;
use App\Models\Store;
use App\Models\Review;
use App\Models\Allergy;
use App\Models\Category;
use App\Models\Nutrition;
use App\Models\GenericName;
use App\Models\PriorityList;
use Illuminate\Http\Request;
use App\CentralLogics\Helpers;
use App\Models\BusinessSetting;
use App\CentralLogics\StoreLogic;
use Illuminate\Support\Facades\DB;
use App\CentralLogics\ProductLogic;
use App\CentralLogics\CategoryLogic;
use App\Http\Controllers\Controller;
use App\Services\ProductBM25Scorer;
use App\Services\Review\ReviewRewardService;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Validator;

class ItemController extends Controller
{

    public function get_latest_products(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'store_id' => 'required',
            'category_id' => 'required',
            'limit' => 'required',
            'offset' => 'required',
        ]);

        if ($validator->fails()) {
            return response()->json(['errors' => Helpers::error_processor($validator)], 403);
        }

        Helpers::setZoneIds($request);
        $zone_id = $request->header('zoneId');
        $type = $request->query('type', 'all');
        $product_id = $request->query('product_id')??null;
        $min = $request->query('min_price');
        $max = $request->query('max_price');
        $filter = $request['filter'] ? (is_array($request['filter']) ? $request['filter'] : str_getcsv(trim($request['filter'], "[]"), ',')) : '';

        $rating_count = $request->query('rating_count');

        $items = ProductLogic::get_latest_products($zone_id, $request['limit'], $request['offset'], $request['store_id'], $request['category_id'], $type,$min,$max,$product_id,$filter,$rating_count);
        $items['categories'] = $items['categories'];
        $items['products'] = Helpers::product_data_formatting($items['products'], true, false, app()->getLocale());
        return response()->json($items, 200);
    }

    public function get_new_products(Request $request)
    {
        Helpers::setZoneIds($request);
        $zone_id = $request->header('zoneId');
        $type = $request->query('type', 'all');
        $product_id = $request->query('product_id')??null;
        $min = $request->query('min_price');
        $max = $request->query('max_price');
        $limit = isset($request['limit'])?$request['limit']:50;
        $offset = isset($request['offset'])?$request['offset']:1;
        $category_id = $request->query('category_id');
        $category_ids = $category_id ? [$category_id] : null;

        $items = ProductLogic::get_new_products($zone_id, $type,$min,$max,$product_id,$limit,$offset, null, null, $category_ids);
        $items['categories'] = $items['categories'];
        $items['products'] = Helpers::product_data_formatting($items['products'], true, false, app()->getLocale());
        return response()->json($items, 200);
    }

    public function get_searched_products(Request $request)
    {
        Helpers::setZoneIds($request);
        $validator = Validator::make($request->all(), [
            'name' => 'required'
        ]);

        if ($validator->fails()) {
            return response()->json(['errors' => Helpers::error_processor($validator)], 403);
        }


        $product_search_default_status =BusinessSetting::where('key', 'product_search_default_status')->first()?->value ?? 1;
        $product_search_sort_by_general =PriorityList::where('name', 'product_search_sort_by_general')->where('type','general')->first()?->value ?? '';
        $product_search_sort_by_unavailable =PriorityList::where('name', 'product_search_sort_by_unavailable')->where('type','unavailable')->first()?->value ?? '';
        $product_search_sort_by_temp_closed =PriorityList::where('name', 'product_search_sort_by_temp_closed')->where('type','temp_closed')->first()?->value ?? '';


        $zone_id = $request->header('zoneId');

        $key = array_values(array_filter(explode(' ', mb_strtolower(trim($request['name'])))));
        $key = array_values(array_unique(array_merge($key, array_filter(array_map(
            static fn ($term) => mb_strlen($term) >= 4 ? mb_substr($term, 0, 3) : null,
            $key
        )))));

        $limit = $request['limit']??10;
        $offset = $request['offset']??1;
        $category_ids = $request['category_ids']?(is_array($request['category_ids'])?$request['category_ids']:json_decode($request['category_ids'])):'';
        $filter = $request['filter']?(is_array($request['filter'])?$request['filter']:str_getcsv(trim($request['filter'], "[]"), ',')):'';
        $type = $request->query('type', 'all');
        $min = $request->query('min_price');
        $max = $request->query('max_price');
        $rating_count = $request->query('rating_count');

        $query = Item::active()->type($type)
        ->with('store', function($query){
            $query->withCount(['campaigns'=> function($query){
                $query->Running();
            }]);
        })
        ->with('category.parent', 'ecommerce_item_details.brand', 'translations')
        ->select(['items.*'])
        ->selectSub(function ($subQuery) {
            $subQuery->selectRaw('active as temp_available')
                ->from('stores')
                ->whereColumn('stores.id', 'items.store_id');
        }, 'temp_available');


        if ($product_search_default_status != '1'){
            if(config('module.current_module_data')['module_type']  !== 'food'){
                if($product_search_sort_by_unavailable == 'remove'){
                    $query = $query->where('stock', '>', 0);
                }elseif($product_search_sort_by_unavailable == 'last'){
                    $query = $query->orderByRaw('CASE WHEN stock = 0 THEN 1 ELSE 0 END');
                }

            }

            if($product_search_sort_by_temp_closed == 'remove'){
                $query = $query->having('temp_available', '>', 0);
            }elseif($product_search_sort_by_temp_closed == 'last'){
                $query = $query->orderByDesc('temp_available');
            }
        }


        $query= $query->when($request->category_id, function($query)use($request){
            $query->whereHas('category',function($q)use($request){
                return $q->whereId($request->category_id)->orWhere('parent_id', $request->category_id);
            });
        })
        ->when($category_ids, function($query)use($category_ids){
            $query->whereHas('category',function($q)use($category_ids){
                return $q->whereIn('id',$category_ids)->orWhereIn('parent_id', $category_ids);
            });
        })
        ->when($request->store_id, function($query) use($request){
            return $query->where('store_id', $request->store_id);
        })
        ->whereHas('module.zones', function($query)use($zone_id){
            $query->whereIn('zones.id', json_decode($zone_id, true));
        })
        ->whereHas('store', function($query)use($zone_id){
            $query->when(config('module.current_module_data'), function($query){
                $query->where('module_id', config('module.current_module_data')['id'])->whereHas('zone.modules',function($query){
                    $query->where('modules.id', config('module.current_module_data')['id']);
                });
            })->whereIn('zone_id', json_decode($zone_id, true));
        })
        ->where(function ($q) use ($key) {
            foreach ($key as $value) {
                $q->orWhere('name', 'like', "%{$value}%");
            }

            $relationships = [
                'translations' => 'value',
                'tags' => 'tag',
                'nutritions' => 'nutrition',
                'allergies' => 'allergy',
                'category.parent' => 'name',
                'category' => 'name',
                'generic' => 'generic_name',
                'ecommerce_item_details.brand' => 'name',
                'pharmacy_item_details.common_condition' => 'name',
            ];
            $q->applyRelationShipSearch(relationships:$relationships ,searchParameter:$key);
        })
        ->when($rating_count, function($query) use ($rating_count){
            $query->where('avg_rating', '>=' , $rating_count);
        })
        ->when($min && $max, function($query)use($min,$max){
            $query->whereBetween('price',[$min,$max]);
        })
        ->when($filter&&in_array('top_rated',$filter),function ($qurey){
            $qurey->withCount('reviews')->orderBy('reviews_count','desc');
        })
        ->when($filter&&in_array('popular',$filter),function ($qurey){
            $qurey->popular();
        })
        ->when($filter&&in_array('discounted',$filter),function ($qurey){
            $qurey->Discounted()->orderBy('discount','desc');
        })
        ->when($filter&&in_array('high',$filter),function ($qurey){
            $qurey->orderBy('price', 'desc');
        })
        ->when($filter&&in_array('low',$filter),function ($qurey){
            $qurey->orderBy('price', 'asc');
        });


        // Fetch candidates (up to 500) then BM25-sort in PHP for accurate relevance.
        // Skip BM25 when caller requests an explicit ordering (price/rating).
        $hasExplicitOrder = $filter && array_intersect(
            is_array($filter) ? $filter : [],
            ['top_rated', 'popular', 'discounted', 'high', 'low']
        );
        $allItems = $query->limit(500)->get();
        $sorted   = $hasExplicitOrder
            ? $allItems->all()
            : ProductBM25Scorer::sort($allItems, $request['name']);

        $item_categories = array_unique(array_map(fn ($p) => $p->category_id, $sorted));
        $total           = count($sorted);
        $pageItems       = array_slice($sorted, ($offset - 1) * $limit, $limit);

        $categories = Category::withCount(['products','childes'])->with(['childes' => function($query)  {
            $query->withCount(['products','childes']);
        }])
        ->where(['position'=>0,'status'=>1])
        ->when(config('module.current_module_data'), function($query){
            $query->module(config('module.current_module_data')['id']);
        })
        ->whereIn('id',$item_categories)
        ->orderBy('priority','desc')->get();

        $data = [
            'total_size' => $total,
            'limit'      => $limit,
            'offset'     => $offset,
            'products'   => $pageItems,
            'categories' => $categories,
        ];

        $data['products'] = Helpers::product_data_formatting($data['products'], true, false, app()->getLocale());
        return response()->json($data, 200);
    }

    public function get_searched_products_suggestion(Request $request)
    {
        Helpers::setZoneIds($request);
        $validator = Validator::make($request->all(), [
            'name' => 'required'
        ]);

        if ($validator->fails()) {
            return response()->json(['errors' => Helpers::error_processor($validator)], 403);
        }
        $zone_id = $request->header('zoneId');

        $key = explode(' ', $request['name']);

        $limit = $request['limit']??10;
        $offset = $request['offset']??1;

        $type = $request->query('type', 'all');

        $items = Item::active()->type($type)

        ->when($request->category_id, function($query)use($request){
            $query->whereHas('category',function($q)use($request){
                return $q->whereId($request->category_id)->orWhere('parent_id', $request->category_id);
            });
        })
        ->when($request->store_id, function($query) use($request){
            return $query->where('store_id', $request->store_id);
        })
        ->whereHas('module.zones', function($query)use($zone_id){
            $query->whereIn('zones.id', json_decode($zone_id, true));
        })
        ->whereHas('store', function($query)use($zone_id){
            $query->when(config('module.current_module_data'), function($query){
                $query->where('module_id', config('module.current_module_data')['id'])->whereHas('zone.modules',function($query){
                    $query->where('modules.id', config('module.current_module_data')['id']);
                });
            })->whereIn('zone_id', json_decode($zone_id, true));
        })
        ->where(function ($q) use ($key) {
            foreach ($key as $value) {
                $q->orWhere('name', 'like', "%{$value}%");
            }
            $q->orWhereHas('translations',function($query)use($key){
                $query->where(function($q)use($key){
                    foreach ($key as $value) {
                        $q->where('value', 'like', "%{$value}%");
                    };
                });
            });
            $q->orWhereHas('tags',function($query)use($key){
                $query->where(function($q)use($key){
                    foreach ($key as $value) {
                        $q->where('tag', 'like', "%{$value}%");
                    };
                });
            });
            $q->orWhereHas('nutritions',function($query)use($key){
                $query->where(function($q)use($key){
                    foreach ($key as $value) {
                        $q->where('nutrition', 'like', "%{$value}%");
                    };
                });
            });
            $q->orWhereHas('allergies',function($query)use($key){
                $query->where(function($q)use($key){
                    foreach ($key as $value) {
                        $q->where('allergy', 'like', "%{$value}%");
                    };
                });
            });
            $q->orWhereHas('generic',function($query)use($key){
                $query->where(function($q)use($key){
                    foreach ($key as $value) {
                        $q->where('generic_name', 'like', "%{$value}%");
                    };
                });
            });
        })->select(['name','image'])

        ->paginate($limit, ['*'], 'page', $offset);

        $data =  [
            'total_size' => $items->total(),
            'limit' => $limit,
            'offset' => $offset,
            'products' => $items->items()
        ];

        return response()->json($data, 200);
    }

    public function get_popular_products(Request $request)
    {
        Helpers::setZoneIds($request);
        $min_price = $request->query('min_price');
        $max_price = $request->query('max_price');
        $rating_count = $request->query('rating_count');

        $filter = $request->query('filter', '');
        $filter = $filter?(is_array($filter)?$filter:str_getcsv(trim($filter, "[]"), ',')):'';
        $category_ids = $request->query('category_ids', '');

        $type = $request->query('type', 'all');

        $zone_id= $request->header('zoneId');

        $items = ProductLogic::popular_products($zone_id, $request['limit']??25, $request['offset']??1, $type,$category_ids, $filter, $min_price, $max_price, $rating_count,$request['search']);
        $items['products'] = Helpers::productListDataFormatting($items['products']);
        return response()->json($items, 200);
    }

    public function get_best_selling_products(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'limit' => 'nullable|integer|min:1|max:100',
            'offset' => 'nullable|integer|min:1',
        ]);

        if ($validator->fails()) {
            return response()->json(['errors' => Helpers::error_processor($validator)], 403);
        }

        Helpers::setZoneIds($request);

        $items = ProductLogic::best_selling_products(
            $request->header('zoneId'),
            $request->query('limit', 6),
            $request->query('offset', 1),
            $request->query('type', 'all')
        );

        $soldQuantities = collect($items['products'])->mapWithKeys(function ($item) {
            return [(int) $item->id => max(0, (int) $item->sold_quantity)];
        });

        $items['products'] = collect(
            Helpers::product_data_formatting($items['products'], true, false, app()->getLocale())
        )->map(function ($product) use ($soldQuantities) {
            $soldQuantity = (int) $soldQuantities->get((int) data_get($product, 'id'), 0);
            $product['order_count'] = $soldQuantity;
            $product['sold_count'] = $soldQuantity;
            $product['sold_quantity'] = $soldQuantity;

            return $product;
        })->values()->all();

        return response()->json($items, 200);
    }

    public function get_most_reviewed_products(Request $request)
    {
        Helpers::setZoneIds($request);

        $type = $request->query('type', 'all');
        $min_price = $request->query('min_price');
        $max_price = $request->query('max_price');
        $rating_count = $request->query('rating_count');

        $filter = $request->query('filter', '');
        $filter = $filter?(is_array($filter)?$filter:str_getcsv(trim($filter, "[]"), ',')):'';
        $category_ids = $request->query('category_ids', '');

        $zone_id= $request->header('zoneId');
        $items = ProductLogic::most_reviewed_products($zone_id, $request['limit']??25, $request['offset']??1, $type,$category_ids, $filter ,$min_price, $max_price, $rating_count);
        $items['categories'] = $items['categories'];

        $items['products'] = Helpers::productListDataFormatting($items['products']);
        return response()->json($items, 200);
    }

    public function get_discounted_products(Request $request)
    {
        Helpers::setZoneIds($request);

        $type = $request->query('type', 'all');
        $min_price = $request->query('min_price');
        $max_price = $request->query('max_price');
        $rating_count = $request->query('rating_count');

        $filter = $request->query('filter', '');
        $filter = $filter?(is_array($filter)?$filter:str_getcsv(trim($filter, "[]"), ',')):'';
        $category_ids = $request->query('category_ids', '');

        $zone_id= $request->header('zoneId');

        $items = ProductLogic::discounted_products(zone_id:$zone_id, limit: $request['limit']??25, offset: $request['offset']??1, type: $type, category_ids: $category_ids, filter:$filter,min: $min_price, max:$max_price, rating_count:$rating_count,search:$request['search']??null);
        $items['products'] = Helpers::productListDataFormatting($items['products']);
        return response()->json($items, 200);
    }

    public function get_cart_suggest_products(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'store_id' => 'required',
        ]);

        if ($validator->fails()) {
            return response()->json(['errors' => Helpers::error_processor($validator)], 403);
        }

        Helpers::setZoneIds($request);
        $zone_id = $request->header('zoneId');

        $type = $request->query('type', 'all');
        $recommended = $request->query('recommended');

        $items = ProductLogic::cart_suggest_products($zone_id, $request['store_id'], $request['limit'], $request['offset'], $type,$recommended);
        $items['items'] = Helpers::product_data_formatting($items['items'], true, false, app()->getLocale());
        return response()->json($items, 200);
    }

    public function get_product(Request $request, $id)
    {
        try {
            if ($request['campaign'] == 1) {
                $item = ItemCampaign::active()
                ->when(config('module.current_module_data'), function ($query) {
                        $query->module(config('module.current_module_data')['id']);
                    })
                    ->when(is_numeric($id), function ($qurey) use ($id) {
                        $qurey->where('id', $id);
                    })
                    ->when(!is_numeric($id), function ($qurey) use ($id) {
                        $qurey->where('slug', $id);
                    })
                    ->first();
            } else {
                $item = Item::withCount('whislists')->with([
                    'tags',
                    'nutritions',
                    'allergies',
                    'reviews' => function ($query) {
                        $query->active()->latest()->with(['customer', 'item', 'itemCampaign', 'order.details']);
                    },
                ])->active()
                    ->when(config('module.current_module_data'), function ($query) {
                        $query->module(config('module.current_module_data')['id']);
                    })
                    ->when(is_numeric($id), function ($qurey) use ($id) {
                        $qurey->where('id', $id);
                    })
                    ->when(!is_numeric($id), function ($qurey) use ($id) {
                        $qurey->where('slug', $id);
                    })
                    ->first();
            }
            
            if (!$item) {
                return response()->json([
                    'errors' => ['code' => 'product-001', 'message' => translate('messages.not_found')]
                ], 404);
            }
            $store = StoreLogic::get_store_details($item->store_id);
            if($store)
            {
                $category_ids = DB::table('items')
                ->join('categories', 'items.category_id', '=', 'categories.id')
                ->selectRaw('categories.position as positions, IF((categories.position = "0"), categories.id, categories.parent_id) as categories')
                ->where('items.store_id', $item->store_id)
                ->where('categories.status',1)
                ->groupBy('categories','positions')
                ->get();

                $store = Helpers::store_data_formatting($store);
                $store['category_ids'] = array_map('intval', $category_ids->pluck('categories')->toArray());
                $store['category_details'] = Category::whereIn('id',$store['category_ids'])->get();
                $store['price_range']  = Item::withoutGlobalScopes()->where('store_id', $item->store_id)
                ->select(DB::raw('MIN(price) AS min_price, MAX(price) AS max_price'))
                ->get(['min_price','max_price'])->toArray();
            }
            $item = Helpers::product_data_formatting($item, false, true, app()->getLocale());
            $itemReviews = $this->collectProductReviews((int) $item['id'], $request->boolean('campaign'));
            if ($itemReviews->count() > 0) {
                $averageRating = round((float) $itemReviews->avg('rating'), 2);
                $item['rating_count'] = max((int) ($item['rating_count'] ?? 0), $itemReviews->count());
                $item['review_count'] = max((int) ($item['review_count'] ?? 0), $itemReviews->count());
                $item['avg_rating'] = $averageRating;
                $item['average_rating'] = $averageRating;
            }
            $item['sold_quantity'] = (int) ($item['sold_quantity'] ?? $item['sold_count'] ?? $item['order_count'] ?? 0);
            $item['review_count'] = (int) ($item['review_count'] ?? $item['rating_count'] ?? 0);
            $item['average_rating'] = (float) ($item['average_rating'] ?? $item['avg_rating'] ?? 0);
            $item['reviews'] = $this->formatProductReviews($itemReviews->take(5));
            $item['store_details'] = $store;
            return response()->json($item, 200);
        } catch (\Exception $e) {
            \Log::error($e);
            return response()->json([
                'errors' => ['code' => 'product-001', 'message' => translate('messages.not_found')]
            ], 404);
        }
    }

    public function get_sitemap_products()
    {
        $items = Item::withoutGlobalScopes(['storage', 'translate'])
            ->active()
            ->when(config('module.current_module_data'), function ($query) {
                $query->module(config('module.current_module_data')['id']);
            })
            ->whereNotNull('slug')
            ->where('slug', '<>', '')
            ->orderBy('id')
            ->get(['slug', 'updated_at']);

        return response()->json([
            'total_size' => $items->count(),
            'products' => $items,
        ], 200);
    }

    public function get_related_products(Request $request,$id)
    {
        Helpers::setZoneIds($request);
        $zone_id= $request->header('zoneId');
        if (Item::find($id)) {
            $items = ProductLogic::get_related_products($zone_id,$id);
            $items = Helpers::product_data_formatting($items, true, false, app()->getLocale());
            return response()->json($items, 200);
        }
        return response()->json([
            'errors' => ['code' => 'product-001', 'message' => translate('messages.not_found')]
        ], 404);
    }
    public function get_related_store_products(Request $request,$id)
    {
        Helpers::setZoneIds($request);
        $zone_id= $request->header('zoneId');
        if (Item::find($id)) {
            $items = ProductLogic::get_related_store_products($zone_id,$id);
            $items = Helpers::product_data_formatting($items, true, false, app()->getLocale());
            return response()->json($items, 200);
        }
        return response()->json([
            'errors' => ['code' => 'product-001', 'message' => translate('messages.not_found')]
        ], 404);
    }

    public function get_recommended(Request $request)
    {
        Helpers::setZoneIds($request);

        $type = $request->query('type', 'all');
        $filter = $request->query('filter', 'all');

        $zone_id= $request->header('zoneId');

        $items = ProductLogic::recommended_items($zone_id, $request->store_id,$request['limit'], $request['offset'], $type, $filter);
        $items['items'] = Helpers::product_data_formatting($items['items'], true, false, app()->getLocale());
        return response()->json($items, 200);
    }

    public function get_set_menus()
    {
        try {
            $items = Helpers::product_data_formatting(Item::active()->with(['rating'])->where(['set_menu' => 1, 'status' => 1])->get(), true, false, app()->getLocale());
            return response()->json($items, 200);
        } catch (\Exception $e) {
            return response()->json([
                'errors' => ['code' => 'product-001', 'message' => 'Set menu not found!']
            ], 404);
        }
    }

    public function get_product_reviews(Request $request, $item_id)
    {
        $isCampaignRequest = $request->boolean('campaign');
        $reviewCollection = $this->collectProductReviews((int) $item_id, $isCampaignRequest);

        if(isset($request['limit']) && ($request['limit'] != null) && isset($request['offset']) && ($request['offset'] != null)){
            $limit = max((int) $request['limit'], 1);
            $page = max((int) $request['offset'], 1);
            $total = $reviewCollection->count();
            $reviews = new LengthAwarePaginator(
                $reviewCollection->forPage($page, $limit)->values(),
                $total,
                $limit,
                $page
            );
        } else {
            $reviews = $reviewCollection;
            $total = $reviewCollection->count();
        }

        $storage = $this->formatProductReviews($reviews);

        $data =  [
            'total_size' => $total,
            'limit' => $request['limit'],
            'offset' => $request['offset'],
            'reviews' => $storage
        ];

        return response()->json($data, 200);
    }

    public function get_product_rating($id)
    {
        try {
            $item = Item::find($id);
            if (! $item) {
                return response()->json(0, 200);
            }

            if (Review::hasColumn('item_id')) {
                $reviews = $this->collectProductReviews((int) $id);
            } else {
                $reviews = $this->collectProductReviews((int) $id);
            }

            $overallRating = ProductLogic::get_overall_rating($reviews);
            return response()->json(floatval($overallRating[0]), 200);
        } catch (\Exception $e) {
            return response()->json(['errors' => $e], 403);
        }
    }

    public function submit_product_review(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'item_id' => 'nullable',
            'order_id' => 'required',
            'rating' => 'required|numeric|max:5',
            'order_detail_id' => 'nullable|integer',
            'device_identifier' => 'nullable|string|max:191',
        ]);

        $order = Order::find($request->order_id);
        if (isset($order) == false) {
            $validator->errors()->add('order_id', translate('messages.order_data_not_found'));
        }

        $orderDetail = $this->resolveReviewOrderDetail($request, $order);
        $resolvedItemId = $this->resolveReviewItemId($request, $orderDetail);
        $resolvedCampaignId = $this->resolveReviewCampaignId($request, $orderDetail);
        $item = $resolvedItemId ? Item::withoutGlobalScopes()->find($resolvedItemId) : null;
        $campaign = $resolvedCampaignId ? ItemCampaign::withoutGlobalScopes()->find($resolvedCampaignId) : null;
        if (!$item && !$campaign && !$orderDetail) {
            $validator->errors()->add('item_id', translate('messages.item_not_found'));
        }

        $duplicateItemId = $resolvedItemId > 0 ? $resolvedItemId : (int) $request->input('item_id', 0);
        $multiReviewQuery = Review::where([
            'user_id' => $request->user()->id,
            'order_id' => $request->order_id,
        ]);
        $hasReviewItemColumn = Review::hasColumn('item_id');
        $hasReviewCampaignColumn = Review::hasColumn('item_campaign_id');

        if (($duplicateItemId > 0 && $hasReviewItemColumn) || ($resolvedCampaignId > 0 && $hasReviewCampaignColumn)) {
            $multiReviewQuery->where(function ($query) use ($duplicateItemId, $resolvedCampaignId) {
                if ($duplicateItemId > 0 && Review::hasColumn('item_id')) {
                    $query->orWhere('item_id', $duplicateItemId);
                }

                if ($resolvedCampaignId > 0 && Review::hasColumn('item_campaign_id')) {
                    $query->orWhere('item_campaign_id', $resolvedCampaignId);
                }
            });
        } elseif ($orderDetail && $hasReviewItemColumn && $hasReviewCampaignColumn) {
            $multiReviewQuery
                ->whereNull('item_id')
                ->whereNull('item_campaign_id');
        } else {
            $multiReviewQuery->where('user_id', $request->user()->id)
                ->where('order_id', $request->order_id);
        }

        $multi_review = $multiReviewQuery->first();
        if (isset($multi_review)) {
            return response()->json([
                'errors' => [
                    ['code'=>'review','message'=> translate('messages.already_submitted')]
                ]
            ], 403);
        } else {
            $review = new Review;
        }

        if ($validator->errors()->count() > 0) {
            return response()->json(['errors' => Helpers::error_processor($validator)], 403);
        }

        $image_array = [];
        if (!empty($request->file('attachment'))) {
            foreach ($request->file('attachment') as $image) {
                if ($image != null) {
                    if (!Storage::disk('public')->exists('review')) {
                        Storage::disk('public')->makeDirectory('review');
                    }
                    array_push($image_array, Storage::disk('public')->put('review', $image));
                }
            }
        }

        $order?->OrderReference?->update([
            'is_reviewed' => 1
        ]);

        $resolvedStoreId = $order?->store_id
            ?? $item?->store_id
            ?? $campaign?->store_id
            ?? $order?->store?->id;
        $resolvedModuleId = $order?->module_id
            ?? $item?->module_id
            ?? $campaign?->module_id
            ?? $order?->store?->module_id;

        $review->user_id = $request->user()->id;
        $review->order_id = $request->order_id;
        $review->comment = $request?->comment;
        $review->rating = $request->rating;
        $review->attachment = json_encode($image_array);
        if (Review::hasColumn('item_id')) {
            $review->item_id = $item?->id ?: ($resolvedItemId > 0 ? $resolvedItemId : null);
        }
        if (Review::hasColumn('item_campaign_id')) {
            $review->item_campaign_id = $campaign?->id ?: ($resolvedCampaignId > 0 ? $resolvedCampaignId : null);
        }
        if (Review::hasColumn('module_id')) {
            $review->module_id = $resolvedModuleId;
        }
        if (Review::hasColumn('store_id')) {
            $review->store_id = $resolvedStoreId;
        }
        if (Review::hasColumn('status')) {
            $review->status = 1;
        }
        $review->save();
        $reward = app(ReviewRewardService::class)->grantIfEligible($review, $request->input('device_identifier'));

        $reviewStore = $item?->store ?? $campaign?->store;
        if($reviewStore)
        {
            $store_rating = StoreLogic::update_store_rating($reviewStore->rating, (int)$request->rating);
            $reviewStore->rating = $store_rating;
            $reviewStore->save();
        }

        if ($item) {
            $item->rating = ProductLogic::update_rating($item->rating, (int)$request->rating);
            $item->avg_rating = ProductLogic::get_avg_rating(json_decode($item->rating, true));
            $item->save();
            $item->increment('rating_count');
        }

        $reviewReward = [
            'granted' => (bool) data_get($reward, 'granted', false),
            'points' => (int) data_get($reward, 'points', 0),
            'popup_message' => data_get($reward, 'popup_message'),
            'review_id' => (int) data_get($reward, 'review_id', $review->id),
            'block_reason' => data_get($reward, 'block_reason'),
        ];

        return response()->json([
            'message' => translate('messages.review_submited_successfully'),
            'review_reward_granted' => $reviewReward['granted'],
            'review_reward_points' => $reviewReward['points'],
            'review_reward_popup_message' => $reviewReward['popup_message'],
            'review_reward_block_reason' => $reviewReward['block_reason'],
            'review_reward' => $reviewReward,
            'review_id' => (int) $review->id,
        ], 200);
    }

    private function formatProductReviews($reviews): array
    {
        $storage = [];

        foreach ($reviews as $temp) {
            $temp['attachment'] = $temp->attachment_full_urls;
            $temp['item_name'] = $temp->display_item_name;
            $temp['item_image_full_url'] = $temp->display_item_image_full_url;
            $temp['customer_name'] = $temp->display_customer_name;
            $temp['customer_image_full_url'] = $temp->display_customer_image_full_url;

            unset($temp['item']);
            $storage[] = $temp;
        }

        return $storage;
    }

    private function collectProductReviews(int $itemId, bool $isCampaignRequest = false)
    {
        $targetColumn = $isCampaignRequest ? 'item_campaign_id' : 'item_id';
        $baseQuery = Review::with(['customer', 'item', 'itemCampaign', 'order.details'])->active();

        if (! Review::hasColumn($targetColumn)) {
            return $baseQuery->get()
                ->filter(fn (Review $review) => (int) ($review->display_item_id ?? 0) === $itemId)
                ->values();
        }

        $directReviews = (clone $baseQuery)
            ->where($targetColumn, $itemId)
            ->get();

        $fallbackReviews = (clone $baseQuery)
            ->where(function ($query) use ($targetColumn) {
                $query->whereNull($targetColumn)->orWhere($targetColumn, 0);
            })
            ->get()
            ->filter(fn (Review $review) => (int) ($review->display_item_id ?? 0) === $itemId);

        return $directReviews
            ->concat($fallbackReviews)
            ->unique('id')
            ->sortByDesc('created_at')
            ->values();
    }

    private function resolveReviewOrderDetail(Request $request, ?Order $order): ?OrderDetail
    {
        if (! $order) {
            return null;
        }

        $orderDetailId = (int) $request->input('order_detail_id', 0);
        if ($orderDetailId > 0) {
            $matchedDetail = OrderDetail::withoutGlobalScopes()
                ->where('id', $orderDetailId)
                ->where('order_id', $order->id)
                ->first();

            if ($matchedDetail) {
                return $matchedDetail;
            }
        }

        $requestItemId = (int) $request->input('item_id', 0);
        $requestCampaignId = (int) $request->input('item_campaign_id', 0);
        $orderDetails = OrderDetail::withoutGlobalScopes()
            ->where('order_id', $order->id)
            ->get();

        if ($requestItemId > 0) {
            $matchedByItemId = $orderDetails->first(function (OrderDetail $detail) use ($requestItemId) {
                if ((int) ($detail->item_id ?? 0) === $requestItemId
                    || (int) ($detail->product_id ?? 0) === $requestItemId
                    || (int) ($detail->display_item_reference_id ?? 0) === $requestItemId) {
                    return true;
                }

                $itemDetails = $detail->display_item_details;
                if (is_string($itemDetails) && trim($itemDetails) !== '') {
                    $decoded = json_decode($itemDetails, true);
                    $itemDetails = json_last_error() === JSON_ERROR_NONE ? $decoded : [];
                }

                if (! is_array($itemDetails)) {
                    return false;
                }

                foreach (['item_id', 'id', 'product_id'] as $candidateKey) {
                    if ((int) data_get($itemDetails, $candidateKey, 0) === $requestItemId) {
                        return true;
                    }
                }

                return false;
            });

            if ($matchedByItemId) {
                return $matchedByItemId;
            }
        }

        if ($requestCampaignId > 0) {
            $matchedByCampaignId = $orderDetails->first(function (OrderDetail $detail) use ($requestCampaignId) {
                if ((int) $detail->item_campaign_id === $requestCampaignId) {
                    return true;
                }

                $itemDetails = $detail->item_details;
                if (is_string($itemDetails) && trim($itemDetails) !== '') {
                    $decoded = json_decode($itemDetails, true);
                    $itemDetails = json_last_error() === JSON_ERROR_NONE ? $decoded : [];
                }

                if (! is_array($itemDetails)) {
                    return false;
                }

                foreach (['item_campaign_id', 'campaign_id', 'campaign_item_id'] as $candidateKey) {
                    if ((int) data_get($itemDetails, $candidateKey, 0) === $requestCampaignId) {
                        return true;
                    }
                }

                return false;
            });

            if ($matchedByCampaignId) {
                return $matchedByCampaignId;
            }
        }

        return $orderDetails->count() === 1 ? $orderDetails->first() : null;
    }

    private function resolveReviewItemId(Request $request, ?OrderDetail $orderDetail): int
    {
        $requestItemId = (int) $request->input('item_id', 0);
        if ($requestItemId > 0) {
            return $requestItemId;
        }

        if ($orderDetail && (int) $orderDetail->item_id > 0) {
            return (int) $orderDetail->item_id;
        }

        if ($orderDetail && (int) ($orderDetail->product_id ?? 0) > 0) {
            return (int) $orderDetail->product_id;
        }

        if ($orderDetail && (int) ($orderDetail->display_item_reference_id ?? 0) > 0) {
            return (int) $orderDetail->display_item_reference_id;
        }

        if (! $orderDetail) {
            return 0;
        }

        $itemDetails = $orderDetail->display_item_details;
        if (is_string($itemDetails) && trim($itemDetails) !== '') {
            $decoded = json_decode($itemDetails, true);
            if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                foreach (['item_id', 'id', 'product_id', 'food_id', 'temp_product_id'] as $candidateKey) {
                    $candidateId = (int) data_get($decoded, $candidateKey, 0);
                    if ($candidateId > 0) {
                        return $candidateId;
                    }
                }
            }
        } elseif (is_array($itemDetails)) {
            foreach (['item_id', 'id', 'product_id', 'food_id', 'temp_product_id'] as $candidateKey) {
                $candidateId = (int) data_get($itemDetails, $candidateKey, 0);
                if ($candidateId > 0) {
                    return $candidateId;
                }
            }
        }

        return 0;
    }

    private function resolveReviewCampaignId(Request $request, ?OrderDetail $orderDetail): int
    {
        $requestCampaignId = (int) $request->input('item_campaign_id', 0);
        if ($requestCampaignId > 0) {
            return $requestCampaignId;
        }

        if ($orderDetail && (int) $orderDetail->item_campaign_id > 0) {
            return (int) $orderDetail->item_campaign_id;
        }

        if (! $orderDetail) {
            return 0;
        }

        $itemDetails = $orderDetail->item_details;
        if (is_string($itemDetails) && trim($itemDetails) !== '') {
            $decoded = json_decode($itemDetails, true);
            if (json_last_error() === JSON_ERROR_NONE && is_array($decoded)) {
                foreach (['item_campaign_id', 'campaign_id', 'campaign_item_id'] as $candidateKey) {
                    $candidateId = (int) data_get($decoded, $candidateKey, 0);
                    if ($candidateId > 0) {
                        return $candidateId;
                    }
                }
            }
        } elseif (is_array($itemDetails)) {
            foreach (['item_campaign_id', 'campaign_id', 'campaign_item_id'] as $candidateKey) {
                $candidateId = (int) data_get($itemDetails, $candidateKey, 0);
                if ($candidateId > 0) {
                    return $candidateId;
                }
            }
        }

        return 0;
    }

    public function item_or_store_search(Request $request){

        Helpers::setZoneIds($request);
        if (!$request->hasHeader('longitude') || !$request->hasHeader('latitude')) {
            $errors = [];
            array_push($errors, ['code' => 'longitude-latitude', 'message' => translate('messages.longitude-latitude_required')]);
            return response()->json([
                'errors' => $errors
            ], 403);
        }
        $validator = Validator::make($request->all(), [
            'name' => 'required',
        ]);
        $zone_id= $request->header('zoneId');
        $longitude= $request->header('longitude');
        $latitude= $request->header('latitude');

        if ($validator->fails()) {
            return response()->json(['errors' => Helpers::error_processor($validator)], 403);
        }
        $key = explode(' ', $request->name);

        $rawItems = Item::active()->whereHas('store', function($query)use($zone_id){
            $query->when(config('module.current_module_data'), function($query){
                $query->where('module_id', config('module.current_module_data')['id'])->whereHas('zone.modules',function($query){
                    $query->where('modules.id', config('module.current_module_data')['id']);
                });
            })->whereIn('zone_id', json_decode($zone_id, true));
        })
        ->with('category.parent')
        ->where(function ($q) use ($key) {
            foreach ($key as $value) {
                $q->orwhere('name', 'like', "%{$value}%")->orWhere('description', 'like', "%{$value}%");
            }

            $relationships = [
                'translations' => 'value',
                'tags' => 'tag',
                'nutritions' => 'nutrition',
                'allergies' => 'allergy',
                'category.parent' => 'name',
                'category' => 'name',
                'generic' => 'generic_name',
                'ecommerce_item_details.brand' => 'name',
                'pharmacy_item_details.common_condition' => 'name',
            ];
            $q->applyRelationShipSearch(relationships:$relationships ,searchParameter:$key);
        })
        ->limit(200)
        ->get(['items.id','items.name','items.image','items.category_id']);

        $items = collect(ProductBM25Scorer::sort($rawItems, $request->name))->take(50);

        $stores = Store::
        whereHas('zone.modules', function($query){
            $query->where('modules.id', config('module.current_module_data')['id']);
        })
        ->withOpen($longitude??0,$latitude??0)
        ->with(['discount'=>function($q){
            return $q->validate();
        }])->weekday()

        ->where(function ($q) use ($key) {
            foreach ($key as $value) {
                $q->orWhere('name', 'like', "%{$value}%");
            }

            $relationships = [
                'translations' => 'value',
                'items.nutritions' => 'nutrition',
                'items.allergies' => 'allergy',
                'items.generic' => 'generic_name',
                'items.ecommerce_item_details.brand' => 'name',
                'items.pharmacy_item_details.common_condition' => 'name'
            ];
            $q->applyRelationShipSearch(relationships:$relationships ,searchParameter:$key);
        })
        ->when(config('module.current_module_data'), function($query)use($zone_id){
            $query->module(config('module.current_module_data')['id']);
            if(!config('module.current_module_data')['all_zone_service']) {
                $query->whereIn('zone_id', json_decode($zone_id, true));
            }
        })
        ->active()
                ->orderByRaw("CASE
                        WHEN LOWER(REPLACE(name, ' ', '')) = LOWER(REPLACE(?, ' ', '')) THEN 1
                        WHEN LOWER(REPLACE(name, ' ', '')) LIKE LOWER(REPLACE(?, ' ', '')) THEN 2
                        WHEN LOWER(REPLACE(name, ' ', '')) LIKE LOWER(REPLACE(?, ' ', '')) THEN 3
                        ELSE 4
                    END,  LENGTH(name) ASC, name ASC ", [
                    $request['name'],            // exact match (normalized)
                    "{$request['name']}%",       // starts with (normalized)
                    "%{$request['name']}%",      // contains (normalized)
                ])
        ->limit(50)
        ->select(['id','name','logo'])
        ->get();

        return [
            'items' => $items,
            'stores' => $stores
        ];

    }

    public function get_store_condition_products(Request $request)
    {
        Helpers::setZoneIds($request);
        $validator = Validator::make($request->all(), [
            'store_id' => 'required',
            'limit' => 'required',
            'offset' => 'required',
        ]);

        if ($validator->fails()) {
            return response()->json(['errors' => Helpers::error_processor($validator)], 403);
        }

        $zone_id= $request->header('zoneId');

        $type = $request->query('type', 'all');
        $limit = $request['limit'];
        $offset = $request['offset'];
        $zones = !empty($zone_id) ? json_decode($zone_id, true) : null;

        $paginator = Item::query()

        ->when(empty($request->store_id), function ($query) use ($zones) {

            $query->when(!empty($zones), function ($query) use ($zones) {
                $query->whereHas('module.zones', function ($q) use ($zones) {
                    $q->whereIn('zones.id', $zones);
                });
            });

            $query->whereHas('store', function ($q) use ($zones) {

                $q->when(!empty($zones), function ($q) use ($zones) {
                    $q->whereIn('zone_id', $zones);
                });

                $q->whereHas('zone.modules', function ($q) {
                    $q->when(config('module.current_module_data'), function ($q) {
                        $q->where('modules.id', config('module.current_module_data')['id']);
                    });
                });
            });
        })

        ->when(is_numeric($request->store_id), function ($query) use ($request) {
            $query->where('store_id', $request->store_id);
        })

        ->when(!is_numeric($request->store_id), function ($query) use ($request) {
            $query->whereHas('store', function ($q) use ($request) {
                $q->where('slug', $request->store_id);
            });
        })

        ->whereHas('pharmacy_item_details', function ($q) {
            $q->whereNotNull('common_condition_id');
        })

        ->whereHas('ecommerce_item_details', function ($q) {
            $q->whereNotNull('brand_id');
        })

        ->active()
        ->type($type)
        ->latest()
        ->paginate($limit, ['*'], 'page', $offset);
        $data=[
            'total_size' => $paginator->total(),
            'limit' => $limit,
            'offset' => $offset,
            'products' => $paginator->items()
        ];
        $data['products'] = Helpers::product_data_formatting($data['products'] , true, false, app()->getLocale());
        return response()->json($data, 200);
    }

    public function get_popular_basic_products(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'limit' => 'required',
            'offset' => 'required',
        ]);

        if ($validator->fails()) {
            return response()->json(['errors' => Helpers::error_processor($validator)], 403);
        }

        Helpers::setZoneIds($request);
        $zone_id = $request->header('zoneId');
        $type = $request->query('type', 'all');
        $product_id = $request->query('product_id')??null;
        $min = $request->query('min_price');
        $max = $request->query('max_price');
        $limit = $request['limit']??25;
        $offset = $request['offset']??1;

        $items = ProductLogic::get_popular_basic_products($zone_id, $limit, $offset, $type, $request['store_id'], $request['category_id'], $min,$max,$product_id);
        $items['categories'] = $items['categories'];
        $items['products'] = Helpers::product_data_formatting($items['products'], true, false, app()->getLocale());
        return response()->json($items, 200);
    }

    public function get_products(Request $request)
    {
        Helpers::setZoneIds($request);
        $data_type = $request->query('data_type', 'all');

        $zone_id = $request->header('zoneId');
        $type = $request->query('type', 'all');
        $filter = $request->query('filter', '');
        $filter = $filter?(is_array($filter)?$filter:str_getcsv(trim($filter, "[]"), ',')):'';
        $category_ids = $request->query('category_ids', '');

        // Common parameters for all product types
        $limit = $request->query('limit', 10);
        $offset = $request->query('offset', 1);
        $min_price = $request->query('min_price');
        $max_price = $request->query('max_price');
        $rating_count = $request->query('rating_count');
        $product_id = $request->query('product_id');

        switch ($data_type) {
            case 'searched':
                return $this->get_searched_products($request);
                break;
            case 'discounted':
                $items = ProductLogic::discounted_products(zone_id:$zone_id,limit: $limit, offset: $offset, type: $type,  category_ids:$category_ids, filter: $filter, min: $min_price,max: $max_price,rating_count: $rating_count, search:$request['search']??null);
                break;
            case 'new':
                $items = ProductLogic::get_new_products($zone_id, $type, $min_price, $max_price, $product_id, $limit, $offset, $filter, $rating_count);
                break;
            case 'category':
                $validator = Validator::make($request->all(), [
                    'category_ids' => 'required',
                ]);

                if ($validator->fails()) {
                    return response()->json(['errors' => Helpers::error_processor($validator)], 403);
                }

                $items = CategoryLogic::category_products($category_ids, $zone_id, $limit, $offset, $type, $filter, $min_price, $max_price, $rating_count);
                break;
            default:
            $items =  [
                'total_size' => 0,
                'limit' => $limit,
                'offset' => $offset,
                'products' => [],
                'categories' => [],
            ];
        }

        $items['products'] = Helpers::product_data_formatting($items['products'], true, false, app()->getLocale());
        return response()->json($items, 200);
    }



    public function getGenericNameList(){
        $names= GenericName::select(['generic_name'])->pluck('generic_name');
        return response()->json($names, 200);
    }
    public function getAllergyNameList(){
        $names= Allergy::select(['allergy'])->pluck('allergy');
        return response()->json($names, 200);
    }
    public function getNutritionNameList(){
        $names= Nutrition::select(['nutrition'])->pluck('nutrition');
        return response()->json($names, 200);
    }


}
