<?php

namespace App\Http\Controllers\Main\Cart\Prefactor;

use App\Helpers\CommonFunctionsHelper;
use App\Helpers\DropdownListHelper;
use App\Helpers\Places;
use App\Helpers\UUIDHelper;
use App\Http\Controllers\Controller;
use App\Http\Requests\User\AddressRequest;
use App\Models\Address;
use App\Models\Consignment;
use App\Models\Order;
use App\Models\PaymentMeta;
use App\Models\Prefactor;
use App\Models\Setting;
use App\Services\Cart\CartService;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Inertia\Inertia;
use Shetabit\Multipay\Drivers\SnappPay\SnappPay;
use App\Payments\Drivers\TorobPay;
use Illuminate\Support\Facades\Cookie;
use Shetabit\Multipay\Exceptions\PurchaseFailedException;
use Shetabit\Multipay\Invoice;
use Shetabit\Payment\Facade\Payment;

class PrefactorsController extends Controller
{
    use Places, UUIDHelper, CommonFunctionsHelper, DropdownListHelper;

    /**
     * cart shiping page
     */
    public function shiping($uuid, CartService $cartService)
    {
        /** @var \App\Models\User $user */
        $user = auth()->user();

        /** @var \App\Models\User $user */
        $prefactor = Prefactor::where('uuid', $uuid)->where('user_id', $user->id)->first();
        if ($prefactor == null || $prefactor->status != 'waiting_payment' || $prefactor->is_expired) {
            abort(404);
        }

        /** @var \App\Models\Order $order */
        $order = $prefactor->order;
        if ($order == null) {
            abort(404);
        }

        // get order consignment
        $consignments = [];
        $consignments = $order->consignments()->with('store')->with('consignmentItems')->get();
        // get consignment items
        $consignmentItems = new Collection();
        foreach ($consignments as $consignment) {
            $consignmentItems = $consignmentItems->merge($consignment->consignmentItems()->get());
        }

        // main store post ways status
        $mainStorePaymentMetaStatus = false;
        $mainStorePaymentMeta = PaymentMeta::where('store_id', null)->first();
        if ($mainStorePaymentMeta != null && $mainStorePaymentMeta->post_ways != null &&  count(unserialize($mainStorePaymentMeta->post_ways)) > 0) {
            $postWays = unserialize($mainStorePaymentMeta->post_ways);
            $hasActive = collect($postWays)->contains(function ($item) {
                return $item['status'] === true;
            });
            $mainStorePaymentMetaStatus = $hasActive;
        }

        // sync consignmentItems count
        foreach ($consignmentItems as $consignmentItem) {
            if ($consignmentItem->store != null && $consignmentItem->store->direct_post && !$consignmentItem->store->transportation_status) {
                $consignmentItem->delete();

                // update consignment items
                $consignmentItems = new Collection();
                foreach ($consignments as $consignment) {
                    $consignmentItems = $consignmentItems->merge($consignment->consignmentItems()->get());
                }
            } else if (!$mainStorePaymentMetaStatus) {
                $consignmentItem->delete();

                // update consignment items
                $consignmentItems = new Collection();
                foreach ($consignments as $consignment) {
                    $consignmentItems = $consignmentItems->merge($consignment->consignmentItems()->get());
                }
            }

            $inventory = $consignmentItem->inventory;
            if ($inventory != null) {
                if ($inventory->count == 0) {
                    $consignmentItem->delete();

                    // update consignment items
                    $consignmentItems = new Collection();
                    foreach ($consignments as $consignment) {
                        $consignmentItems = $consignmentItems->merge($consignment->consignmentItems()->get());
                    }
                } else if ($consignmentItem->count > $inventory->count) {
                    $consignmentItem->update([
                        'count' => $inventory->count
                    ]);
                }
            } else {
                $consignmentItem->delete();
            }
        }

        // validate empty cart
        if ($consignmentItems->count() == 0) {
            abort(404);
        }

        // get customer club (price and score)
        $customerClub = Setting::where('key', 'customer_club')->first();
        $customerClub = $customerClub != null ? unserialize($customerClub->value) : ['price' => 0, 'score' => 0];
        $customerClubScore = $customerClub['price'] > 0 && $customerClub['score'] > 0 ? round(($consignmentItems->sum('get_full_count_price') / str_replace(',', '', $customerClub['price'])) * str_replace(',', '', $customerClub['score'])) : 0;

        // user addresses
        $addresses = $user->addresses()->latest()->get()->map(fn($address) => [
            'id' => $address->id,
            'user_id' => $address->user_id,
            'first_name' => $address->first_name,
            'last_name' => $address->last_name,
            'state' => $address->state,
            'city' => $address->city,
            'phone' => $address->phone,
            'postal_code' => $address->postal_code,
            'full_address' => $address->full_address,
            'active' => $address->active,
            'get_map' => $address->get_map,
            'jalali_created_at' => $address->jalali_created_at,
            'jalali_updated_at' => $address->jalali_updated_at,
        ]);

        // main payment meta
        $paymentMeta = PaymentMeta::whereNull('store_id')->first();

        /********* address *********/
        $selectedAddress = $order->address != null ? unserialize($order->address) : null;
        if ($selectedAddress == null) {
            // delete prev send methods
            $order->update([
                'send_method' => null,
            ]);
            foreach ($consignments as $consignment) {
                $consignment->update([
                    'send_method' => null,
                    'send_type' => null,
                ]);
            }

            // select user active address for default
            $activeAddress = $user->addresses()->where('active', true)->first() != null ? $user->addresses()->where('active', true)->first() : null;
            if ($activeAddress != null) {
                $address = $activeAddress->toArray();
                if (isset($address['safe'])) unset($address['safe']);
                if (isset($address['user'])) unset($address['user']);
                $finalAddress = [
                    'type' => 'user',
                    'address' => $address
                ];
                $order->update([
                    'address' => serialize($finalAddress)
                ]);
                // resync
                $selectedAddress = $order->address != null ? unserialize($order->address) : null;
            }
        }

        $consignmentsInformation = [];
        if ($selectedAddress != null && $selectedAddress['type'] == 'shop') {
            $consignmentsInformation = $cartService->singleConsignmentInformation($order);
        } else {
            // seprated Consignments
            $consignmentsInformation = $cartService->sepratedConsignmentInformation($order, $selectedAddress);

            /********* send method *********/
            if ($selectedAddress != null && $selectedAddress['type'] == 'user') {
                // determine send_method from get_post_ways
                foreach ($consignmentsInformation['consignments'] as $key => &$consignment) {
                    $cs = null;
                    if ($key != 'main') {
                        $cs = Consignment::where('user_id', $user->id)->where('uuid', $consignment['uuid'])->first();
                    }

                    if (!empty($consignment['get_post_ways'])) {
                        if ($key == 'main') {
                            if ($order->send_method == null) {
                                $order->update([
                                    'send_method' => serialize([
                                        'type' => 'post',
                                        'item' => $consignment['get_post_ways'][0]
                                    ])
                                ]);
                            }
                        } else {
                            if ($cs != null && $cs->send_method == null) {
                                $cs->update([
                                    'send_method' => serialize([
                                        'type' => 'post',
                                        'item' => $consignment['get_post_ways'][0]
                                    ]),
                                    'send_type' => 'direct',
                                ]);
                            }
                        }
                    } else {
                        if ($cs != null) {
                            $cs->delete();
                        }
                    }
                }
                unset($consignment);
            }

            // resync seprated Consignments
            $consignmentsInformation = $cartService->sepratedConsignmentInformation($order, $selectedAddress);
        }

        return Inertia::render('Main/Cart/Prefactor/Shiping', [
            'prefactor' => [
                'id' => $prefactor->id,
                'uuid' => $prefactor->uuid,
                'title' => $prefactor->title,
                'status' => $prefactor->status,
                'jalali_expired_at' => $prefactor->jalali_expired_at[2],
                'is_expired' => $prefactor->is_expired,
            ],

            'places' => $this->places(),
            'addresses' => $addresses,

            'selectedAddress' => $selectedAddress,

            'sepratedConsignments' => $consignmentsInformation['consignments'],
            'itemsCount' => count($consignmentItems),
            'financial' => $consignmentsInformation['financial'],
            'customerClubScore' => $customerClubScore,

            'paymentMeta' => $paymentMeta,
        ])->title(__('messages.title.cart_shiping'));
    }

