<?php

namespace Modules\Order\Http\Controllers;

use App\Http\Controllers\BasicController;
use Illuminate\Http\Request;
use Modules\Notification\Entities\Model as Notification;
use Modules\Category\Entities\Model as Category;
use Modules\Client\Entities\Model as Client;
use Modules\Coupon\Entities\Model as Coupon;
use Modules\Delivery\Entities\Model as Delivery;
use Modules\Address\Entities\Model as Address;
use Modules\Product\Entities\Model as Product;
use Modules\DeliveryTime\Entities\Model as DeliveryTime;
use Modules\DeliveryDate\Entities\Model as DeliveryDate;
use Modules\Branch\Entities\Model as Branch;
use Modules\Country\Entities\Region as Region;
use Modules\Payment\Entities\Model as Payment;
use Modules\Order\Entities\Model;
use Illuminate\Support\Facades\Hash;
use PDF;
use App\Functions\PushNotification;
use Yajra\DataTables\DataTables;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;


class Controller extends BasicController
{
    public function index(Request $request)
    {
        return view('order::index');
    }

    public function datatable(Request $request)
    {
        $query = Model::query()
            ->with(['Client', 'Address', 'Delivery', 'Branch', 'Payment', 'Products.Items'])
            ->when($request->from, function ($query) use ($request) {
                return $query->whereDate('created_at', '>=', $request->from);
            })
            ->when($request->to, function ($query) use ($request) {
                return $query->whereDate('created_at', '<=', $request->to);
            })
            ->when($request->status == null, function ($query) use ($request) {
                return $query->whereNot('status', 5);
            })
            ->when($request->status != null, function ($query) use ($request) {
                return $query->where('status', $request->status);
            })
            ->when($request->follow != null, function ($query) use ($request) {
                return $query->where('follow', $request->follow);
            })
            ->when($request->id, function ($query) use ($request) {
                return $query->where('id', $request->id);
            })
            ->when($request->name, function ($query) use ($request) {
                return $query->WhereHas('Client', function ($q) use ($request) {
                    $q->where('name', 'LIKE', "%{$request->name}%");
                });
            })
            ->when($request->phone, function ($query) use ($request) {
                return $query->WhereHas('Client', function ($q) use ($request) {
                    $phone = zapPhoneCode($request->phone);
                    $q->Where('phone', 'LIKE', "%{$phone}%");
                });
            })
            ->when(request()->sort_column && request()->sort_direction, function ($query) {
                return $query->orderBy(request()->sort_column, request()->sort_direction);
            });

        $partsSearch = request()->get('search')['value'];
        $phone = zapPhoneCode($partsSearch);

        return DataTables::of($query)
            ->addColumn('actions', function ($Model) {
                return view('order::layouts.actions', ['Model' => $Model]);
            })
            ->addColumn('status-icon', function ($Model) {
                return view('order::layouts.status', ['Model' => $Model]);
            })
            ->addColumn('payment', function ($Model) {
                return $Model->Payment->title();
            })
            ->addColumn('device_type', function ($Model) {
                if($Model->mobile_type == 'web'){
                    return __('trans.website');
                }elseif($Model->mobile_type == 'pos'){
                    return 'POS';
                }else{
                    return __('trans.app');
                }
            })
            ->addColumn('delivery', function ($Model) {
                return $Model->Delivery->title();
            })
            ->addColumn('date', function ($Model) {
                return $Model->created_at->format('M j, Y');
            })
            ->addColumn('time', function ($Model) {
                return $Model->created_at->format('g:i a');
            })
            ->editColumn('sub_total', function ($Model) {
                return $Model->sub_total.' '.Country()->currancy_code;
            })
            ->editColumn('charge_cost', function ($Model) {
                return $Model->charge_cost.' '.Country()->currancy_code;
            })
            ->editColumn('discount', function ($Model) {
                return $Model->discount.' '.Country()->currancy_code;
            })
            ->editColumn('vat', function ($Model) {
                return $Model->vat.' '.Country()->currancy_code;
            })
            ->editColumn('coupon', function ($Model) {
                return $Model->coupon.' '.Country()->currancy_code;
            })
            ->editColumn('net_total', function ($Model) {
                return $Model->net_total.' '.Country()->currancy_code;
            })
            ->addColumn('name', function ($Model) {
                return $Model->Client->name;
            })
            ->addColumn('phone', function ($Model) {
                return $Model->Client->phone_code.$Model->Client->phone;
            })

            ->filter(function ($instance) use ($partsSearch, $phone) {
                if (! empty($partsSearch)) {
                    $instance->where(function ($query) use ($partsSearch, $phone) {
                        $query->Where('net_total', 'LIKE', "%{$partsSearch}%")
                            ->orWhereHas('Client', function ($q) use ($partsSearch, $phone) {
                                $q->where('name', 'LIKE', "%{$partsSearch}%")->orWhere('phone', 'LIKE', "%{$phone}%");
                            })
                            ->orWhereHas('Branch', function ($q) use ($partsSearch) {
                                $q->where('title_ar', 'LIKE', "%{$partsSearch}%")->orWhere('title_en', 'LIKE', "%{$partsSearch}%");
                            })
                            ->orWhereHas('Payment', function ($q) use ($partsSearch) {
                                $q->where('title_ar', 'LIKE', "%{$partsSearch}%")->orWhere('title_en', 'LIKE', "%{$partsSearch}%");
                            })
                            ->orWhereHas('Delivery', function ($q) use ($partsSearch) {
                                $q->where('title_ar', 'LIKE', "%{$partsSearch}%")->orWhere('title_en', 'LIKE', "%{$partsSearch}%");
                            });
                    });
                }
            })
            ->addIndexColumn()
            ->rawColumns(['status', 'actions'])
            ->make(true);
    }

