<?php

namespace App\Http\Middleware;

use App\Helpers\UUIDHelper;
use App\Models\Consignment;
use App\Models\ConsignmentItem;
use App\Models\Order;
use App\Models\OrderGroup;
use App\Models\PaymentMeta;
use App\Models\Setting;
use App\Models\Transaction;
use App\Notifications\abandonedCart;
use App\Notifications\AffiliateRevenue;
use App\Notifications\SellerRevenue;
use Carbon\Carbon;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Symfony\Component\HttpFoundation\Response;

class HandleCartMiddleware
{
    use UUIDHelper;

    /**
     * Handle an incoming request.
     *
     * @param  \Closure(\Illuminate\Http\Request): (\Symfony\Component\HttpFoundation\Response)  $next
     */
    public function handle(Request $request, Closure $next): Response
    {
        if (!Cache::has('handle_cart_last_run') || Cache::get('handle_cart_last_run') < now()->subHour()) {
            // delete expired consignment items
            $this->deleteExpiredConsignmentItems();

            // delete empty consignments
            $this->deleteEmptyConsignments();

            // delete empty orders
            $this->deleteEmptyOrders();

            // delete empty order groups
            $this->deleteEmptyOrderGroups();

            // delete expired waiting_payment transaction
            $this->deleteExpiredTransactions();

            // change status for delivered orders
            $this->changeStatusForDeliveredOrders();

            // abandoned cart notification
            $this->abandonedCartNotification();

            Cache::put('handle_cart_last_run', now(), 3600); // 1 hour cache
        }

        return $next($request);
    }

    /**
     * delete expired consignment items
     */
    public function deleteExpiredConsignmentItems()
    {
        $consignmentItems = ConsignmentItem::whereHas('consignment', function ($query) {
            $query->whereHas('order', function ($query) {
                $query->where('status', 'awaiting');
            });
        })->where(function ($query) {
            $query->where('status', 'ready_to_send')->OrWhere('status', 'awaiting_request_from_seller');
        })->where('updated_at', '<=', Carbon::now()->subDays(15)->toDateTimeString())->get();

        foreach ($consignmentItems as $consignmentItem) {
            $consignmentItem->delete();
        }
    }

    /**
     * delete empty consignments
     */
    public function deleteEmptyConsignments()
    {
        $consignments = Consignment::doesntHave('consignmentItems')->delete();
    }

    /**
     * delete empty orders
     */
    public function deleteEmptyOrders()
    {
        $orders = Order::where('status', 'awaiting')->doesntHave('consignments')->delete();
    }

    /**
     * delete empty orders
     */
    public function deleteEmptyOrderGroups()
    {
        $orderGroups = OrderGroup::doesntHave('orders')->delete();
    }

    /**
     * delete empty orders
     */
    public function deleteExpiredTransactions()
    {
        $transactions = Transaction::where('status', 'waiting_payment')->where('updated_at', '<=', Carbon::now()->subMinutes(12)->toDateTimeString())->delete();
    }

    /**
     * change sent orders to delivered
     */
    public function changeStatusForDeliveredOrders()
    {
        $releaseOfOrderAmounts = Cache::remember('setting_release_of_order_amounts', now()->addMonth(), function () {
            return Setting::where('key', 'release_of_order_amounts')->first();
        });
        $releaseOfOrderAmounts = $releaseOfOrderAmounts != null ? (int)str_replace(',', '', $releaseOfOrderAmounts->value) : null;

        $sentOrders = Order::where('status', 'sent')->where('sent_at', '<=', Carbon::now()->subDays($releaseOfOrderAmounts)->toDateTimeString())->get();
        foreach ($sentOrders as $order) {
            $order->update([
                'status' => 'delivered'
            ]);

            foreach ($order->consignments()->where('status', '!=', 'cancelled')->where('status', '!=', 'returned')->get() as $consignment) {
                $consignment->update([
                    'status' => 'delivered'
                ]);

                foreach ($consignment->consignmentItems()->where('status', '!=', 'cancelled')->where('status', '!=', 'returned')->get() as $consignmentItem) {
                    $consignmentItem->update([
                        'status' => 'delivered'
                    ]);
                }
            }
        }
    }

    /**
     * abandoned Cart Notification
     */
    public function abandonedCartNotification()
    {
        $awaitingOrders = Order::where('status', 'awaiting')->get();

        if ($awaitingOrders != null && count($awaitingOrders) > 0) {
            // get abandoned cart from settings
            $abandonedCart = Cache::remember('setting_abandoned_cart', now()->addMonth(), function () {
                return Setting::where('key', 'abandoned_cart')->first();
            });
            $abandonedCart = $abandonedCart != null ? unserialize($abandonedCart->value) : ['status' => false, 'time_period' => []];

            if ($abandonedCart != null) {
                // check abandoned cart status
                if ($abandonedCart['status'] && count($abandonedCart['time_period']) > 0) {
                    foreach ($awaitingOrders as $order) {
                        $orderMinutesPassed = $order->created_at->diffInMinutes(Carbon::now());
                        $orderAbandonedCart = $order->abandoned_cart ? unserialize($order->abandoned_cart) : [];

                        // find largest time period at this time
                        $maxTimePeriod = null;
                        foreach ($abandonedCart['time_period'] as $tp) {
                            $timePeriod = (int)$tp;
                            if ($orderMinutesPassed >= $timePeriod && !in_array($timePeriod, $orderAbandonedCart)) {
                                // comparission for find large time period
                                if ($maxTimePeriod === null || $timePeriod > $maxTimePeriod) {
                                    $maxTimePeriod = $timePeriod;
                                }
                            }
                        }

                        // check maxTimePeriod exists or not
                        if ($maxTimePeriod !== null) {
                            $newAbandonedCart = $orderAbandonedCart;
                            // sort time_period array
                            $sortedTimePeriods = array_map('intval', $abandonedCart['time_period']);
                            sort($sortedTimePeriods);

                            // add all small time period to passed
                            foreach ($sortedTimePeriods as $timePeriod) {
                                if ($timePeriod <= $maxTimePeriod && !in_array($timePeriod, $newAbandonedCart)) {
                                    $newAbandonedCart[] = $timePeriod;
                                }
                            }

                            // send notification
                            $user = $order->user;
                            $user->notify(new abandonedCart($user->get_name, $order->uuid));

                            // update order
                            $order->update([
                                'abandoned_cart' => serialize($newAbandonedCart),
                            ]);
                        }
                    }
                }
            }
        }
    }
}