    /**
     * update shiping data
     */
    public function shiping_update($uuid, Request $request, CartService $cartService)
    {
        /** @var \App\Models\User $user */
        $user = auth()->user();
        if (!$user) {
            return redirect()->route('login');
        }

        /** @var \App\Models\User $user */
        $prefactor = Prefactor::where('uuid', $uuid)->where('user_id', $user->id)->first();
        if ($prefactor == null || $prefactor->status != 'waiting_payment' || $prefactor->is_expired) {
            abort(404);
        }

        /** @var \App\Models\Order $order */
        $order = $prefactor->order;
        if ($order == null) {
            abort(404);
        }

        // get order consignment
        $consignments = [];
        $consignments = $order->consignments()->with('consignmentItems')->get();
        // add consignment items
        $consignmentItems = new Collection();
        foreach ($consignments as $consignment) {
            $consignmentItems = $consignmentItems->merge($consignment->consignmentItems()->get());
        }
        // validate empty cart
        if ($consignmentItems->count() == 0) {
            return redirect()->route('index');
        }

        // main payment meta
        $paymentMeta = PaymentMeta::where('store_id', null)->first();

        /*********** address ***********/
        $finalAddress = $order->address != null ? unserialize($order->address) : null;
        if ($request->exists('address_id') && $request->address_id != null && $request->exists('type') && $request->type != null) {
            if ($request->type == 'user') {
                // selected address
                $address = Address::where('id', $request->address_id)->where('user_id', $user->id)->first();
                if ($address == null) {
                    return redirect()->back()->withErrors('messages.address_not_found');
                }

                // activate user address
                $cartService->activateAddress($user, $address);

                $address = $address->toArray();
                if (isset($address['safe'])) unset($address['safe']);
                if (isset($address['user'])) unset($address['user']);
                $finalAddress = [
                    'type' => 'user',
                    'address' => $address
                ];
            } else if ($request->type == 'shop') {
                if (array_key_exists($request->address_id, $paymentMeta->get_reception_centers_addresses) && $paymentMeta->get_reception_centers_addresses[$request->address_id] != null) {
                    $finalAddress = [
                        'type' => 'shop',
                        'address' => [
                            'id' => $request->address_id,
                            ...$paymentMeta->get_reception_centers_addresses[$request->address_id]
                        ],
                    ];
                }
            }

            // validate address
            if ($finalAddress == null || !is_array($finalAddress)) {
                return redirect()->back()->withErrors([__('messages.address_not_selected_or_wrong')]);
            }

            // reset send_method
            $order->update([
                'send_method' => null
            ]);
            foreach ($consignments as $consignment) {
                $consignment->update([
                    'send_method' => null,
                    'send_type' => null,
                ]);
            }
        }
        // update address
        $order->update([
            'address' => serialize($finalAddress),
        ]);
        $finalAddress = $order->address != null ? unserialize($order->address) : null;

        /*********** send method ***********/
        $sendMethod = null;
        if ($finalAddress != null && $finalAddress['type'] == 'user') {
            if ($request->exists('consignment_uuid') && $request->consignment_uuid != null && $request->exists('send_method_uuid') && $request->send_method_uuid != null) {
                if ($request->exists('type') && $request->type == 'main') {
                    if ($order == null) {
                        return redirect()->back()->withErrors([__('messages.unknown_error')]);
                    }

                    $defaultPostWays = $paymentMeta?->get_post_ways ?? [];
                    $defaultPostWays = array_values(array_filter($defaultPostWays, fn($way) => $way['status'] ?? false));
                    $selectedSendMethod = array_filter($defaultPostWays, fn($item) => $item['uuid'] === $request->send_method_uuid);
                    if (count($selectedSendMethod) > 0) {
                        $sendMethod = [
                            'type' => 'post',
                            'item' => reset($selectedSendMethod)
                        ];

                        $order->update([
                            'send_method' => serialize($sendMethod),
                        ]);
                    }
                } else if ($request->exists('type') && $request->type == 'direct') {
                    $consignment = Consignment::where('user_id', $user->id)->where('uuid', $request->consignment_uuid)->first();
                    if ($consignment == null) {
                        return redirect()->back()->withErrors([__('messages.unknown_error')]);
                    }

                    $consignmentStorePaymentMeta = $consignment->store->paymentMeta;
                    $consignmentStorePaymentMeta = $consignmentStorePaymentMeta?->get_post_ways ?? [];
                    $consignmentStorePaymentMeta = array_values(array_filter($consignmentStorePaymentMeta, fn($way) => $way['status'] ?? false));
                    $selectedSendMethod = array_filter($consignmentStorePaymentMeta, fn($item) => $item['uuid'] === $request->send_method_uuid);
                    if (count($selectedSendMethod) > 0) {
                        $sendMethod = [
                            'type' => 'post',
                            'item' => reset($selectedSendMethod)
                        ];

                        $consignment->update([
                            'send_method' => serialize($sendMethod),
                            'send_type' => 'direct',
                        ]);
                    }
                }

                // validate send method
                if ($sendMethod == null || !is_array($sendMethod)) {
                    return redirect()->back()->withErrors([__('messages.send_way_not_selected_or_wrong')]);
                }
            }
        } else if ($finalAddress != null && $finalAddress['type'] == 'shop') {
            $sendMethod = [
                'type' => 'inperson',
            ];

            $order->update([
                'send_method' => serialize($sendMethod),
            ]);
        }

        // update and return back
        return redirect()->route('prefactor.shiping', $prefactor->uuid);
    }

