<?php

namespace App\Http\Controllers\Backend;

use App\DataTables\Orders\OrderDataTable;
use App\Exceptions\ServerStatus;
use App\Http\Controllers\Controller;
use App\Http\Resources\OrderManagement\OrderResource;
use App\Models\OrderManagement\Order;
use App\Models\OrderManagement\OrderTransaction;
use App\Models\PaymentManagement\Payment;
use App\Models\PaymentManagement\PaymentMethod;
use App\Models\ProductManagement\Category;
use App\Models\ProductManagement\Product;
use App\Models\SettingManagement\Status;
use App\Models\StockManagement\Stock;
use App\Models\UserManagement\Admin;
use App\Models\UserManagement\Customer;
use App\Notifications\Frontend\User\OrderStatusNotification;
use App\Services\InteractWithCart;
use App\Services\OmnipayService;
use App\Services\OrderService;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\HtmlString;
use Yajra\DataTables\Facades\DataTables;
use Yajra\DataTables\Html\Button;
use Yajra\DataTables\Html\Column;

class OrderController extends Controller{

    use InteractWithCart;


    public function __construct(){

        $this->middleware(['permission:order-list'])
            ->only(["index","dtIndex"]);

        $this->middleware(['permission:order-show'])
            ->only(["show"]);

        $this->middleware('permission:order-create')
            ->only(['create',"dtEdit"]);

        $this->middleware('permission:order-set-status')
            ->only(['setStatus']);

        $this->middleware('permission:order-delete')
            ->only(['delete',"deleteMany","dtEdit"]);

    }


    public function setStatus(Request $request,Order $order){
        return DB::transaction(function () use ($order,$request){
            try {
                $inputs = $request->all();
                $next_status = $inputs['status_code'];
                $status = Status::findOrFail($request->status_code);
                $rules = [
                    "status_code" => "required"
                ];
                $messages = [];

                if (in_array($next_status, Order::getStatusesRequiredRaesons())){
                    $rules["reason"] = ['required' , "min:4"];
                }

                $validator = validator($inputs,$rules,$messages);

                if($validator->fails()){
                    $result = [ "success" => false, "message" => __("Validation Error")];
                    $errors = get_errors_for_editor($validator);
                    return response()->json( array_merge($result,$errors), 500);
                }

                if ($order->status_code !== $next_status){
                    $order->setStatusTo($next_status);

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

                    if (filled($inputs['reason'])){
                        $status_change =    $order->statusChanges()
                                                    ->where([
                                                        'status_code'=>$next_status,
                                                        'created_by'=> auth()->id(),
                                                    ])
                                                    ->latest()
                                                    ->first();

                        if ($status_change){
                            $status_change->update(['message' => $inputs['reason']]);
                        }
                    }
                }

                DB::commit();
                return response()->json([
                    "message" => action_message(),
                    "success" => true,
                ],ServerStatus::SUCCESS);

            }catch (\Exception $exception){
                DB::rollBack();
                write_log("orders/set-status",$exception);

                return response()->json([
                    "message" => $exception->getMessage(),
                    "success" => false,
                ],ServerStatus::APPLICATION_ERROR);
            }
        });
    }

    public function indexV1(OrderDataTable $dataTable){
        $statuses = Order::getStatuses();
        $title = __("Manage Orders");
        $billingDesks = auth()->user()->billingDesks;
        $users = Admin::active()->get();
        $admin = auth()->user();
        return $dataTable->render('backend.orders.index',compact("title","statuses","billingDesks","users","admin"));
    }

    public function index(){
        $statuses = Order::getStatuses();
        $title = __("Manage Orders");
        $billing_desks = auth()->user()->billingDesks;
        $users = Admin::active()->get();
        $admin = auth()->user();
        return view('backend.orders.index',compact("title","statuses","billing_desks","users","admin"));
    }


    public function showOrder(Request $request){

        $ref_id = $request->ref_id;

        $order = Order::with(["status","customer","pointOfSale","warehouse","paymentUsages"])
                      ->firstWhere('ref_id',$ref_id);



        return response()->json($order ? OrderResource::make($order) : null);
    }