    public function salesReport(Request $request)
    {
        $from =  $request->from ?? now()->format('Y-m-d');
        $to =  $request->to ?? now()->format('Y-m-d');
        
        $Orders = Model::query()
            ->with(['Products.Product'])
            ->whereDate('created_at', '>=', $from)
            ->whereDate('created_at', '<=', $to)
            ->whereNot('status', 2)
            ->whereNot('status', 0)
            ->whereNot('follow', 0)
            ->get();
    
        $DisplayProducts = collect();
    
        foreach ($Orders as $Order) {
            foreach ($Order->Products as $OrderProduct) {
                $DisplayProducts->push([
                    'product_title' => $OrderProduct->Product->title(),
                    'product_id'    => $OrderProduct->product_id,
                    'quantity'      => $OrderProduct->quantity,
                    'total'         => $OrderProduct->total,
                ]);
            }
        }
        
        $Products = Product::whereIn('id',$DisplayProducts->pluck('product_id'))->with('Sections.Items')->get();
    
        $GroupedProducts = $DisplayProducts->groupBy('product_id')->map(function ($items) use($Products) {
            $product_id = $items->first()['product_id'];
            $Product = $Products->where('id',$product_id)->first();
            $quantity = 0;
            foreach($Product->Sections as $Section){
                foreach($Section->Items as $Item){
                    $quantity += $Item->quantity;
                }
            }
            return [
                'product_id'    => $Product->id,
                'product_title' => $Product->title(),
                'quantity'      => $quantity,
                'sold'          => $items->sum('quantity'),
                'total'         => $items->sum('total'),
            ];
        })->values();

        return view('order::sales-report', compact('GroupedProducts','from','to'));
    }

    public function destroy($id)
    {
        $Model = Model::where('id', $id)->delete();
    }