    /**
     * cart payment page
     */
    public function payment($uuid, Request $request, CartService $cartService)
    {
        /** @var \App\Models\User $user */
        $user = auth()->user();

        /** @var \App\Models\User $user */
        $prefactor = Prefactor::where('uuid', $uuid)->where('user_id', $user->id)->first();
        if ($prefactor == null || $prefactor->status != 'waiting_payment' || $prefactor->is_expired) {
            abort(404);
        }

        /** @var \App\Models\Order $order */
        $order = $prefactor->order;
        if ($order == null) {
            abort(404);
        }

        // selected address
        $selectedAddress = $order->address != null ? unserialize($order->address) : null;

        // send method status
        $sendMethodValidation = $order->send_method != null ? true : false;

        // tax
        $vaTaxPercent = Setting::where('key', 'va_tax_percent')->first();
        $vaTaxPercent = $vaTaxPercent != null ? (int)$vaTaxPercent->value : 0;
        $order->update([
            'va_tax_percent' => $vaTaxPercent,
        ]);

        // get order consignment
        $consignments = $order->consignments()->with('consignmentItems')->get();

        // add consignment items
        $consignmentItems = new Collection();
        foreach ($consignments as $consignment) {
            if ($consignment->send_method != null) {
                $sendMethodValidation = true;
            }
            $consignmentItems = $consignmentItems->merge($consignment->consignmentItems()->get());
        }

        // validate address and send_method
        if ($selectedAddress == null || !$sendMethodValidation) {
            return redirect()->route('prefactor.shiping', $prefactor->uuid);
        }

        // main store post ways status
        $mainStorePaymentMetaStatus = false;
        $mainStorePaymentMeta = PaymentMeta::where('store_id', null)->first();
        if ($mainStorePaymentMeta != null && $mainStorePaymentMeta->post_ways != null &&  count(unserialize($mainStorePaymentMeta->post_ways)) > 0) {
            $postWays = unserialize($mainStorePaymentMeta->post_ways);
            $hasActive = collect($postWays)->contains(function ($item) {
                return $item['status'] === true;
            });
            $mainStorePaymentMetaStatus = $hasActive;
        }

        // sync consignmentItems count
        foreach ($consignmentItems as $consignmentItem) {
            if ($consignmentItem->store != null && $consignmentItem->store->direct_post && !$consignmentItem->store->transportation_status) {
                $consignmentItem->delete();

                // update consignment items
                $consignmentItems = new Collection();
                foreach ($consignments as $consignment) {
                    $consignmentItems = $consignmentItems->merge($consignment->consignmentItems()->get());
                }
            } else if (!$mainStorePaymentMetaStatus) {
                $consignmentItem->delete();

                // update consignment items
                $consignmentItems = new Collection();
                foreach ($consignments as $consignment) {
                    $consignmentItems = $consignmentItems->merge($consignment->consignmentItems()->get());
                }
            }

            $inventory = $consignmentItem->inventory;
            if ($inventory != null) {
                if ($inventory->count == 0) {
                    $consignmentItem->delete();

                    // update consignment items
                    $consignmentItems = new Collection();
                    foreach ($consignments as $consignment) {
                        $consignmentItems = $consignmentItems->merge($consignment->consignmentItems()->get());
                    }
                } else if ($consignmentItem->count > $inventory->count) {
                    $consignmentItem->update([
                        'count' => $inventory->count
                    ]);
                }
            } else {
                $consignmentItem->delete();
            }
        }

        // validate empty cart
        if ($consignmentItems->count() == 0) {
            abort(404);
        }

        // consignments information array
        $consignmentsInformation = $cartService->sepratedConsignmentInformation($order, $selectedAddress);
        // validate send_method
        if ($selectedAddress['type'] == 'user') {
            foreach ($consignmentsInformation['consignments'] as &$cns) {
                if ($cns['send_method'] == null) {
                    return redirect()->route('prefactor.shiping', $prefactor->uuid);
                }
            }
            unset($cns);
        }

        // get customer club (price and score)
        $customerClub = Setting::where('key', 'customer_club')->first();
        $customerClub = $customerClub != null ? unserialize($customerClub->value) : ['price' => 0, 'score' => 0];
        $customerClubScore = $customerClub['price'] > 0 && $customerClub['score'] > 0 ? round(($consignmentItems->sum('get_full_count_price') / str_replace(',', '', $customerClub['price'])) * str_replace(',', '', $customerClub['score'])) : 0;

        // get payment gateways
        $allPaymentGateways = $this->paymentGatewayList();
        $paymentGatewaysList = Setting::where('key', 'payment_gateway')->first() != null
            ? unserialize(Setting::where('key', 'payment_gateway')->first()->value)
            : [];

        // حذف درگاه‌هایی که دیگر در allPaymentGateways وجود ندارند
        $validIds = array_column($allPaymentGateways, 'id');
        $paymentGatewaysList = array_filter($paymentGatewaysList, function ($pg) use ($validIds) {
            return in_array($pg['id'], $validIds);
        });

        // create payment gateway final result for active pg's
        $paymentGateways = array_filter(array_map(function ($pg) {
            $data = [
                'id' => $pg['id'],
                'label' => $pg['label'],
                'icon' => $pg['icon'] ?? null,
                'status' => $pg['status'],
            ];

            if ($pg['id'] === 'card_to_card') {
                $data['fields'] = $pg['fields'];
            }

            return $data;
        }, $paymentGatewaysList), function ($pg) {
            return $pg['status'];
        });

        // transfer card to card to end of array
        usort($paymentGateways, fn($a) => $a['id'] === 'card_to_card' ? 1 : -1);
        $paymentGateways = array_values($paymentGateways);

        // snapppay eligible
        if (in_array('snapppay', array_column($paymentGateways, 'id'))) {
            try {
                // set payment gateway driver configs
                foreach ($paymentGatewaysList as $pg) {
                    foreach ($pg['fields'] as $key => $field) {
                        Config::set('payment.drivers.' . $pg['id'] . '.' . $key, $field['value']);
                    }
                }

                $invoice = new Invoice;
                $invoice->amount($consignmentsInformation['financial']['totalFinalPrice']);

                // select driver and invoice
                $settings = config('payment.drivers.snapppay');
                $snappPayDriver = new SnappPay($invoice, $settings);

                $result = $snappPayDriver->eligible();

                if (isset($result['eligible']) && $result['eligible'] === true) {
                    foreach ($paymentGateways as $key => $gateway) {
                        if ($gateway['id'] === 'snapppay') {
                            $paymentGateways[$key] = [
                                'id' => 'snapppay',
                                'eligible' => $result['eligible'],
                                'label' => $result['title_message'] ?? null,
                                'description' => $result['description'] ?? null,
                                'status' => true,
                            ];
                            break;
                        }
                    }
                } else {
                    // delete snapppay
                    foreach ($paymentGateways as $key => $gateway) {
                        if ($gateway['id'] === 'snapppay') {
                            unset($paymentGateways[$key]);
                            break;
                        }
                    }
                }
            } catch (\Exception $e) {
                // delete snapppay
                foreach ($paymentGateways as $key => $gateway) {
                    if ($gateway['id'] === 'snapppay') {
                        unset($paymentGateways[$key]);
                        break;
                    }
                }
            }
        }

        // torobpay eligible
        if (in_array('torobpay', array_column($paymentGateways, 'id'))) {
            try {
                // set payment gateway driver configs
                foreach ($paymentGatewaysList as $pg) {
                    foreach ($pg['fields'] as $key => $field) {
                        Config::set('payment.drivers.' . $pg['id'] . '.' . $key, $field['value']);
                    }
                }

                $invoice = new Invoice;
                $invoice->amount($consignmentsInformation['financial']['totalFinalPrice']);

                // select driver and invoice
                $settings = config('payment.drivers.torobpay');
                $torobPayDriver = new TorobPay($invoice, $settings);

                $result = $torobPayDriver->eligible();

                if (isset($result['eligible']) && $result['eligible'] === true) {
                    foreach ($paymentGateways as $key => $gateway) {
                        if ($gateway['id'] === 'torobpay') {
                            $paymentGateways[$key] = [
                                'id' => 'torobpay',
                                'eligible' => $result['eligible'],
                                'label' => $result['title_message'] ?? null,
                                'description' => $result['description'] ?? null,
                                'status' => true,
                            ];
                            break;
                        }
                    }
                } else {
                    // delete torobpay
                    foreach ($paymentGateways as $key => $gateway) {
                        if ($gateway['id'] === 'torobpay') {
                            unset($paymentGateways[$key]);
                            break;
                        }
                    }
                }
            } catch (\Exception $e) {
                // delete torobpay
                foreach ($paymentGateways as $key => $gateway) {
                    if ($gateway['id'] === 'torobpay') {
                        unset($paymentGateways[$key]);
                        break;
                    }
                }
            }
        }

        // add gateways icon
        $iconsMap = [];
        foreach ($allPaymentGateways as $original) {
            if (!empty($original['icon'])) {
                $iconsMap[$original['id']] = $original['icon'];
            }
        }
        foreach ($paymentGateways as &$pg) {
            if (empty($pg['icon']) && isset($iconsMap[$pg['id']])) {
                $pg['icon'] = $iconsMap[$pg['id']];
            }
        }
        unset($pg);

        // vpn warning
        $vpnWarningStatus = false;
        $vpnWarning = Setting::where('key', 'vpn_warning')->get()->first();
        $vpnWarning = $vpnWarning != null ? (bool)$vpnWarning->value : false;
        if ($vpnWarning) {
            try {
                $userIP = $request->ip();
                $data = Cache::remember('ip-' . $userIP, now()->addHours(1), function () use ($userIP) {
                    $response = Http::get('http://ip-api.com/json/' . $userIP);
                    return $response->json();
                });

                if ($data['status'] === 'success' && $data['countryCode'] !== 'IR') {
                    $vpnWarningStatus = true;
                }
            } catch (\Exception $e) {
                Log::error('IP-API Error: ' . $e->getMessage());
            }
        }

        return Inertia::render('Main/Cart/Prefactor/Payment', [
            'prefactor' => [
                'id' => $prefactor->id,
                'uuid' => $prefactor->uuid,
                'title' => $prefactor->title,
                'status' => $prefactor->status,
                'jalali_expired_at' => $prefactor->jalali_expired_at[2],
                'is_expired' => $prefactor->is_expired,
            ],

            'selectedAddress' => $selectedAddress,

            'sepratedConsignments' => $consignmentsInformation['consignments'],
            'itemsCount' => count($consignmentItems),
            'financial' => $consignmentsInformation['financial'],
            'discount' => $order->discount != null ? unserialize($order->discount) : null,
            'customerClubScore' => $customerClubScore,

            'paymentGateways' => array_values($paymentGateways),
            'vpn_warning_status' => $vpnWarningStatus
        ])->title(__('messages.title.cart_payment'));
    }

