/home/brandsfa/.trash/app/Http/Controllers/Admin/Vendor/VendorController.php
<?php

namespace App\Http\Controllers\Admin\Vendor;

use App\Contracts\Repositories\DeliveryManRepositoryInterface;
use App\Contracts\Repositories\DeliveryZipCodeRepositoryInterface;
use App\Contracts\Repositories\OrderRepositoryInterface;
use App\Contracts\Repositories\OrderTransactionRepositoryInterface;
use App\Contracts\Repositories\ProductRepositoryInterface;
use App\Contracts\Repositories\ReviewRepositoryInterface;
use App\Contracts\Repositories\ShippingAddressRepositoryInterface;
use App\Contracts\Repositories\ShopRepositoryInterface;
use App\Contracts\Repositories\VendorRepositoryInterface;
use App\Contracts\Repositories\VendorWalletRepositoryInterface;
use App\Contracts\Repositories\WithdrawRequestRepositoryInterface;
use App\Enums\ExportFileNames\Admin\Vendor as VendorExport;
use App\Enums\ViewPaths\Admin\Vendor;
use App\Enums\WebConfigKey;
use App\Events\VendorRegistrationMailEvent;
use App\Events\VendorStatusUpdateEvent;
use App\Events\WithdrawStatusUpdateEvent;
use App\Exports\SellerListExport;
use App\Exports\SellerWithdrawRequest;
use App\Http\Controllers\BaseController;
use App\Http\Requests\Admin\VendorAddRequest;
use App\Services\ShopService;
use App\Services\VendorService;
use App\Traits\CommonTrait;
use App\Traits\PaginatorTrait;
use App\Traits\PushNotificationTrait;
use Brian2694\Toastr\Facades\Toastr;
use Illuminate\Contracts\View\View;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Maatwebsite\Excel\Facades\Excel;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

class VendorController extends BaseController
{
    use PaginatorTrait;
    use CommonTrait;
    use PushNotificationTrait;

    public function __construct(
        private readonly VendorRepositoryInterface $vendorRepo,
        private readonly OrderRepositoryInterface $orderRepo,
        private readonly ProductRepositoryInterface $productRepo,
        private readonly ReviewRepositoryInterface $reviewRepo,
        private readonly DeliveryManRepositoryInterface $deliveryManRepo,
        private readonly OrderTransactionRepositoryInterface $orderTransactionRepo,
        private readonly ShippingAddressRepositoryInterface $shippingAddressRepo,
        private readonly DeliveryZipCodeRepositoryInterface $deliveryZipCodeRepo,
        private readonly WithdrawRequestRepositoryInterface $withdrawRequestRepo,
        private readonly VendorWalletRepositoryInterface $vendorWalletRepo,
        private readonly ShopRepositoryInterface $shopRepo,
        private readonly VendorService $vendorService,
        private readonly ShopService $shopService,
    )
    {
    }

    /**
     * @param Request|null $request
     * @param string|null $type
     * @return View
     * Index function is the starting point of a controller
     */
    public function index(Request|null $request, string $type = null): View
    {
        return $this->getListView($request);
    }

    public function getListView(Request $request): View
    {
        $current_date = date('Y-m-d');
        $sellers = $this->vendorRepo->getListWhere(
            orderBy:['id'=>'desc'],
            searchValue: $request['searchValue'],
            relations: ['orders', 'product'],
            dataLimit:getWebConfig(name: WebConfigKey::PAGINATION_LIMIT)
        );
        return view(Vendor::LIST[VIEW], compact('sellers', 'current_date'));
    }

    public function getAddView(Request $request): View
    {
        return view(Vendor::ADD[VIEW]);
    }
    public function add(VendorAddRequest $request):JsonResponse
    {
        $vendor = $this->vendorRepo->add(data: $this->vendorService->getAddData($request));
        $this->shopRepo->add($this->shopService->getAddShopDataForRegistration(request:$request,vendorId: $vendor['id']));
        $this->vendorWalletRepo->add($this->vendorService->getInitialWalletData(vendorId:$vendor['id']));
        $data = [
            'name' => $request['f_name'],
            'status' => 'pending',
            'subject' => translate('Vendor_Registration_Successfully_Completed'),
            'title' => translate('Registration_Complete').'!',
            'message' => translate('congratulation').'!'.translate('Your_registration_request_has_been_send_to_admin_successfully').'!'.translate('Please_wait_until_admin_reviewal').'.',
        ];
        event(new VendorRegistrationMailEvent($request['email'],$data));
        return response()->json(['message'=>translate('vendor_added_successfully')]);
    }