    public function changestatus(Request $request)
    {
        $Order = Model::with(['Client.country'])->where('id', $request->id)->first();

        if ($request->status == 2) {
            $msg = 'order_rejected';
        } elseif ($request->status == 1 && $request->follow == 1) {
            $msg = 'order_preparing';
        } elseif ($request->status == 1 && $request->follow == 2) {
            $msg = 'order_onway';
        } elseif ($request->status == 1 && $request->follow == 3) {
            $msg = 'order_delivered';
        } else {
            $msg = 'updatedSuccessfully';
        }

        $message = '%0a *('.env('APP_NAME').')* %0a';
        $message .= '%0a *Order Number :* '.$Order->id;
        $message .= '%0a '.__('trans.'.$msg).' %0a';
        $message .= '%0a *Powered By Emcan Solutions* %0a';

        $Order->status = $request->status;
        $Order->follow = $request->follow;
        $Order->save();


        $Notification = Notification::create([
            'title_ar' => __('trans.'.$msg, [], 'ar'),
            'title_en' => __('trans.'.$msg, [], 'en'),
            'type' => 'order',
            'order_id' => $Order->id,
            'client_id' => $Order->client_id,
            'done' => 1,
        ]);

        PushNotification::send([
            'message' => $Notification->{'title_'.lang()},
            'client_id' => $Order->client_id,
            'data' => [
                'type' => 'order'
            ],
        ]);
        
        return response()->json([
            'msg' => $msg,
        ]);
    }

        
    public function create()
    {
        $Branches = Branch::get();
        $Payments = Payment::get();
        return view('order::create', compact('Branches', 'Payments'));
    }

    /**
     * Fetch products with variant combinations for POS
     */
    public function fetchProducts(Request $request)
    {
        $query = Product::query()
            ->with(['Image', 'Sections.Items'])
            ->where('status', 1);

        // Search functionality
        if ($search = $request->input('search')) {
            $query->where(function ($q) use ($search) {
                $q->where('title_en', 'like', "%{$search}%")
                    ->orWhere('title_ar', 'like', "%{$search}%")
                    ->orWhereHas('Sections.Items', function ($q2) use ($search) {
                        $q2->where('barcode', 'like', "%{$search}%");
                    });
            });
        }

        $products = $query->paginate(20);

        // Transform products to include variant combinations
        $products->getCollection()->transform(function ($product) {
            return $this->expandProductVariants($product);
        });

        // Flatten the collection
        $flatProducts = $products->getCollection()->flatten(1);
        
        return response()->json([
            'data' => $flatProducts,
            'current_page' => $products->currentPage(),
            'last_page' => $products->lastPage(),
            'total' => $products->total()
        ]);
    }

    /**
     * Expand product into variant combinations
     */
    private function expandProductVariants($product)
    {
        $sections = $product->Sections;

        if ($sections->isEmpty()) {
            return [$this->formatProductData($product)];
        }
        
        return $this->generateVariantCombinations($product, $sections);
    }

    /**
     * Generate all variant combinations from sections
     */
    private function generateVariantCombinations($product, $sections)
    {
        $variants = [];
        $sectionArrays = [];

        foreach ($sections as $section) {
            if ($section->Items->isEmpty()) continue;
            
            $sectionArrays[] = [
                'section' => $section,
                'items' => $section->Items->toArray()
            ];
        }

        if (empty($sectionArrays)) {
             return [$this->formatProductData($product)];
        }

        $combinations = $this->cartesianProduct($sectionArrays);

        foreach ($combinations as $combination) {
            $variantPrice = floatval($product->price);
            $variantLabels = [];
            $modifierIds = [];

            $variantTitles = [$product->{'title_' . lang()}]; 

            foreach ($combination as $item) {
                $itemPrice = floatval($item['item']['price']);
                $variantPrice += $itemPrice;
                $variantTitles[] = $item['item']['title_' . lang()]; 
                $variantLabels[] = $item['item']['title_' . lang()]; 
                $modifierIds[] = $item['item']['id'];
            }
            
            $variants[] = [
                'id' => $product->id,
                'title_ar' => $product->title_ar,
                'title_en' => $product->title_en,
                'price' => number_format($variantPrice, 3, '.', ''), 
                'quantity' => $this->calculateVariantStock($combination),
                'image' => $product->Image ? ['image' => asset($product->Image->image)] : null,
                'variant_title' => implode(' ', $variantTitles), 
                'variant_label' => implode(' | ', $variantLabels), 
                'variant_key' => 'variant_' . implode('_', $modifierIds),
                'modifier_ids' => $modifierIds,
                'is_variant' => true
            ];
        }

        return $variants;
    }
    