    /**
     * go to payment gateway
     */
    public function gateway($uuid, Request $request, CartService $cartService)
    {
        /** @var \App\Models\User $user */
        $user = auth()->user();

        /** @var \App\Models\User $user */
        $prefactor = Prefactor::where('uuid', $uuid)->where('user_id', $user->id)->first();
        if ($prefactor == null || $prefactor->status != 'waiting_payment' || $prefactor->is_expired) {
            abort(404);
        }

        /** @var \App\Models\Order $order */
        $order = $prefactor->order;
        if ($order == null) {
            abort(404);
        }
        if ($order->address == null) {
            return redirect()->route('prefactor.shiping', $prefactor->uuid);
        }

        // add order description
        $order->update([
            'description' => $request->exists('description') ? $request->description : null,
        ]);

        // selected address
        $selectedAddress = $order->address != null ? unserialize($order->address) : null;

        // consignments information array
        $consignmentsInformation = $cartService->sepratedConsignmentInformation($order, $selectedAddress);

        // payment gateways
        $paymentGateways = Setting::where('key', 'payment_gateway')->first() != null ? unserialize(Setting::where('key', 'payment_gateway')->first()->value) : [];

        // validate existing payment gateways
        if (count($paymentGateways) == 0) {
            return redirect()->back()->withErrors([__('messages.no_any_payment_gateways')]);
        }

        // validate selected payment gateway
        $paymentGatewaysIDStatus = [];
        foreach ($paymentGateways as $pg) {
            $paymentGatewaysIDStatus[$pg['id']] = $pg['status'];
        }
        if (!$request->exists('payment_gateway') || $request->payment_gateway == null || $request->payment_gateway == '')
            return redirect()->back()->withErrors([__('messages.no_any_selected_payment_gateway')]);
        if (!array_key_exists($request->payment_gateway, $paymentGatewaysIDStatus) || !$paymentGatewaysIDStatus[$request->payment_gateway])
            return redirect()->back()->withErrors([__('messages.selected_payment_gateway_not_active')]);

        // set payment gateway driver configs
        foreach ($paymentGateways as $pg) {
            foreach ($pg['fields'] as $key => $field) {
                Config::set('payment.drivers.' . $pg['id'] . '.' . $key, $field['value']);
            }
        }

        // get order consignment
        $consignments = $order->consignments()->with('consignmentItems')->get();

        // add consignment items
        $consignmentItems = new Collection();
        $consignmentItemsForEdit = new Collection();
        foreach ($consignments as $consignment) {
            $consignmentItems = $consignmentItems->merge($consignment->consignmentItems()->get());
            $consignmentItemsForEdit = $consignmentItemsForEdit->merge($consignment->consignmentItems()->get());
        }

        // recharge consignments time for 1 day if less than 10 minutes
        foreach ($consignmentItemsForEdit as $ci) {
            $updatedAt = Carbon::parse($ci->updated_at);
            $expireAt = $updatedAt->copy()->addDays(15);
            if ($expireAt->diffInMinutes(now()) <= 10) {
                $ci->update([
                    'updated_at' => $updatedAt->addDay()
                ]);
            }
        }

        // validate empty cart
        if ($consignmentItems->count() == 0) {
            return redirect()->route('index');
        }

        // validate send_method
        if ($selectedAddress['type'] == 'user') {
            foreach ($consignmentsInformation['consignments'] as &$cns) {
                if ($cns['send_method'] == null) {
                    return redirect()->route('prefactor.shiping', $prefactor->uuid);
                }
            }
            unset($cns);
        }

        // check wallet
        $payablePrice = $consignmentsInformation['financial']['totalFinalPrice'];
        if ($request->payment_gateway != 'snapppay' && $request->payment_gateway != 'digipay' && $request->payment_gateway != 'azki' && $request->payment_gateway != 'torobpay' && $request->payment_gateway != 'card_to_card') {
            if ($request->use_wallet) {
                if ($consignmentsInformation['financial']['totalFinalPrice'] - $user->wallet->amount <= 0) {
                    // pay just with wallet
                    $orderUUID = $cartService->registerPrefactorOrder($prefactor->uuid);
                    if ($orderUUID === false) {
                        return redirect()->route('prefactor.shiping', ['uuid' => $prefactor->uuid, 'status' => 'error']);
                    } else if ($orderUUID) {
                        return redirect()->route('cart.order.status', ['order' => $orderUUID, 'prefactor' => $prefactor->uuid]);
                    }

                    // order registeration error
                    return redirect()->route('prefactor.shiping', ['uuid' => $prefactor->uuid, 'status' => 'error']);
                }

                $payablePrice = $consignmentsInformation['financial']['totalFinalPrice'] - $user->wallet->amount;
            }
        }

        // handle card to card
        if ($request->payment_gateway == 'card_to_card') {
            $request->validate([
                'receipt' => [
                    'required',
                    'file',
                    'mimes:png,jpg,jpeg',
                    'max:5120'
                ]
            ]);

            // upload receipt file
            $receiptFileID = $cartService->uploadReceiptFile($request->file('receipt'));
            if ($receiptFileID === false) {
                return redirect()->back()->withErrors([__('messages.error_while_uploading_image')]);
            }

            // register order without wallet
            $orderUUID = $cartService->registerOrderCardToCard($payablePrice, $receiptFileID, $uuid, true);
            if ($orderUUID === false) {
                return redirect()->route('cart', ['status' => 'error']);
            } else if ($orderUUID) {
                // delete affiliate cookies
                Cookie::queue(Cookie::forget('affiliates'));

                return redirect()->route('cart.order.status', ['order' => $orderUUID]);
            }
        }

        // add transaction
        $transaction = $user->transactions()->create([
            'amount' => $payablePrice,
            'transaction_id' => $this->generateUniqueRandomNumber(9, \App\Models\Transaction::class, 'transaction_id'),
            'payment_gateway' => $request->payment_gateway,
            'payment_details' => serialize([
                'type' => 'prefactor',
                'uuid' => $prefactor->uuid
            ]),
            'description' => __('messages.transaction_user_charge'),
            'type' => 'user_charge',
            'status' => 'waiting_payment'
        ]);

        // create invoice and send to payment gateway
        $invoice = new Invoice;
        if ($request->payment_gateway === 'vandar') {
            $invoice->amount($payablePrice * 10);
        } else {
            $invoice->amount($payablePrice);
        }
        $invoice->detail(['mobile' => $this->convertToInternationalFormat($user->phone)]);
        $invoice->detail(['transaction_id' => $transaction->id]);

        // handle snapppay
        if ($request->payment_gateway === 'snapppay') {
            // set order id
            $snapppayOrderUUID = $this->generateUniqueRandomNumber(10, \App\Models\OrderGroup::class, 'uuid');
            $invoice->uuid($snapppayOrderUUID);

            // items
            $cartItems = [];
            $cartTotalAmount = 0;

            foreach ($consignmentsInformation['consignments'] as $cartData) {
                foreach ($cartData['items'] as $item) {
                    $cartItems[] = [
                        'amount' => $item['price'],
                        'category' => $item['product']['category'],
                        'count' => $item['count'],
                        'id' => $item['id'],
                        'name' => $item['product']['title'],
                        'commissionType' => 100,
                    ];

                    $cartTotalAmount += ($item['price']) * $item['count'];
                }
            }

            $cartList = [
                [
                    'cartId' => 1,
                    'cartItems' => $cartItems,
                    'isShipmentIncluded' => true,
                    'isTaxIncluded' => true,
                    'shippingAmount' => $consignmentsInformation['financial']['sendPrice'],
                    'taxAmount' => 0,
                    'totalAmount' => $cartTotalAmount + $consignmentsInformation['financial']['sendPrice'],
                ]
            ];

            $invoice->detail('cartList', $cartList);
            $invoice->detail('discountAmount', $consignmentsInformation['financial']['totalCouponDiscountPrice']);
            $invoice->detail('externalSourceAmount', 0);
        }

        // handle digipay
        if ($request->payment_gateway === 'digipay') {
            $cartItems = [];
            foreach ($consignmentsInformation['consignments'] as $cartData) {
                foreach ($cartData['items'] as $item) {
                    $cartItems[] = [
                        'productCode' => (string)$item['id'],
                        'productType' => 1,
                        'count' => $item['count'],
                        'categoryId' => $item['product']['category'],
                    ];
                }
            }

            $invoice->detail([
                'phone' => $user->phone,
                'basketDetailsDto' => [
                    'basketId' => (string)random_int(1000000000, 9999999999),
                    'items' => $cartItems
                ],
            ]);
        }

        // handle azki
        if ($request->payment_gateway === 'azki') {
            $cartItems = [];
            foreach ($consignmentsInformation['consignments'] as $cartData) {
                foreach ($cartData['items'] as $item) {
                    $cartItems[] = [
                        'name' => $item['product']['title'],
                        'count' => $item['count'],
                        'amount' => $item['price'],
                        'url' => route('main.products.show', $item['product']['slug']),
                    ];
                }
            }

            // add send price
            $cartItems[] = [
                'name' => __('messages.sentence.send_price'),
                'count' => 1,
                'amount' => $consignmentsInformation['financial']['sendPrice'],
                'url' => null,
            ];

            // subtract discount coupon price
            if ($consignmentsInformation['financial']['totalCouponDiscountPrice'] > 0) {
                $cartItems[] = [
                    'name' => __('messages.sentence.deduction_with_discount_code'),
                    'count' => 1,
                    'amount' => $consignmentsInformation['financial']['totalCouponDiscountPrice'] * -1,
                    'url' => null,
                ];
            }

            $invoice->detail([
                'items' => $cartItems,
            ]);
        }

        // handle torobpay
        if ($request->payment_gateway === 'torobpay') {
            // set order id
            $torobpayOrderUUID = $this->generateUniqueRandomNumber(10, \App\Models\OrderGroup::class, 'uuid');
            $invoice->uuid($torobpayOrderUUID);

            // items
            $cartItems = [];
            $cartTotalAmount = 0;

            foreach ($consignmentsInformation['consignments'] as $cartData) {
                foreach ($cartData['items'] as $item) {
                    $cartItems[] = [
                        'amount' => $item['price'],
                        'category' => $item['product']['category'],
                        'count' => $item['count'],
                        'id' => $item['id'],
                        'name' => $item['product']['title'],
                        'commissionType' => 0,
                    ];

                    $cartTotalAmount += ($item['price']) * $item['count'];
                }
            }

            $cartList = [
                [
                    'cartId' => 1,
                    'cartItems' => $cartItems,
                    'isShipmentIncluded' => true,
                    'isTaxIncluded' => true,
                    'shippingAmount' => $consignmentsInformation['financial']['sendPrice'],
                    'taxAmount' => 0,
                    'totalAmount' => $cartTotalAmount + $consignmentsInformation['financial']['sendPrice'],
                ]
            ];

            $invoice->detail('cartList', $cartList);
            $invoice->detail('discountAmount', $consignmentsInformation['financial']['totalCouponDiscountPrice']);
            $invoice->detail('externalSourceAmount', 0);
            // address
            $invoice->detail('address', $selectedAddress['address']['full_address']);
            $invoice->detail('postalCode', $selectedAddress['address']['postal_code']);
            $invoice->detail('customer_full_name', $user->name);
            $invoice->detail('city', $selectedAddress['address']['city']);
            $invoice->detail('province', $selectedAddress['address']['state']);
            $invoice->detail('registration_phone_number', $this->convertToInternationalFormat($user->phone));
        }

        // handle toman
        if ($request->payment_gateway === 'toman') {
            $tomanItems = [];
            $tomanSendPrice = 0;

            foreach ($consignmentsInformation['consignments'] as $cartData) {
                foreach ($cartData['items'] as $item) {
                    $tomanItems[] = [
                        'name' => $item['product']['title'],
                        'price' => $item['price'] * 10,
                        'quantity' => $item['count']
                    ];
                }

                $tomanSendPrice += $cartData['sendPrice'];
            }

            // add shipment price
            $tomanItems[] = [
                'name' => 'هزینه ارسال',
                'price' => $tomanSendPrice * 10,
                'quantity' => 1
            ];

            // subtract wallet amount
            $tomanItems[] = [
                'name' => 'کسر کیف پول',
                'price' => ($user->wallet->amount * 10) * -1,
                'quantity' => 1
            ];

            Config::set('payment.drivers.toman.data', [
                'res_number' => $transaction->uuid,
                'return_to' => route('cart.payment.callback_get'),
                'items' => $tomanItems
            ]);
        }

        // handle payping
        if ($request->payment_gateway === 'payping') {
            //
        }

        // You can specify callbackUrl
        $pay = null;
        try {
            $pay = Payment::via($request->payment_gateway)->callbackUrl(route('cart.payment.callback_get'))->purchase(
                $invoice,
                function ($driver, $transactionId) use ($transaction) {
                    $transaction->update([
                        'gateway_transaction_id' => $transactionId,
                    ]);
                }
            );
        } catch (PurchaseFailedException $e) {
            return redirect()->back()->withErrors([$e->getMessage()]);
        }

        if ($pay != null) {
            $redirectionForm = $pay->pay();
            $data = [
                'action' => $redirectionForm->getAction(),
                'method' => $redirectionForm->getMethod(),
                'inputs' => $redirectionForm->getInputs(),
            ];

            return Inertia::render('Main/Cart/PaymentRedirect', $data);
        }

        return redirect()->back()->withErrors([__('messages.error_in_payment_gateway')]);
    }