    public function updateStatus(Request $request): RedirectResponse
    {
        $vendor = $this->vendorRepo->getFirstWhere(params:['id' => $request['id']]);
        $this->vendorRepo->update(id: $request['id'], data: ['status' => $request['status']]);
        if ($request['status'] == "approved") {
            Toastr::success(translate('Vendor_has_been_approved_successfully'));
        } else if ($request['status'] == "rejected") {
            Toastr::info(translate('Vendor_has_been_rejected_successfully'));
        } else if ($request['status'] == "suspended") {
            $this->vendorRepo->update(id: $request['id'], data: ['auth_token' => Str::random(80)]);
            Toastr::info(translate('Vendor_has_been_suspended_successfully'));
        }
        if ($vendor['status'] == 'pending'){
            if ($request['status'] == "approved"){
                $data = [
                    'name' => $vendor['f_name'],
                    'status' => 'approved',
                    'subject' => translate('Vendor_Registration_Approved'),
                    'title' => translate('registration_Approved').'!',
                    'message' => translate('your_registration_request_has_been_approved_by_admin').'.'.translate('now_you_can_complete_your_store_setting_and_start_selling_your_product_on').getWebConfig('company_name').'.',
                ];
            }elseif ($request['status'] == "rejected"){
                $data = [
                    'name' => $vendor['f_name'],
                    'status' => 'denied',
                    'subject' => translate('Vendor_Registration_Denied'),
                    'title' => translate('registration_Denied').'!',
                    'message' => translate('your_registration_request_has_been_denied_by_admin').'.'.translate('please_contact_with_admin_or_support_center_if_you_have_any_queries').'.',
                ];
            }
        }else{
            if ($request['status'] == "suspended"){
                $data = [
                    'name' => $vendor['f_name'],
                    'status' => 'suspended',
                    'subject' => translate('Account_Suspended'),
                    'title' => translate('Your_Account_Has_Been_Suspended').'!',
                    'message' => translate('your_account_access_has_been_suspended_by_admin').'.'.translate('from_now_you_can_access_your_app_and_panel_again').' '.translate('please_contact_us_for_any_queries,_we’re_always_happy_to_help').'.',
                ];
            }else{
                $data = [
                    'name' => $vendor['f_name'],
                    'status' => 'approved',
                    'subject' => translate('Account_Activate'),
                    'title' => translate('you’ve_got_access_to_your_account_again').'!',
                    'message' => translate('your_account_suspension_has_been_revoked_by_admin').'.'.translate('from_now_you_can_access_your_app_and_panel_again').' '.translate('please_contact_us_for_any_queries,_we’re_always_happy_to_help').'.',
                ];
            }
        }
        event(new VendorRegistrationMailEvent($vendor['email'],$data));
        return back();
    }

    public function exportList(Request $request): BinaryFileResponse
    {
        $sellers = $this->vendorRepo->getListWhere(
            orderBy:['id'=>'desc'],
            searchValue: $request['searchValue'],
            relations: ['orders', 'product'],
            dataLimit: 'all'
        );

        $active = $sellers->where('status','approved')->count();
        $inactive = $sellers->where('status','!=','approved')->count();
        $data = [
            'sellers' => $sellers,
            'search' => $request['searchValue'],
            'active' =>$active,
            'inactive' => $inactive,
        ];
        return Excel::download(new SellerListExport($data),VendorExport::EXPORT_XLSX);
    }

    public function getOrderListView(Request $request, $seller_id): View
    {
        $orders = $this->orderRepo->getListWhere(
            orderBy: ['id'=>'desc'],
            searchValue: $request['searchValue'],
            filters: ['seller_id'=> $seller_id, 'seller_is'=> 'seller'],
            dataLimit:getWebConfig(name: WebConfigKey::PAGINATION_LIMIT),
        );
        $seller = $this->vendorRepo->getFirstWhere(params: ['id'=>$seller_id]);
        return view(Vendor::ORDER_LIST[VIEW], compact('orders', 'seller'));
    }