    /**
     * Calculate stock for variant combination (minimum stock among items)
     */
    private function calculateVariantStock($combination)
    {
        $minStock = PHP_INT_MAX;

        foreach ($combination as $item) {
            $stock = $item['item']['quantity'] ?? 0;
            $minStock = min($minStock, $stock);
        }

        return $minStock === PHP_INT_MAX ? 0 : $minStock;
    }

    /**
     * Generate Cartesian product
     */
    private function cartesianProduct($arrays)
    {
        if (empty($arrays)) return [[]];

        $result = [[]];

        foreach ($arrays as $sectionData) {
            $append = [];

            foreach ($result as $product) {
                foreach ($sectionData['items'] as $item) {
                    $append[] = array_merge($product, [[
                        'section' => $sectionData['section'],
                        'item' => $item
                    ]]);
                }
            }

            $result = $append;
        }

        return $result;
    }

    /**
     * Format basic product data
     */
    private function formatProductData($product)
    {
        return [
            'id' => $product->id,
            'title_ar' => $product->title_ar,
            'title_en' => $product->title_en,
            'price' => $product->price,
            'quantity' => $product->quantity ?? 0,
            'image' => $product->Image ? ['image' => asset($product->Image->image)] : null,
            'sections' => $product->Sections,
            'is_variant' => false
        ];
    }
    
    /**
     * Get order history (last 10 POS orders)
     */
    public function orderHistory(Request $request)
    {
        $orders = Model::with(['Products.Product', 'Client', 'Products.Items'])
            ->where('mobile_type', 'pos')
            ->orderBy('created_at', 'desc')
            ->limit(10)
            ->get()
            ->map(function ($order) {
                return [
                    'id' => $order->id,
                    'created_at' => $order->created_at->format('M j, g:i a'),
                    'net_total' => $order->net_total,
                    'discount' => $order->discount,
                    'charge_cost' => $order->charge_cost,
                    'items_count' => $order->Products->sum('quantity'),
                    'products' => $order->Products->map(function ($product) {
                        $modifierTitles = $product->Items->map(function($item) {
                            return $item->{'title_'.lang()}; 
                        })->implode(' | ');
                        
                        return [
                            'product_id' => $product->product_id,
                            'title' => $product->Product->title(),
                            'price' => $product->price,
                            'quantity' => $product->quantity,
                            'modifier_ids' => $product->Items->pluck('id')->toArray(),
                            'variant_label_from_history' => $modifierTitles
                        ];
                    })
                ];
            });

        return response()->json(['orders' => $orders]);
    }