    public function listOrders(){

        if (request()->ajax()) {
            $search = request()->search['value'] ?? null;
//            $orders = Order::with(["status","customer","pointOfSale","warehouse","paymentUsages"])
//                          ->latest()
//                        //  ->take(10)
//                          ->get() ;

           // $orders = collect(null);

            $customer_table = (new Customer)->getTable();
            $customer_search_fields = collect(["first_name","last_name","email"]);

            $customer_search_fields = $customer_search_fields->map(fn($item) => $customer_table.".".$item)->toArray();

           // return datatables($orders)
            return DataTables::eloquent(
                                    Order::with(["status","customer","billingDesk","warehouse","paymentUsages"])

                                        ->when(filled($search),
                                                fn ($q) => $q->whereLike("ref_id",$search)
                                                              ->orWhere(
                                                                  fn ($q) => $q->whereHas("customer",fn ($q) => $q->whereLike($customer_search_fields,$search))
                                                               )
                                        )
                                        ->latest()

                                    )
                                    ->addIndexColumn()
                                    ->addColumn("order_formated" , function ($row) {
                                       return view("backend.orders.traitment.item-order",['order' => $row]);
                                     })

                                    ->only(["id","ref_id","order_formated","created_at"])
                                     ->make(true);
        }


    }

    public function show(Order $order): View{

        $statusId = $order->status_code;
//        $status = $order->status;
        $nextStatuses = Status::where("code","<>" ,$statusId)->get();

        $paidStatus = Status::find(Status::PAID);

        $rejectedStatuses = Order::getStatusesNotModifiables();
        //si "rejeté" n'est pas parmis les status
        if(!$nextStatuses->contains("code",Status::CANCELED) and $statusId != Status::CANCELED){
            $canceledStatus = Status::find(Status::REJECTED);
            $nextStatuses->add($canceledStatus);
        }

        return view('backend.orders.show', compact('order', 'nextStatuses',"rejectedStatuses","paidStatus"));
    }

    public function update(Request $request, Order $order): RedirectResponse{
        $this->authorize('edit_order');

        $user = Customer::find($order->customer_id);

        if ($request->order_status == Status::REFUNDED_ORDER){
            $omniPay = new OmnipayService('PayPal_Express');
            $response = $omniPay->refund([
                            'amount' => $order->total,
                            'transactionReference' => $order->transactions()->where('transaction_status', OrderTransaction::PAID)
                                ->first()->transaction_number,
                            'cancelUrl' => $omniPay->getCancelUrl($order->id),
                            'returnUrl' => $omniPay->getReturnUrl($order->id),
                            'notifyUrl' => $omniPay->getNotifyUrl($order->id),
                        ]);

            if ($response->isSuccessful()) {
                $order->update(['status_code' => Status::REFUNDED_ORDER]);
                $order->transactions()->create([
                    'transaction_status' => OrderTransaction::REFUNDED,
                    'transaction_number' => $response->getTransactionReference(), // coming from PayPal
                    'payment_result' => 'success'
                ]);

                $user->notify(new OrderStatusNotification($order));

                return back()->with([
                    'message' => __('Refunded successfully'),
                    'alert-type' => 'success',
                ]);
            }

        }
        else {

            $order->update(['status_code'=> $request->order_status]);

            $user->notify(new OrderStatusNotification($order));

            return back()->with([
                            'message' => action_message("update"),
                            'alert-type' => 'success',
                        ]);

        }
    }

    public function destroy(Order $order): RedirectResponse{
        $this->authorize('delete_order');
        $order->delete();

        return redirect()->route('admin.orders.index')->with([
            'message' => action_message("delete"),
            'alert-type' => 'success'
        ]);
    }