    public function getProductListView(Request $request, $seller_id): View
    {
        $filters = ['seller_id' => $seller_id, 'added_by' => 'seller'];
        $products = $this->productRepo->getListWhere(
            orderBy: ['id' => 'desc'],
            searchValue: $request['searchValue'],
            filters: $filters,
            relations: ['translations'],
            dataLimit: getWebConfig(name: WebConfigKey::PAGINATION_LIMIT)
        );
        $seller = $this->vendorRepo->getFirstWhere(params: ['id'=>$seller_id]);
        return view(Vendor::PRODUCT_LIST[VIEW], compact('products', 'seller'));
    }

    public function updateSalesCommission(Request $request, $id): RedirectResponse
    {
        if ($request['status'] == 1 && $request['commission'] == null) {
            Toastr::error(translate('you_did_not_set_commission_percentage_field'));
            return back();
        }
        $this->vendorRepo->update(id: $id, data: ['sales_commission_percentage' => $request['commission_status'] == 1 ? $request['commission'] : null]);
        Toastr::success(translate('Commission_percentage_for_this_seller_has_been_updated'));
        return back();
    }

    public function getOrderDetailsView($order_id, $seller_id): View
    {
        $country_restrict_status = getWebConfig(name: 'delivery_country_restriction');
        $zip_restrict_status = getWebConfig(name: 'delivery_zip_code_area_restriction');

        $countries = $country_restrict_status ? $this->get_delivery_country_array() : COUNTRIES;
        $zip_codes = $zip_restrict_status ? $this->deliveryZipCodeRepo->getListWhere(dataLimit: 'all') : 0;

        $order = $this->orderRepo->getFirstWhere(
            params: ['id'=> $order_id],
            relations: ['shipping','customer'],
        );

        $physical_product = false;
        foreach($order->details as $product){
            if(isset($product->product) && $product->product->product_type == 'physical'){
                $physical_product = true;
            }
        }

        $shipping_method = getWebConfig(name: 'shipping_method');

        $delivery_men = $this->deliveryManRepo->getListWhereIn(
            filters: ['is_active'=>1, 'order_seller' => $order['seller_is'], 'seller_id' => $order['seller_id'], 'shipping_method' => $shipping_method],
            dataLimit: 'all',
        );

        $shipping_address = $this->shippingAddressRepo->getFirstWhere(params: ['id'=>$order['shipping_address']]);
        $total_delivered = $this->orderRepo->getListWhere(
            filters: ['seller_id' => $order['seller_id'], 'seller_is'=> 'seller', 'order_status' => 'delivered', 'order_type' => 'default_type'],
            dataLimit: 'all',
        )->count();

        $linked_orders = $this->orderRepo->getListWhereNotIn(
            filters: ['order_group_id' => $order['order_group_id']],
            whereNotIn: ['order_group_id' => ['def-order-group'],'id' => [$order['id']]],
            dataLimit: 'all',
        );
        if ($order['order_type'] == 'default_type') {
            $orderCount = $this->orderRepo->getListWhereCount(filters: ['customer_id' => $order['customer_id']]);
        } else {
            $orderCount = $this->orderRepo->getListWhereCount(filters: ['customer_id' => $order['customer_id'], 'order_type' => 'POS']);
        }
        return view(Vendor::ORDER_DETAILS[VIEW], compact('order', 'seller_id','delivery_men', 'linked_orders','physical_product',
            'shipping_address','total_delivered', 'countries','zip_codes','zip_restrict_status','country_restrict_status','orderCount'));
    }