    /**
     * Store new order or update existing order (AJAX)
     */
    public function store(Request $request)
    {
        $cartData = json_decode($request->cart_data, true);

        if (empty($cartData) || !is_array($cartData)) {
            return response()->json([
                'success' => false,
                'message' => __('trans.Invalid cart data')
            ], 400);
        }

        DB::beginTransaction();

        try {
            $client = Client::findOrFail(1);

            $paymentDetails = null;
            if ($request->payment_details) {
                $paymentDetails = json_decode($request->payment_details, true);
            }

            $payment_id = $request->payment_id;
            if ($request->payment_id == 'Multiple') {
                $payment_id = 10;
            }

            // Check if editing existing order
            $orderId = $request->order_id;
            $isEdit = !empty($orderId);

            if ($isEdit) {
                // EDITING EXISTING ORDER
                $order = Model::with(['Products'])->findOrFail($orderId);
                
                // Store old order products for inventory restoration
                $oldOrderProducts = $order->Products->keyBy(function($item) {
                    $modifierKey = implode('_', json_decode($item->item_ids ?: '[]', true));
                    return $item->product_id . '_' . $modifierKey;
                });

                // Update order details
                $order->update([
                    'delivery_id' => $request->delivery_id ?? 2,
                    'branch_id' => $request->branch_id,
                    'payment_id' => $payment_id,
                    'sub_total' => $request->sub_total,
                    'discount' => $request->discount ?? 0,
                    'vat' => $request->vat,
                    'charge_cost' => $request->charge_cost ?? 0,
                    'net_total' => $request->net_total,
                    'payment_details' => $paymentDetails ? json_encode($paymentDetails) : null,
                    
                    'delivery_time_from' => $request->delivery_time_from,
                    'delivery_time_to' => $request->delivery_time_to,
                    'delivery_date' => $request->delivery_date,
                ]);

                // Process new cart items
                $newCartKeys = [];
                foreach ($cartData as $item) {
                    $productId = (int) $item['product_id'];
                    $quantity = (int) $item['quantity'];
                    $price = (float) $item['price'];
                    $modifierIds = $item['modifier_ids'] ?? [];
                    
                    $cartKey = $productId . '_' . implode('_', $modifierIds);
                    $newCartKeys[] = $cartKey;

                    if (isset($oldOrderProducts[$cartKey])) {
                        // EXISTING PRODUCT - Calculate quantity difference
                        $oldProduct = $oldOrderProducts[$cartKey];
                        $oldQuantity = $oldProduct->quantity;
                        $quantityDiff = $quantity - $oldQuantity;

                        // Update order product
                        $oldProduct->update([
                            'price' => $price,
                            'quantity' => $quantity,
                            'total' => $price * $quantity,
                        ]);

                        // Adjust inventory based on quantity change
                        if ($quantityDiff != 0) {
                            if (!empty($modifierIds)) {
                                // Adjust modifier stock
                                foreach ($modifierIds as $modifierId) {
                                    DB::table('product_section_item')
                                        ->where('id', $modifierId)
                                        ->decrement('quantity', $quantityDiff);
                                }
                            } else {
                                // Adjust product stock
                                Product::where('id', $productId)->decrement('quantity', $quantityDiff);
                            }
                        }
                    } else {
                        // NEW PRODUCT ADDED TO ORDER
                        $orderProduct = $order->Products()->create([
                            'order_id' => $order->id,
                            'product_id' => $productId,
                            'price' => $price,
                            'quantity' => $quantity,
                            'total' => $price * $quantity,
                        ]);

                        // Create order product items
                        if (!empty($modifierIds)) {
                            $this->createOrderProductItems($orderProduct, $modifierIds);
                            $this->decrementModifierStock($modifierIds, $quantity);
                        } else {
                            Product::where('id', $productId)->decrement('quantity', $quantity);
                        }
                    }
                }

                // REMOVE PRODUCTS THAT ARE NO LONGER IN CART
                foreach ($oldOrderProducts as $cartKey => $oldProduct) {
                    if (!in_array($cartKey, $newCartKeys)) {
                        // Restore inventory for removed product
                        $modifierIds = json_decode($oldProduct->item_ids ?: '[]', true);
                        
                        if (!empty($modifierIds)) {
                            foreach ($modifierIds as $modifierId) {
                                DB::table('product_section_item')
                                    ->where('id', $modifierId)
                                    ->increment('quantity', $oldProduct->quantity);
                            }
                        } else {
                            Product::where('id', $oldProduct->product_id)
                                ->increment('quantity', $oldProduct->quantity);
                        }

                        // Delete the order product and its items
                        $oldProduct->Items()->delete();
                        $oldProduct->delete();
                    }
                }

            } else {
                // CREATING NEW ORDER
                $order = Model::create([
                    'client_id' => $client->id,
                    'delivery_id' => $request->delivery_id ?? 2,
                    'address_id' => null,
                    'branch_id' => $request->branch_id,
                    'payment_id' => $payment_id,
                    'sub_total' => $request->sub_total,
                    'discount' => $request->discount ?? 0,
                    'vat' => $request->vat,
                    'coupon' => 0,
                    'charge_cost' => $request->charge_cost ?? 0,
                    'net_total' => $request->net_total,
                    'mobile_type' => 'pos',
                    'payment_details' => $paymentDetails ? json_encode($paymentDetails) : null,
                    
                    'status' => 1,
                    'follow' => 1,
                    
                    'delivery_time_from' => $request->delivery_time_from,
                    'delivery_time_to' => $request->delivery_time_to,
                    'delivery_date' => $request->delivery_date,
                ]);

                // Process order products
                $this->processOrderProducts($cartData, $order);
            }

            DB::commit();

            return response()->json([
                'success' => true,
                'message' => $isEdit ? __('trans.Order updated successfully') : __('trans.Order created successfully'),
                'order_id' => $order->id
            ]);

        } catch (\Exception $e) {
            DB::rollback();
            Log::error('Order operation failed: ' . $e->getMessage() . ' on line ' . $e->getLine());

            return response()->json([
                'success' => false,
                'message' => __('trans.Failed to process order')
            ], 500);
        }
    }