    /**
     * update address
     */
    public function store_address($uuid, AddressRequest $request)
    {
        /** user
         * @var \App\Models\User $user
         */
        $user = auth()->user();

        /** @var \App\Models\User $user */
        $prefactor = Prefactor::where('uuid', $uuid)->where('user_id', $user->id)->first();
        if ($prefactor == null || $prefactor->status != 'waiting_payment') {
            abort(404);
        }

        /** @var \App\Models\Order $order */
        $order = $prefactor->order;
        if ($order == null) {
            abort(404);
        }

        /** postal code validation */
        if (strlen(\request('postal_code')) != 10) {
            return redirect()->back()->withErrors([__('messages.postal_code_wrong')]);
        }

        /** map longitude & latitude */
        $map = [];
        if ($request->exists('map') && is_array($request->map)) {
            $map = $request->map;
        }

        /** check active status */
        $active = false;
        if ($user->addresses()->count() == 0) {
            $active = true;
        }

        $address = $user->addresses()->create([
            'first_name' => $request->first_name,
            'last_name' => $request->last_name,
            'state' => $request->state,
            'city' => $request->city,
            'phone' => $request->phone,
            'postal_code' => $request->postal_code,
            'full_address' => $request->full_address,
            'map' => serialize($map),
            'active' => $active
        ]);

        if ($order != null) {
            if ($address != null) {
                $address = $address->toArray();
                unset($address['user']);
                unset($address['safe']);
                $order->update([
                    'address' => serialize([
                        'type' => 'user',
                        'address' => $address
                    ]),
                    'send_method' => null,
                ]);
            }
        }

        return redirect()->route('prefactor.shiping', $prefactor->uuid)->with('message', [__('messages.address_created')]);
    }