    public function getSales(){

      return  DataTables::eloquent(
          auth(activeGuard())->user()->orders()
              ->with([
                  "paymentUsages" => fn ($query) => $query->whereHas("payment",
                                                            fn ($q) => $q->whereHas("method",
                                                                            fn ($q) => $q->select("id","name","code")
                                                                        )
                                                                       ->select("id","provider_reference","internal_reference","paid_amount")
                                                          ) ,
                                                          //->select("id","name","code"),
                  "customer",
//                "items" => fn ($query) => $query->select(DB::raw("product_id, quantity as qty,price,amount")),
                  "pointOfSale" => fn ($query) => $query->select("id","name"),
                  "billingDesk" => fn ($query) => $query->select("id","name"),
                  "warehouse" => fn ($query) => $query->select("id","name"),
                  "items" ,
                //  "products" ,
                  "status" ,
              ])
            //  ->whereDateBetween("created_at",first_date_of_period(),date("Y-m-d"))
              ->whereDate("created_at",date("Y-m-d"))
              ->latest()
            //  ->take(5)
          //    ->get()
              )
              ->addIndexColumn()
              ->editColumn('created_at', fn ($row) => format_date($row->created_at,__("Y-m-d H:i:s")))
              ->editColumn('updated_at', fn ($row) => format_date($row->updated_at,__("Y-m-d H:i:s")))
              ->editColumn('customer', fn ($row) => $row->customer?->only("id","full_name","balance","email","phone") ?? [])
              ->editColumn('point_of_sale', fn ($row) => $row->pointOfSale?->only("id","name","code") ?? [])
              ->editColumn('billing_desk', fn ($row) => $row->billingDesk?->only("id","name","code") ?? [])
              ->editColumn('warehouse', fn ($row) => $row->warehouse?->only("id","name","code") ?? [])
              ->editColumn('status', fn ($row) => $row->status?->only("code","name","badge_label","badge_dot") ?? [])
              ->addColumn('status_displayed', function ($row){
                  $status = $row->status;

                  return (filled($status))
                          ? new HtmlString($status->badge_label)
                          : "---";
                })
              ->editColumn('created_at', fn ($row) => format_date($row->created_at,__("Y-m-d H:i:s")))
              ->editColumn('updated_at', fn ($row) => format_date($row->updated_at,__("Y-m-d H:i:s")))
              //->editColumn('items', fn ($row) => format_date($row->updated_at,__("Y-m-d H:i:s")))
              ->editColumn('items',
                  fn ($row) =>
                      $row->items
                          ->map(fn ($item)=>
                                array_merge(
                                   $item->product->only("name","full_name",'ref',"category_id") ,
                                   [
                                       "product_id" => $item->product_id,
                                       "qty" => $item->quantity,
                                       "price" => $item->price,
                                       "amount" => $item->amount,
                                   ]
                                )
                           )
                           ->toArray()

              )
             // ->except(["id","ref_id","product","qty","product_id","expired_at",'user',"action","warehouse_id","info"])
              ->make(true)
          ;
    }

    public function getDataPage(){
        $paymentMethods = PaymentMethod::active()->get();
        $admin =  auth(activeGuard())->user();
        $customers =   Customer::with([
                            "addresses" => fn ($query) => $query->select("title")
                        ])
                        ->active()
                        ->get()
                        ->map
                        ->only([
                            'id',"first_name","last_name","full_name",
                            "avatar","email","phone","status","username",
                            "full_default_address","balance","addresses","has_files"
                        ]) ;



        $billingDesks = $admin->billingDesks()
                              ->active()
                              ->with([])
                              ->get()
                                //->take(2)
                              ->map
                              ->only(['id',"name","code"]);

        $categories = Category::active()
                                ->get()
                                ->map
                                ->only("id","name");

        $products = Product::with([
                                "tags" => fn ($query) => $query->select("name"),
                                "category" => fn ($query) => $query->select("id","name"),
                            ])
                            ->active()
                          //  ->whereHas('media')
                            ->get()
                            //->take(10)
                            ->map
                          //  ->where("image",'!=',url('default.png'))
                            ->only([
                                'id',"name","price","full_name","category_id",
                                "image","files","is_available",
                                "tags"
                            ]);

        $stocks = [];
        $warehouses = [];

        if (getSettingsOf("check_stock_when_ordering_backend")){
            $warehouses = $admin->pointOfSales()
                               // ->active()
                                ->get()
                                ->map
                                ->only(['id',"name","code"]);

            if ($warehouses){
                $stocks = Stock::with("product")
                                ->whereIn('warehouse_id',$warehouses->pluck("id"))
                                ->get()
//                                ->groupBy("product_id")
//                                ->map(function ($group){
//                                    $first = $group->first();
//                                    $product = $first->product;
//                                    return [
//                                        "product_id" => $first->product_id,
//                                        "name" => $product->name,
//                                        "qty" => $group->sum('qty'),
//                                        "category" => $product->category->name,
//                                    ];
//                                })
                                ->map
                                ->only(['id',"lot_id","product_id",'expired_at',"qty","warehouse_id","is_expired"]);
            }
        }

        return [
            "warehouses" => $warehouses,
            "products" => $products,
            "billing_desks" => $billingDesks,
            "customers" => $customers,
            "admin" => $admin,
            "payment_methods" => $paymentMethods,
            "stocks" => $stocks,
            "categories" => $categories,
        ];
    }