    public function getView(Request $request, $id, $tab = null): View|RedirectResponse
    {

        $seller = $this->vendorRepo->getFirstWhere(
            params: ['id'=>$id, 'withCount' => ['orders', 'product']],
            relations: ['orders', 'product']
        );
        if(!$seller){
          return redirect()->route('admin.sellers.seller-list');
        }
        $seller?->product?->map(function($product){
            $product['rating'] = $product?->reviews->pluck('rating')->sum();
            $product['rating_count'] = $product->reviews->count();
            $product['single_rating_5'] = 0;
            $product['single_rating_4'] = 0;
            $product['single_rating_3'] = 0;
            $product['single_rating_2'] = 0;
            $product['single_rating_1'] = 0;
            foreach($product->reviews as $review) {
                $rating = $review->rating;
                if($rating>0){
                    match ($rating) {
                        5 => $product->single_rating_5++,
                        4 => $product->single_rating_4++,
                        3 => $product->single_rating_3++,
                        2 => $product->single_rating_2++,
                        1 => $product->single_rating_1++,
                    };
                }
            }
        });
        $seller['single_rating_5'] = $seller?->product->pluck('single_rating_5')->sum();
        $seller['single_rating_4'] = $seller?->product->pluck('single_rating_4')->sum();
        $seller['single_rating_3'] = $seller?->product->pluck('single_rating_3')->sum();
        $seller['single_rating_2'] = $seller?->product->pluck('single_rating_2')->sum();
        $seller['single_rating_1'] = $seller?->product->pluck('single_rating_1')->sum();
        $seller['total_rating'] = $seller?->product->pluck('rating')->sum();
        $seller['rating_count'] = $seller->product->pluck('rating_count')->sum();
        $seller['average_rating'] = $seller['total_rating'] / ($seller['rating_count'] == 0 ? 1 : $seller['rating_count']);

        if(!isset($seller)){
            Toastr::error(translate('vendor_not_found_It_may_be_deleted'));
            return back();
        }

        if ($tab == 'order') {
            return $this->getOrderListTabView(request:$request, seller:$seller);
        } else if ($tab == 'product') {
            return $this->getProductListTabView(request:$request, seller:$seller);
        } else if ($tab == 'setting') {
            return $this->getSettingListTabView(request:$request, seller:$seller, id:$id);
        } else if ($tab == 'transaction') {
            return $this->getTransactionListTabView(request:$request, seller:$seller);
        } else if ($tab == 'review') {
            return $this->getReviewListTabView(request:$request, seller:$seller);
        }

        return view(Vendor::VIEW[VIEW], [
            'seller' => $seller,
            'current_date' => date('Y-m-d'),
        ]);
    }

    public function getOrderListTabView(Request $request, $seller): View
    {
        $orders = $this->orderRepo->getListWhere(
            orderBy: ['id'=>'desc'],
            searchValue: $request['searchValue'],
            filters: ['seller_id'=> $seller['id'], 'seller_is'=> 'seller', 'order_type'=>'default_type'],
            dataLimit:getWebConfig(name: WebConfigKey::PAGINATION_LIMIT),
        );
        $pendingOrder = $this->orderRepo->getListWhere(
            orderBy: ['id'=>'desc'],
            searchValue: $request['searchValue'],
            filters: ['seller_id'=> $seller['id'], 'seller_is'=> 'seller', 'order_type'=>'default_type','order_status'=>'pending'],
            dataLimit: 'all',
        )->count();
        $deliveredOrder = $this->orderRepo->getListWhere(
            orderBy: ['id'=>'desc'],
            searchValue: $request['searchValue'],
            filters: ['seller_id'=> $seller['id'], 'seller_is'=> 'seller', 'order_type'=>'default_type','order_status'=>'delivered'],
            dataLimit: 'all',
        )->count();

        return view(Vendor::VIEW_ORDER[VIEW], compact('seller', 'orders','pendingOrder','deliveredOrder'));
    }

    public function getProductListTabView(Request $request, $seller): View
    {
        $products = $this->productRepo->getListWhere(
            orderBy: ['id' => 'desc'],
            searchValue: $request['searchValue'],
            filters: ['seller_id' => $seller['id'], 'added_by' => 'seller'],
            relations: ['translations'],
            dataLimit: getWebConfig(name: WebConfigKey::PAGINATION_LIMIT)
        );
        return view(Vendor::VIEW_PRODUCT[VIEW], compact('seller', 'products'));
    }