    /**
     * update address
     */
    public function update_address($uuid, AddressRequest $request, Address $address)
    {
        /** user
         * @var App\Models\User $user
         */
        $user = auth()->user();

        /** @var \App\Models\User $user */
        $prefactor = Prefactor::where('uuid', $uuid)->where('user_id', $user->id)->first();
        if ($prefactor == null || $prefactor->status != 'waiting_payment') {
            abort(404);
        }

        /** @var \App\Models\Order $order */
        $order = $prefactor->order;
        if ($order == null) {
            abort(404);
        }

        /** check user validation */
        if ($address->user_id != $user->id) {
            return redirect()->back()->withErrors([__('messages.unauthorized')]);
        }

        /** postal code validation */
        if (strlen(\request('postal_code')) != 10) {
            return redirect()->back()->withErrors([__('messages.postal_code_wrong')]);
        }

        /** map longitude & latitude */
        $map = [];
        if ($request->exists('map') && is_array($request->map)) {
            $map = $request->map;
        }

        $address->update([
            'first_name' => $request->first_name,
            'last_name' => $request->last_name,
            'state' => $request->state,
            'city' => $request->city,
            'phone' => $request->phone,
            'postal_code' => $request->postal_code,
            'full_address' => $request->full_address,
            'map' => serialize($map)
        ]);

        $selectedAddress = $order != null && $order->address != null ? unserialize($order->address) : null;
        if ($selectedAddress != null && $address->id == $selectedAddress['address']['id']) {
            $newAddress = $user->addresses()->where('id', $address->id)->first();
            if ($newAddress != null) {
                $newAddress = $newAddress->toArray();
                unset($newAddress['user']);
                unset($newAddress['safe']);
                $order->update([
                    'address' => serialize([
                        'type' => 'user',
                        'address' => $newAddress
                    ]),
                    'send_method' => null,
                ]);
            } else {
                $order->update([
                    'address' => null,
                    'send_method' => null,
                ]);
            }
        }

        return redirect()->route('prefactor.shiping', $prefactor->uuid)->with('message', [__('messages.address_edited')]);
    }

    /**
     * delete address
     */
    public function delete_address($uuid, Address $address)
    {
        /** user
         * @var App\Models\User $user
         */
        $user = auth()->user();

        /** @var \App\Models\User $user */
        $prefactor = Prefactor::where('uuid', $uuid)->where('user_id', $user->id)->first();
        if ($prefactor == null || $prefactor->status != 'waiting_payment') {
            abort(404);
        }

        /** @var \App\Models\Order $order */
        $order = $prefactor->order;
        if ($order == null) {
            abort(404);
        }

        /** check user validation */
        if ($address->user_id != $user->id) {
            return redirect()->back()->withErrors([__('messages.unauthorized')]);
        }

        $address->delete();

        $order->update([
            'address' => null,
            'send_method' => null,
        ]);

        return redirect()->route('prefactor.shiping', $prefactor->uuid)->with('message', [__('messages.address_deleted')]);
    }
}