    public function create(): View{
        $title = __("Create Order");
        $admin =  auth(activeGuard())->user();
        $statuses = Order::getStatuses();
//        $data = $this->getDataPage();
//
//        $warehouses = $data['warehouses'] ?? null;
//        $stocks = $data['stocks'] ?? null;
//        $products = $data['products'] ?? null;
//        $points_of_sales = $data['points_of_sales'] ?? null;
//        $customers = $data['customers'] ?? null;
//        $admin = $data['admin'] ?? null;
//        $paymentMethods = $data['payment_methods'] ?? null;
//        $categories = $data['categories'] ?? null;

//        return view('backend.orders.create.index',
//            compact("title","paymentMethods",
//                "customers","products","points_of_sales",
//                "admin","warehouses","stocks","categories")
//        );
        return view('backend.orders.create.index',
            compact("title","admin","statuses")
        );

    }

    public function store(Request $request , OrderService $service){
        $response = [
            'success' => false,
        ];
        try {
            $customer = $request->customer ?? null;
            $order = $request->order;

//        return [
//            "succcess" => false,
//            "message" => "Un messafge",
//        ];

            if ($customer){
                $is_new = $customer->is_new ?? false;

                // Cree le client
                if ($is_new or !isset($customer['id'])){
                    $phone = $customer['phone'];

                    $customer = Customer::firstOrCreate(
                                    [
                                        "phone" => $phone,
                                        "first_name" => $customer['first_name'],
                                        "last_name" => $customer['last_name'] ?? null,
                                        //"email" => $customer['email'] ?? null,
                                    ],
                                    [
                                        "first_name" => $customer['first_name'],
                                        "last_name" => $customer['last_name'] ?? null,
                                        "email" => $customer['email'] ?? null,
                                        "password" => filled($phone) ? bcrypt($phone) : null,
                                        //"email" => $customer['email'],
                                    ]
                                );
                }else{
                    $customer = Customer::firstWhere('id',$customer['id']);
                }
            }
//            dd($customer->toArray());
            $order['customer'] = $customer->toArray();
            $items = $this->items($order['items']);
            $total = $this->total($order);



            $total['billing_desk_id'] = $request->billing_desk_id ?? null;
            $total['warehouse_id'] = $request->warehouse_id ?? null;
            $total['created_by'] = auth(activeGuard())->id();
            $total['status_code'] = $order['config']["status_code"] ?? null;
            $total['note'] = $order['config']["note"] ?? null;

            $data = [  "order" => $total,   "items" => $items, ];

            $options = [
                "check-stock" => getSettingsOf("check_stock_when_ordering_backend"),
                "new-payment" => $order['config']["new_payment"] ?? null,
                "use-payment" => $order['config']['payment_required'] ?? true,
            ];


//        dd($data,$customer,$options);
            $response = $service->saveOrder($data , $customer , $options);

           // $success = $response['success'];

        }catch (\Exception $exception){
            $response['success'] = false;
            $response['message'] = $exception->getMessage();
            $response['exception'] = $exception->getTrace();
        }
        return response()->json($response, $response['success'] ? 200 : 500);
    }
}
