<?php

namespace App\Services;

use App\Exceptions\InsuffisantFundsException;
use App\Exceptions\OrderSaved\ProductsNotAvailableWhenOrderSavedException;
use App\Exceptions\ServerStatus;
use App\Models\OrderManagement\Order;
use App\Models\OrderManagement\OrderTransaction;
use App\Models\PaymentManagement\Payment;
use App\Models\ProductManagement\Product;
use App\Models\SettingManagement\Status;
use App\Models\StockManagement\Warehouse;
use App\Models\UserManagement\User;
use App\Notifications\Backend\User\OrderCreatedNotification;
use App\Notifications\Frontend\User\OrderThanksNotification;
use Exception;
use Gloudemans\Shoppingcart\Facades\Cart;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Str;
use Mccarlosen\LaravelMpdf\Facades\LaravelMpdf as PDF;
use Throwable;

class OrderService{

    public function createOrder($request){
        DB::beginTransaction();

        try {
            $order = Order::create([
                'ref_id' => 'ORD-' . Str::random(15),
                "customer_id" => auth()->id(),
                'user_address_id' => $request['userAddressId'],
                'shipping_company_id' => $request['shippingCompanyId'],
                'payment_method_id' => $request['paymentMethodId'],
                'subtotal' => getNumbersOfCart()->get('subtotal'),
                'discount_code' => session()->has('coupon') ? session()->get('coupon')['code'] : null,
                'discount' => getNumbersOfCart()->get('discount'),
                'shipping' => getNumbersOfCart()->get('shipping'),
                'tax' => getNumbersOfCart()->get('productTaxes'),
                'total' => getNumbersOfCart()->get('total'),
                'currency' => 'XAF',
                'order_status' => 0,
            ]);

            $content = Cart::content();
            $cart = $this->formatCart($content);
            DB::rollBack();

            $warehouse = $order->userAddress->city->warehouse;

            throw_if(blank($warehouse) ,new Exception(__("Point de vente non configuré")));

            $productsIsAvailable = $this->productsIsAvailable($warehouse,$cart);
            throw_if(!empty($productsIsAvailable['errors']) ,new Exception(implode(" \n ",$productsIsAvailable['errors'])));

            foreach ($cart as $item) {
                $order->items()->create([
                    'product_id' => $item['product_id'],
                    'quantity' => $item['quantity']
                ]);
            }

            $order->transactions()->create([
                'transaction_status' => OrderTransaction::NEW_ORDER
            ]);

            DB::commit();
            return $order;
        }catch (Exception $exception){
            DB::rollBack();
            write_log("orders/checkout",$exception,"error");

            return $exception;
        }

    }

    public function formatCart($cart){
        $products = [];
        if ($cart){
            foreach ($cart as $item) {
              //  $product = $item->model;
                $products[$item->rowId] = [
                    'product_id' => $item->id,
                    'quantity' => $item->qty,
                    'price' => $item->price,
                ];
            }
        }
        return $products;
    }