    /**
     * Process order products and update inventory (for new orders)
     */
    private function processOrderProducts(array $cartData, $order)
    {
        foreach ($cartData as $item) {
            $productId = (int) $item['product_id'];
            $quantity = (int) $item['quantity'];
            $price = (float) $item['price'];
            $modifierIds = $item['modifier_ids'] ?? [];

            $orderProduct = $order->Products()->create([
                'order_id' => $order->id,
                'product_id' => $productId,
                'price' => $price,
                'quantity' => $quantity,
                'total' => $price * $quantity,
                'item_ids' => implode(',', $modifierIds),
            ]);

            if (!empty($modifierIds)) {
                $this->createOrderProductItems($orderProduct, $modifierIds);
                $this->decrementModifierStock($modifierIds, $quantity);
            } else {
                Product::where('id', $productId)->decrement('quantity', $quantity);
            }
        }
    }

    /**
     * Create order product items (modifiers description)
     */
    private function createOrderProductItems($orderProduct, $modifierIds)
    {
        $sectionItems = \Modules\Product\Entities\SectionItem::whereIn('id', $modifierIds)
            ->with('Section')
            ->get()
            ->groupBy('section_id');

        foreach ($sectionItems as $items) {
            $section = $items->first()->Section;
            
            $itemTitlesAr = $items->pluck('title_ar')->implode(', ');
            $itemTitlesEn = $items->pluck('title_en')->implode(', ');

            $orderProduct->Items()->create([
                'order_product_id' => $orderProduct->id,
                'title_ar' => '-*' . $section->title_ar . '*: ' . $itemTitlesAr,
                'title_en' => '-*' . $section->title_en . '*: ' . $itemTitlesEn,
                'item_ids' => implode(',', $modifierIds),
            ]);
        }
    }

    /**
     * Decrement modifier/section item stock
     */
    private function decrementModifierStock($modifierIds, $quantity)
    {
        foreach ($modifierIds as $modifierId) {
            DB::table('product_section_item')
                ->where('id', $modifierId)
                ->decrement('quantity', $quantity);
        }
    }
    
    
    
    
    public function download_pdf()
    {
        $Order = Model::where('id', request()->id)
            ->with(['Client', 'Address.Region', 'Payment', 'Products.Items'])
            ->firstOrFail();
    
        $data = [
            'Order' => $Order,
        ];
    
        $filename = md5($Order->id) . '.pdf';
        $relativePath = '/uploads/Orders/' . $filename;
        $absolutePath = public_path($relativePath);
    
        $pdf = PDF::loadView('order::print', $data, [], [
            'mode' => 'utf-8',
            'default_font_size' => 8,
            'format' => [80, 175],
        ]);
    
        $pdf->getMpdf()->SetTitle('Order ' . $Order->id . ' Details');
    
        $pdf->save($absolutePath);
    
        return response()->file($absolutePath);
    }
}