    public function getSettingListTabView(Request $request, $seller, $id): View
    {
        return view(Vendor::VIEW_SETTING[VIEW], compact('seller'));
    }
    public function updateSetting(Request $request ,$id):RedirectResponse
    {
        if ($request->has('commission')) {
            request()->validate([
                'commission' => 'required|numeric|min:1',
            ]);
            if ($request['commission_status'] == 1 && $request['commission'] == null) {
                Toastr::error(translate('you_did_not_set_commission_percentage_field.'));
            } else {
                $this->vendorRepo->update(id: $id, data: ['sales_commission_percentage' => $request['commission_status'] == 1 ? $request['commission'] : null]);
                Toastr::success(translate('commission_percentage_for_this_seller_has_been_updated.'));
            }
        }
        if ($request->has('gst')) {
            if ($request['gst_status'] == 1 && $request['gst'] == null) {
                Toastr::error(translate('you_did_not_set_GST_number_field.'));
            } else {
                $this->vendorRepo->update(id: $id, data: ['gst' => $request['gst_status'] == 1 ? $request['gst'] : null]);
                Toastr::success(translate('GST_number_for_this_seller_has_been_updated.'));
            }
        }
        if ($request->has('seller_pos_update')) {
            $this->vendorRepo->update(id: $id, data: ['pos_status' => $request->get('seller_pos', 0)]);
            Toastr::success(translate('vendor_pos_permission_updated'));
        }
        return redirect()->back();
    }

    public function getTransactionListTabView(Request $request, $seller): View
    {
        $filters = [
            'seller_is'=>'seller',
            'seller_id'=>$seller['id'],
            'status' => $request['status'] ?? 'all'

        ];
        $transactions = $this->orderTransactionRepo->getListWhere(
            orderBy:['id'=>'desc'],
            searchValue: $request['searchValue'],
            filters: $filters,
            relations: ['order.customer'],
            dataLimit:getWebConfig(name: WebConfigKey::PAGINATION_LIMIT),
        );
        return view(Vendor::VIEW_TRANSACTION[VIEW], compact('seller', 'transactions'));
    }

    public function getReviewListTabView(Request $request, $seller): View
    {
        if ($request->has('searchValue')) {
            $product_id = $this->productRepo->getListWhere(
                searchValue: $request['searchValue'],
                filters: ['added_by'=>'seller', 'seller_id'=>$seller['id']],
                dataLimit: 'all')->pluck('id')->toArray();
            $filtersBy = [
                'product_id' => $product_id,
            ];
            $reviews = $this->reviewRepo->getListWhereIn(
                orderBy:['id'=>'desc'],
                filters: ['added_by' => 'seller'],
                whereInFilters: $filtersBy,
                relations: ['product'],
                nullFields: ['delivery_man_id'],
                dataLimit: getWebConfig(name: 'pagination_limit'));
        } else {
            $reviews = $this->reviewRepo->getListWhereIn(
                orderBy:['id'=>'desc'],
                filters: ['product_user_id' => $seller['id']],
                relations: ['product', 'customer'],
                dataLimit:getWebConfig(name: 'pagination_limit'));
        }
        return view(Vendor::VIEW_REVIEW[VIEW], [
            'seller' => $seller,
            'reviews' => $reviews,
        ]);
    }

    public function getWithdrawView($withdraw_id, $seller_id): View|RedirectResponse
    {
        $withdrawRequest = $this->withdrawRequestRepo->getFirstWhere(params: ['id' => $withdraw_id], relations: ['seller']);
        if ($withdrawRequest) {
            $withdrawalMethod = is_array($withdrawRequest['withdrawal_method_fields']) ? $withdrawRequest['withdrawal_method_fields'] : json_decode($withdrawRequest['withdrawal_method_fields']);
            $direction = session('direction');
            return view(Vendor::WITHDRAW_VIEW[VIEW], compact('withdrawRequest', 'withdrawalMethod','direction'));
        }
        Toastr::error(translate('withdraw_request_not_found'));
        return back();
    }