    /**
     * @param array $datas
     * @param Model $payer
     * @param array $options
     * @return array|JsonResponse
     * @throws Throwable
     */
    public function saveOrder(array $datas,Model $payer,$options = []){

        $checkStock = $options['check-stock'] ?? true;
        $usePayment = $options['use-payment'] ?? true;
        $newPayment = $options['new-payment'] ?? null;
        $generateInvoice = $options['generate-invoice'] ?? false;
        $order = null;
        DB::beginTransaction();

        try {

            $orderDatas = $datas['order'];
            $orderItems = $datas['items'];
            $status_code = $orderDatas['status_code'] ?? null;
            $warehouse_id = $orderDatas['warehouse_id'] ?? null;
            $customer_id = $orderDatas['customer_id'] ?? null;

//            if (!isset($orderDatas['status_code'])){
//                $orderDatas['status_code'] = Status::UNPROCESSED;
//            }

            throw_if(blank($orderItems) ,
                new ProductsNotAvailableWhenOrderSavedException(__("You cannot order without products items."))
            );

            unset($orderDatas['status_code']);
            $order = Order::create($orderDatas); // [App\Models\Order]

            //dd($order,$orderDatas);
            $customer = $order->customer ;

            if ($newPayment and $customer){
                $balance_before = $customer->balance;

               $result = $customer->addPayment($newPayment);
              // DB::rollBack();
                $balance_after = $customer->balance;
//               dd($result,$orderDatas,"customer-balance-before = ".$balance_before," customer-balance-after = ".$balance_after);

                throw_if(!$result['success'],
                    new InsuffisantFundsException(implode("<br>",$result['errors']))
                );
            }

//            dd($checkStock,$usePayment);
            //si checkStock = true
            if ($checkStock){
                $warehouse = $order->userAddress?->city?->warehouse ?? null; // App\Models\StockManagement\Warehouse

                if (filled($warehouse_id)){
                    $warehouse = Warehouse::find($warehouse_id);
                }

                throw_if(blank($warehouse) , // warehouse/point of sale not setting up
                    new ProductsNotAvailableWhenOrderSavedException(__("Point of sales is required to check stock availability."))
                );
                $productsIsAvailable = $this->productsIsAvailable($warehouse,$orderItems);

                throw_if(filled($productsIsAvailable['errors']) ,
                    new ProductsNotAvailableWhenOrderSavedException(implode("<br>",$productsIsAvailable['errors']))
                );

                // dd($warehouse,$productsIsAvailable);
            }

            if ($usePayment){
                //en changeant le status de la commande à "PAID"; la commande consommera les paiements disponible du client
                $order->setStatusTo(Status::PAID);
            }

            $order->items()->createMany($orderItems);

//            DB::rollBack();
//            dd($status_code,$orderDatas);

            if ($status_code and $status_code != Status::UNPROCESSED){
                $order->setStatusTo($status_code);
            }

            DB::commit();
            $order = Order::find($order->id);

            if ($generateInvoice and filled($order)){
                try {
                    $order->generateInvoice();
                }catch (\Exception $exception){}
            }

            return [
                'message' => __("Order sent successfully with ID : :id",['id' => $order->ref_id]),
                'success' => true,
                'order' => $order->only(["id",'ref_id',"subtotal","total","status_code","note","created_at"]),
            ];

        }
        catch (Exception $exception){
            DB::rollBack();
          //  $class = get_class($exception);
            write_log("orders/save",$exception,"error");
            $attrs = [
                //'message' => $class->getMessage(),
                'message' => general_error($exception),
                "success" => false,
//                "exception" => $exception,
            ];
            if ($exception instanceof InsuffisantFundsException){
                $attrs['message'] = $exception->getMessage();
                $attrs['payment'] = [
                    "required" => true
                ];
            }
            if ($exception instanceof ProductsNotAvailableWhenOrderSavedException){
                $attrs['message'] = $exception->getMessage();
                $attrs['products'] = [
                    "available" => false
                ];
            }

            if (request()->ajax() or request()->wantsJson()){
              //  return response()->json($attrs,ServerStatus::APPLICATION_ERROR );
            }
            $attrs['exception'] = $exception->getTrace();
           // $attrs['trace'] = $exception->getTraceAsString();
            return $attrs ;
        }

    }

    /** Check si les produits commandés ont disponibles en stock
     * @param Warehouse $warehouse
     * @param $items
     * @return array
     */
    public function productsIsAvailable(Warehouse $warehouse , $items){
        $errors = [];
        $success = [];
        $warning = [];
        $cartInstance = Cart::instance("default");
        $cart = $cartInstance->content();
        $available = true;

        foreach ($items as $rowId => $item){
            $product_id = $item['product_id'];
            $product = Product::find($product_id);
            $productName = $product->fullName;
            //qte commandé
            $ordered_qty = $item['quantity'];
            $stock = $product->isAvailable($warehouse,$ordered_qty);
            $available_qty = $stock['available_qty'];

            $message = "";
            // si le produit est dispo dans l'entrepot
            if ($stock['available'] or $available_qty > 0){

                if ($ordered_qty  > $available_qty){  //qte commandé > qte en stock
                    $message =  __("- You cannot order :qty x :productName",
                        ['qty' => $ordered_qty , "productName" => $productName]);
                   // $resultats['errors'][$rowId] = ["color" => "warning", "message" => $message,];
                    $warning[$rowId] = $message;

                    if ($cart->isNotEmpty()){
                       $cartInstance->update($rowId,1) ; // modifie le panier
                    }
                    $available = false;
                }
                else{ /*sinon; le produit est disponible*/
                    $message =  __("- Your order of :qty x :productName is available.",
                        ['qty' => $ordered_qty , "productName" => $productName]);
                    $success[$rowId] = $message;
                   // $resultats['success'][$rowId]= ["color" => "success", "message" => $message,];
                }
            }
            else{
                $message = __("- Your order of :qty x :productName is not available.",
                    ["qty" =>$ordered_qty, "productName" => $productName]);
                $errors[$rowId] = $message;
              //  $resultats['errors'][$rowId]= ["color" => "danger", "message" => $message,];

                if ($cart->isNotEmpty() and $cart->has($rowId)){
                    $cartInstance->remove($rowId) ; // supprime le produit du panier
                }
                $available = false;
            }
            if (!empty($message))  {
               // $errors[] = "<small style='font-size: 12px; text-align: left !important;'>$message</small>";
            }
        }

        return [
            'errors' => $errors ,
            "success" => $success,
            "warning" => $warning,
            "available" => $available,
        ];
    }
}