    public function getWithdrawListView(Request $request): View
    {
        $withdrawRequests = $this->withdrawRequestRepo->getListWhereNull(
            orderBy:['id'=>'desc'],
            filters: ['approved' => $request['approved']],
            nullFilters: ['delivery_man_id'],
            relations: ['seller'],
            dataLimit:getWebConfig(name: 'pagination_limit')
        );
        return view(Vendor::WITHDRAW_LIST[VIEW], compact('withdrawRequests'));
    }

    public function exportWithdrawList(Request $request): BinaryFileResponse
    {
        $withdrawRequests = $this->withdrawRequestRepo->getListWhereNull(
            orderBy:['id'=>'desc'],
            filters: ['approved' => $request['approved']],
            nullFilters: ['delivery_man_id'],
            relations: ['seller'],
            dataLimit: 'all'
        );

        $withdrawRequests->map(function ($query) {
            $query->shop_name = isset($query->seller) ? $query->seller->shop->name : '';
            $query->shop_phone = isset($query->seller) ? $query->seller->shop->contact : '';
            $query->shop_address = isset($query->seller) ? $query->seller->shop->address : '';
            $query->shop_email = isset($query->seller) ? $query->seller->email : '';
            $query->withdrawal_amount = setCurrencySymbol(amount: usdToDefaultCurrency(amount: $query->amount), currencyCode: getCurrencyCode(type: 'default'));
            $query->status = $query->approved == 0 ? 'Pending' : ($query->approved == 1 ? 'Approved':'Denied');
            $query->note = $query->transaction_note;
            $query->withdraw_method_name = isset($query->withdraw_method) ? $query->withdraw_method->method_name : '';
            if(!empty($query->withdrawal_method_fields)){
                foreach (json_decode($query->withdrawal_method_fields) as $key=>$field) {
                    $query[$key] = $field;
                }
            }
        });

        $pending = $withdrawRequests->where('approved', 0)->count();
        $approved = $withdrawRequests->where('approved', 1)->count();
        $denied = $withdrawRequests->where('approved', 2)->count();

        return Excel::download(new SellerWithdrawRequest([
                    'withdraw_request'=>$withdrawRequests,
                    'filter' => session('withdraw_status_filter'),
                    'pending'=> $pending,
                    'approved'=> $approved,
                    'denied'=> $denied,
                ]), 'Seller-Withdraw-Request.xlsx'
        );
    }


    public function withdrawStatus(Request $request, $id): RedirectResponse
    {
        $withdrawData = [
            'approved' => $request['approved'],
            'transaction_note' => $request['note'],
        ];

        $withdraw = $this->withdrawRequestRepo->getFirstWhere(params: ['id'=>$id], relations: ['seller']);
        if(isset($withdraw->seller->cm_firebase_token) && $withdraw->seller->cm_firebase_token) {
            event(new WithdrawStatusUpdateEvent(key: 'withdraw_request_status_message', type: 'seller', lang: $withdraw->deliveryMan?->app_language ?? getDefaultLanguage(), status: $request['approved'], fcmToken: $withdraw->seller?->cm_firebase_token));
        }

        if ($request['approved'] == 1) {
            $this->vendorWalletRepo->getFirstWhere(params: ['seller_id'=>$withdraw['seller_id']])->increment('withdrawn', $withdraw['amount']);
            $this->vendorWalletRepo->getFirstWhere(params: ['seller_id'=>$withdraw['seller_id']])->decrement('pending_withdraw', $withdraw['amount']);

            $this->withdrawRequestRepo->update(id: $id, data: $withdrawData);
            Toastr::success(translate('Vendor_Payment_has_been_approved_successfully'));
            return redirect()->route('admin.sellers.withdraw_list');
        }

        $this->vendorWalletRepo->getFirstWhere(params: ['seller_id'=>$withdraw['seller_id']])->increment('total_earning', $withdraw['amount']);
        $this->vendorWalletRepo->getFirstWhere(params: ['seller_id'=>$withdraw['seller_id']])->decrement('pending_withdraw', $withdraw['amount']);
        $this->withdrawRequestRepo->update(id: $id, data: $withdrawData);

        Toastr::info(translate('Vendor_Payment_request_has_been_Denied_successfully'));
        return redirect()->route('admin.sellers.withdraw_list');

    }




}