<?php

namespace App\Http\Controllers;

use App\Classes\BillgenerateUpdate;
use App\Classes\MikrotikService\Mikrotik;
use App\Jobs\BatchPackageChangeJob;
use App\Models\Pop;
use App\Models\Client;
use App\Models\Balance;
use App\Models\BillGenerate;
use App\Models\Packages;
use App\Models\SubPackage;
use Illuminate\Http\Request;
use App\Services\ExpireCheck;
use App\Models\CustomerAccount;
use App\Models\CustomerPackageChangeInfo;
use App\Services\ClientServices;
use App\Services\PriceCalculate;
use Illuminate\Support\Facades\DB;
use App\Services\CommissionService;
use Illuminate\Support\Facades\Auth;
use App\Models\ReselleBalanceLogReport;
use App\Models\SubResellerBalanceLogReport;
use App\Services\CustomerAccountService;
use Brian2694\Toastr\Facades\Toastr;
use App\Classes\MikrotikService\SyncWithMk;
use App\Classes\Notification;
use App\Jobs\BatchPackChangeJob;
use App\Jobs\BillGenerateUpdateJob;
use App\Models\ClientEditLog;
use Exception;
use Faker\Core\Uuid;
use Illuminate\Support\Facades\Log;
use Session;
use Illuminate\Support\Str;
use Tymon\JWTAuth\Http\Middleware\Check;

class PackageChangeController extends Controller
{

    protected $billingCycle = [];

    protected $packageChangeIncomeHead = 4;


    public function packageChange()
    {
        $confday = config('app.billing_cycle');

        return view('clients.packageChange', [
            'page_title' => 'Package Change',
            'confday' => $confday
        ]);
    }


    public function batchUserPackageChange(Request $request)
    {
        $rawIds = [];

        // 1. new blade sends "client_ids=1,2,3" in a hidden input
        if ($request->filled('client_ids')) {
            $rawIds = array_merge($rawIds, explode(',', $request->client_ids));
        }

        // 2. old blade (or JS back-up) still sends id[] array
        if ($request->has('id')) {
            $rawIds = array_merge($rawIds, $request->id);
        }

        // sanitise / de-duplicate
        $inputIds = array_filter($rawIds, function ($id) {
            return !empty($id) && is_numeric($id) && $id > 0;
        });
        $uniqueIds = array_values(array_unique(array_map('intval', $inputIds)));

        if (empty($uniqueIds)) {
            Toastr::error('No valid client IDs provided for package change', 'error');
            return redirect()->back();
        }

        $pack = $request->package_id;
        $auth_user = Auth::user()->id;

        // Path 1: Without cost calculation - simple job dispatch
        if ($request->change_pack_without_cost == 'yes') {
            Client::with('pop', 'customerAccount', 'pop.reseller', "pop.nas", 'packages', 'subpack')
                ->whereIn('id', $uniqueIds)
                ->chunk(100, function ($clients) use ($auth_user, $pack) {
                    BatchPackChangeJob::dispatch($clients, $auth_user, $pack);
                });

            Toastr::success('Package change queued for ' . count($uniqueIds) . ' clients', 'success');
            if (!CheckAPI()) {
                exec('/usr/bin/sudo /etc/init.d/freeradius restart');
            }
            return redirect()->back();
        }

        // Path 2: With cost calculation - complex processing
        // IMPROVEMENT: Consider moving this to a job as well for large batches
        // For now, keeping the synchronous approach but with improvements

        $users = Client::with('pop', 'customerAccount', 'pop.reseller', "pop.nas", 'packages', 'subpack')
            ->whereIn('id', $uniqueIds)
            ->get();

        $new_package_id = $request->package_id;
        $processedUserIds = [];
        $baseUniqueToken = $request->_token;

        // IMPROVEMENT: Track results for better feedback
        $results = [
            'success' => 0,
            'skipped' => 0,
            'failed' => 0,
            'errors' => []
        ];

        foreach ($users as $user) {
            // IMPROVEMENT: Check early to avoid unnecessary processing
            if (in_array($user->id, $processedUserIds)) {
                Toastr::warning('Client ID ' . $user->userid . ' already processed, skipping duplicate.', 'warning');
                $results['skipped']++;
                continue;
            }

            // Process the package change
            $result = $this->processSinglePackageChange($user, $new_package_id, $baseUniqueToken);

            // Track the result
            if ($result['status'] === 'success') {
                $results['success']++;
                Toastr::success('Package Change Successful for ' . $user->userid, 'success');
            } else {
                $results['failed']++;
                $results['errors'][] = "User {$user->userid}: {$result['message']}";
                Toastr::error($result['message'] . ' (User: ' . $user->userid . ')', 'error');
            }

            $processedUserIds[] = $user->id;
        }

        // IMPROVEMENT: Show summary of batch operation
        if ($results['success'] > 0) {
            Toastr::success("Successfully changed {$results['success']} package(s)", 'success');
        }
        if ($results['failed'] > 0) {
            Toastr::warning("Failed to change {$results['failed']} package(s)", 'warning');
        }

        if (!CheckAPI()) {
            exec('/usr/bin/sudo /etc/init.d/freeradius restart');
        }
        return redirect()->back();
    }

    /**
     * IMPROVEMENT: Extract single user package change logic to separate method
     * Makes testing easier and improves code organization
     */
    protected function processSinglePackageChange($user, $new_package_id, $baseUniqueToken)
    {
        try {
            // Store original package info before any changes
            $old_package_name = $user->packages->package_name ?? '';
            $old_package_rate = $user->packages->package_rate ?? 0;
            $old_sub_package_name = $user->subpack->name ?? "";
            $old_sub_package_rate = $user->subpack->rate ?? 0;

            // IMPROVEMENT: Ensure customer account exists before transaction
            if (!$user->customerAccount) {
                CustomerAccount::create([
                    'client_id'       => $user->id,
                    'totalPaid'       => 0,
                    'dueAmount'       => 0,
                    'totalDiscount'   => 0,
                    'totalBillAmount' => 0
                ]);
                $user->load('customerAccount');
            }

            $expire = ExpireCheck::expire_check_for_package_change(
                $user->pop,
                $new_package_id,
                $user->billing_cycle,
                $user->expire_date
            );

            // Determine old and new packages
            if ($user->package_id == $expire['reseller_package_id']) {
                $p = Packages::find($expire['reseller_package_id']);
                $R_package = ['old' => $p, 'new' => $p];
            } else {
                $R_package['old'] = Packages::find($user->package_id);
                $R_package['new'] = Packages::find($expire['reseller_package_id']);
            }

            // IMPROVEMENT: Pre-transaction validations
            $validation = $this->validatePackageChange($user, $expire, $R_package);
            if (!$validation['valid']) {
                return ['status' => 'error', 'message' => $validation['message']];
            }

            // Calculate cost
            $dt = today();
            $days_in_month = config('app.billing_day_count', false) ?: $dt->daysInMonth;
            $cost = max(0, (($R_package['new']['package_rate'] - $R_package['old']['package_rate']) / $days_in_month) * $expire['total_date']);

            // IMPROVEMENT: Single transaction wrapping all operations
            DB::beginTransaction();

            $uniqueString = $baseUniqueToken . '_' . $user->id;
            $reseller_id = $user->pop->reseller_id;

            // Determine which process to use based on customer balance
            if ($user->customerAccount->dueAmount < 0 && abs($user->customerAccount->dueAmount) >= $cost) {
                $status = $this->packageChangeAndCustomerBalanceUpdateForAdvancedBalance(
                    $user,
                    $expire,
                    $cost,
                    $R_package,
                    $reseller_id,
                    $uniqueString
                );
            } elseif ($user->customerAccount->dueAmount < 0 && abs($user->customerAccount->dueAmount) < $cost) {
                $status = $this->package_change_and_customer_balance_update_for_advance_balance_and_extra_amount(
                    $user,
                    $expire,
                    $cost,
                    $R_package,
                    $reseller_id,
                    $uniqueString
                );
            } else {
                $status = $this->resellerPackageChangeProcess(
                    $user,
                    $expire,
                    $R_package,
                    $reseller_id,
                    $cost,
                    $uniqueString
                );
            }

            // Handle the status result
            if ($status !== 'success') {
                DB::rollBack();

                $errorMessages = [
                    'reseller_balance_error' => 'Reseller Balance Required',
                    'sub_same_package' => 'Same Package Not changeable',
                    'same_package' => 'Same Package Not changeable',
                    'error' => 'Not Enough Balance or Something is wrong',
                    'balance_error' => 'Not Enough Balance or Something is wrong',
                ];

                return [
                    'status' => 'error',
                    'message' => $errorMessages[$status] ?? 'Unknown error: ' . $status
                ];
            }

            // Success - commit and do post-processing
            DB::commit();

            // Dispatch bill generation job
            BillGenerateUpdateJob::dispatch($user->id);

            // Handle bill generation and SMS
            if ($user->pop->bill_generate == "yes") {
                $session_name = "package_bill_auth" . auth()->user()->id;
                $check_billgenerate_in_session = session($session_name);

                if ($check_billgenerate_in_session) {
                    $bill_generate = BillGenerate::find($check_billgenerate_in_session);
                    if ($bill_generate && $bill_generate->billing_type == "package_change") {
                        try {
                            sendSmsAfterBillgenerate($bill_generate->id);
                        } catch (Exception $e) {
                        }
                    }
                    session()->forget($session_name);
                }
            }

            // Log package change info if cost was involved
            if ($cost > 0) {
                $after_package_change_client = Client::with('packages', 'subpack')->find($user->id);

                CustomerPackageChangeInfo::create([
                    'client_id'                      => $user->id,
                    'previous_package_name'          => $old_package_name,
                    'previous_package_price'         => $old_package_rate,
                    'new_package_name'               => $after_package_change_client->packages->package_name,
                    'new_package_price'              => $after_package_change_client->packages->package_rate,
                    'previous_sub_package_name'      => $old_sub_package_name,
                    'previous_sub_package_price'     => $old_sub_package_rate,
                    'new_sub_package_name'           => $after_package_change_client->subpack->name ?? "",
                    'new_sub_package_price'          => $after_package_change_client->subpack->rate ?? 0,
                ]);
            }

            return ['status' => 'success', 'message' => 'Package changed successfully'];
        } catch (\Throwable $th) {
            DB::rollBack();

            // IMPROVEMENT: Log the error for debugging
            Log::error('Package change failed', [
                'user_id' => $user->id,
                'package_id' => $new_package_id,
                'error' => $th->getMessage(),
                'trace' => $th->getTraceAsString()
            ]);

            return ['status' => 'error', 'message' => $th->getMessage()];
        }
    }

    /**
     * IMPROVEMENT: Extracted validation logic for clarity
     */
    protected function validatePackageChange($user, $expire, $R_package)
    {
        // Check if same package
        if ($user->pop->subreseller == 'yes') {
            if ($user->sub_package_id == $expire['sub_reseller_package_id']) {
                return ['valid' => false, 'message' => 'Same Package is Not Changeable'];
            }
        } else {
            if ($user->package_id == $expire['reseller_package_id']) {
                return ['valid' => false, 'message' => 'Same Package is Not Changeable'];
            }
        }

        // Check upgrade conditions
        if ($R_package['old']->package_rate <= $R_package['new']->package_rate) {
            // Skip these checks for subreseller without sub_package
            if (!($user->pop->subreseller == 'yes' && $user->sub_package_id == null)) {

                // Check client status
                if (
                    $user->client_status == 'expired' ||
                    $user->client_status == 'deactive' ||
                    today()->subDay() > $user->expire_date
                ) {
                    return ['valid' => false, 'message' => 'Customer Expired or Deactivated'];
                }

                // Check due amount for own reseller
                if (
                    $user->pop->reseller->reseller_type == 'own' &&
                    $user->customerAccount &&
                    $user->customerAccount->dueAmount > 0
                ) {
                    return ['valid' => false, 'message' => 'Customer Has due amount!! Please Clear Previous Due.'];
                }
            }
        }

        return ['valid' => true];
    }



    public function userPackageChange(Request $request)
    {

        // dd($request->all());
        if (!empty($request->id) && !empty($request->package_id)) {

            $user = Client::with('pop', 'customerAccount', 'pop.reseller', "pop.nas", 'packages', 'subpack')->find($request->id);

            // dd($user->customerAccount);
            $old_package_name = $user->packages->package_name;
            $old_package_rate = $user->packages->package_rate;

            $old_sub_package_name = $user->subpack->name ?? "";
            $old_sub_package_rate = $user->subpack->rate ?? 0;

            if (!$user->customerAccount) {
                CustomerAccount::create([
                    'client_id'       => $user->id,
                    'totalPaid'       => 0,
                    'dueAmount'       => 0,
                    'totalDiscount'   => 0,
                    'totalBillAmount' => 0
                ]);

                $user = Client::with('pop', 'customerAccount', 'pop.reseller', "pop.nas")->find($request->id);
            }




            $expire = ExpireCheck::expire_check_for_package_change($user->pop, $request->package_id, $user->billing_cycle, $user->expire_date);

            if ($user->package_id == $expire['reseller_package_id']) {

                $p = Packages::find($expire['reseller_package_id']);
                $R_package['old'] = $p;
                $R_package['new'] = $p;
            } else {
                $R_package['old'] = Packages::find($user->package_id);
                $R_package['new'] = Packages::find($expire['reseller_package_id']);
            }


            if ($R_package['old']->package_rate <= $R_package['new']->package_rate) {

                if ($user->pop->subreseller == 'yes' && $user->sub_package_id == null) {
                } else {
                    if ($user->client_status == 'expired' || $user->client_status == 'deactive' || today()->subDay() > $user->expire_date) {

                        return redirect()->back()->with('error_message', 'Customer Expired or Deactivated');
                    }
                }
            }


            if ($user->pop->reseller->reseller_type == 'own' && $user->customerAccount->dueAmount > 0  && ($R_package['old']->package_rate <= $R_package['new']->package_rate)) {


                if ($user->pop->subreseller == 'yes' && $user->sub_package_id == null) {
                } else {

                    Toastr::error('Customer Has due amount!! Please Clear Previous Due.', 'error');
                    return redirect()->back();
                }
            }




            $dt = today();

            if (config('app.billing_day_count') == false) {
                $days_in_month = $dt->daysInMonth;
            } else {
                $days_in_month = config('app.billing_day_count');
            }

            if (globalPermission('check_client_balance_before_package_billing_date_change') && $user->pop->reseller->reseller_type == 'own') {
                // Get customer's advance balance (credit)
                $advanceBalance = $user->customerAccount->dueAmount <= 0
                    ? abs($user->customerAccount->dueAmount)
                    : 0;

                // Validate required data
                if (empty($days_in_month) || $days_in_month <= 0) {
                    Toastr::error('Invalid billing period.', 'error');
                    return redirect()->back();
                }

                // Calculate prorated cost for package change
                $rateDifference = $R_package['new']['package_rate'] - $R_package['old']['package_rate'];
                $packageChangeCost = max(0, ($rateDifference / $days_in_month) * $expire['total_date']);

                $packageChangeCost = number_format($packageChangeCost);

                // Check if customer has sufficient balance
                if ($advanceBalance < $packageChangeCost) {
                    $shortfall = $packageChangeCost - $advanceBalance;
                    Toastr::error(
                        "Insufficient balance client. Required: {$packageChangeCost}, Available: {$advanceBalance}, Shortfall: {$shortfall}",
                        'error'
                    );
                    return redirect()->back();
                }
            }




            DB::beginTransaction();


            try {

                $uniqueString = $request->uuid;
                $reseller_id = $user->pop->reseller_id;

                $cost =  (($R_package['new']['package_rate'] - $R_package['old']['package_rate']) / $days_in_month) * $expire['total_date'];
                $cost = $cost < 0 ? 0 : $cost;


                if ($user->pop->subreseller == 'yes') {

                    if ($user->sub_package_id == $expire['sub_reseller_package_id']) {
                        return redirect()->back()->with('error_message', 'Same Package is Not Changable');
                    }
                } else {
                    if ($user->package_id == $expire['reseller_package_id']) {
                        return redirect()->back()->with('error_message', 'Same Package is Not Changable');
                    }
                }

                if ($user->customerAccount) {

                    if ($user->customerAccount->dueAmount < 0 &&  abs($user->customerAccount->dueAmount) >= $cost) {

                        $status = $this->packageChangeAndCustomerBalanceUpdateForAdvancedBalance($user, $expire, $cost, $R_package, $reseller_id, $uniqueString);
                    } elseif ($user->customerAccount->dueAmount < 0 && abs($user->customerAccount->dueAmount) < $cost) {

                        $status = $this->package_change_and_customer_balance_update_for_advance_balance_and_extra_amount($user, $expire, $cost, $R_package, $reseller_id, $uniqueString);
                    } else {

                        $status = $this->resellerPackageChangeProcess($user, $expire, $R_package, $reseller_id, $cost, $uniqueString);
                    }
                } else {

                    $status = $this->resellerPackageChangeProcess($user, $expire, $R_package, $reseller_id, $cost, $uniqueString);
                }


                // dd('scope');

                // dd('did not check customer account');



                if ($status == 'reseller_balance_error') {

                    return redirect()->back()->with('error_message', '<span class="badge badge-danger">Reseller Balance Required</span>');
                } elseif ($status == 'sub_same_package' or $status == 'same_package') {

                    return redirect()->back()->with('error_message', '<span class="badge badge-danger">Same Package Not changable...</span>');
                } elseif ($status == 'error' or $status == 'balance_error') {
                    // dd($status);
                    Toastr::error('Not Enough Balance or Something is wrong...');
                    return redirect()->back();
                } elseif ($status == 'success') {

                    // dd('hi');

                    DB::table('radcheck')
                        ->whereUsername($user->userid)
                        ->update([
                            'op' => ':='
                        ]);


                    DB::commit();
                    BillGenerateUpdateJob::dispatch($user->id);
                    if ($user->pop->bill_generate == "yes") {
                        $session_name = "package_bill_auth" . auth()->user()->id;
                        $check_billgenerate_in_session = session($session_name);
                        // dd($check_billgenerate_in_session);
                        if ($check_billgenerate_in_session) {
                            $bill_generate = BillGenerate::find($check_billgenerate_in_session);
                            if ($bill_generate->billing_type == "package_change") {
                                sendSmsAfterBillgenerate($bill_generate->id);
                            }

                            Session::put($session_name, null);
                            // dd(session($session_name));

                        }
                    }
                    if ($cost > 0) {

                        $after_package_change_client = Client::with('packages', 'subpack')->find($request->id);
                        $customer_package_cahange_info = new CustomerPackageChangeInfo();
                        $customer_package_cahange_info->client_id               = $request->id;
                        $customer_package_cahange_info->previous_package_name  = $old_package_name;
                        $customer_package_cahange_info->previous_package_price = $old_package_rate;
                        $customer_package_cahange_info->new_package_name       = $after_package_change_client->packages->package_name;
                        $customer_package_cahange_info->new_package_price      = $after_package_change_client->packages->package_rate;

                        $customer_package_cahange_info->previous_sub_package_name   = $old_sub_package_name;
                        $customer_package_cahange_info->previous_sub_package_price  = $old_sub_package_rate;
                        $customer_package_cahange_info->new_sub_package_name        = $after_package_change_client->subpack->name ?? "";
                        $customer_package_cahange_info->new_sub_package_price       = $after_package_change_client->subpack->rate ?? 0;
                        $customer_package_cahange_info->save();
                    }

                    exec('/usr/bin/sudo /etc/init.d/freeradius restart');
                    (new Notification)->notify("..::[Customer Package Change]::.. \n Package Changed.\n Change By " . auth()->user()->name . " \nCustomer Username : " . $user->userid . " \nPassword : " . $user->password . "\n Change Time : " . now());
                    Toastr::success('Package Change Successfull', 'success');
                    return redirect()->back();

                    // return redirect()->back()->with('success_message', 'Package Changed Successfully');
                }
            } catch (\Throwable $th) {

                dd($th);
                DB::rollback();
                Toastr::error('Not Enough Balance or Something is wrong...');
                return redirect()->back();
            }
        } else {

            return redirect()->back();
        }
    }

    public function package_change_and_customer_balance_update_for_advance_balance_and_extra_amount($user, $expire, $cost, $R_package, $reseller_id, $uniqueString)
    {

        $dt = today();

        if (config('app.billing_day_count') == false) {
            $days_in_month = $dt->daysInMonth;
        } else {
            $days_in_month = config('app.billing_day_count');
        }


        $user_advanced_balance = $user->customerAccount->dueAmount < 0 ? abs($user->customerAccount->dueAmount) : 0;

        $reseller_adjustable_amount = abs($cost == 0 ? 0 : $user_advanced_balance - $cost);


        if ($reseller_adjustable_amount > floatval(Balance::getBalance('reseller', $reseller_id))) { //reseller balance checking
            return 'reseller_balance_error';
        }

        $action = "Package Changed. From Package: " . $R_package['old']['package_name'] .
            " To : " . $R_package['new']['package_name'] .
            " . Expire: " . $expire['exp_date'] .
            " For " . $expire['total_date'] .
            " days cost BDT " . number_format((float) $cost, 2, '.', '') .
            ". Cusomer advanced balance: " . number_format((float) abs($user->customerAccount->dueAmount), 2, '.', '') .
            ".Reseller Deductable Amount: " . number_format((float) $reseller_adjustable_amount, 2, '.', '');




        $resellerBalanceLogReportId = $this->reseller_log_and_balance_update($reseller_id, $user, $action, $reseller_adjustable_amount, $uniqueString);
        $this->doPackageChangeReseller($user, $expire);


        $bill_for_package_change = (new CustomerAccountService)->customer_account_update($user, $cost, 'Customer Package Change', 'package_change', $reseller_adjustable_amount, $this->packageChangeIncomeHead);
        if (isset($bill_for_package_change->id)) {
            (new CommissionService())->reseller_commission($reseller_adjustable_amount, $action, $reseller_id, $R_package['new']->commission, $R_package['new']->package_rate, $user->id, "bill", $bill_for_package_change->id);
        } else {
            (new CommissionService())->reseller_commission($reseller_adjustable_amount, $action, $reseller_id, $R_package['new']->commission, $R_package['new']->package_rate, $user->id);
        }



        if ($user->pop->subreseller == 'yes') {

            if ($user->sub_package_id != null) {

                $SR_package = $this->get_sub_reseller_package_info($expire, $user);

                $cost = (($SR_package['new']['rate'] - $SR_package['old']['rate']) / $days_in_month) * $expire['total_date'];
                $cost = $cost < 0 ? 0 : $cost;

                $sub_reseller_adjustable_balance = abs($cost == 0 ? 0 : $user_advanced_balance - $cost);


                if ($sub_reseller_adjustable_balance > floatval(Balance::getBalance('pop', $user->pop->id))) { //pop balance checking
                    return 'balance_error';
                }


                $SubResellerAction = "Package Changed. From Package: " . $SR_package['old']['name'] .
                    " To : " . $SR_package['new']['name'] .
                    " . Expire: " . $expire['exp_date'] .
                    " For " . $expire['total_date'] .
                    " days cost BDT " . number_format((float) $cost, 2, '.', '') .
                    ". Cusomer advanced balance: " . number_format((float) abs($user->customerAccount->dueAmount), 2, '.', '') .
                    ".Sub Reseller Deductable Amount: " . number_format((float) $sub_reseller_adjustable_balance, 2, '.', '');

                (new CommissionService())->subreseller_commission($sub_reseller_adjustable_balance, $SubResellerAction, $user->pop->id, $SR_package['new']->commission, $SR_package['new']->rate, $user->id);

                $this->sub_reseller_log_and_balance_update($expire, $reseller_id, $user, $SubResellerAction, $sub_reseller_adjustable_balance, 0, $resellerBalanceLogReportId);
            } else {
                Client::where('id', $user->id)->update(['sub_package_id' => $expire['sub_reseller_package_id']]);
            }
        }

        return 'success';
    }




    public function packageChangeAndCustomerBalanceUpdateForAdvancedBalance($user, $expire, $cost, $R_package, $reseller_id, $uniqueString)
    {
        $dt = today();

        $action = "Package Changed. From Package: " . $R_package['old']['package_name'] .
            " To : " . $R_package['new']['package_name'] .
            " . Expire: " . $expire['exp_date'] .
            " For " . $expire['total_date'] .
            " days cost BDT " . number_format((float) $cost, 2, '.', '') .
            ". Cusomer has advanced balance so not deducted.";

        $resellerBalanceBalanceLogReportId = $this->reseller_log_and_balance_update($reseller_id, $user, $action, 0, $uniqueString);
        $this->doPackageChangeReseller($user, $expire);



        (new CustomerAccountService)->customer_account_update($user, round($cost), 'Customer Package Change', 'package_change', 0, $this->packageChangeIncomeHead);




        if ($user->pop->subreseller == 'yes') {

            $SR_package = $this->get_sub_reseller_package_info($expire, $user);

            $cost = (($SR_package['new']['rate'] - $SR_package['old']['rate']) / $dt->daysInMonth) * $expire['total_date'];
            $cost = $cost < 0 ? 0 : $cost;



            $SubResellerAction = "Package Changed. From Package: " . $SR_package['old']['name'] .
                " To : " . $SR_package['new']['name'] .
                " . Expire: " . $expire['exp_date'] .
                " For " . $expire['total_date'] .
                " days cost BDT " . number_format((float) $cost, 2, '.', '') .
                ". Cusomer has advanced balance so no deduct.";

            $this->sub_reseller_log_and_balance_update($expire, $reseller_id, $user, $SubResellerAction, 0, 0, $resellerBalanceBalanceLogReportId);
        }

        return 'success';
    }

    public function sub_reseller_log_and_balance_update($expire, $reseller_id, $user, $SubResellerAction, $total_cost_sub_reseller, $totalResellerPackageChangeCost = 0, $resellerBalanceLogReportId = null)
    {

        Client::where('id', $user->id)->update(['sub_package_id' => $expire['sub_reseller_package_id']]);

        SubResellerBalanceLogReport::create(
            [
                'reseller_id'       => $reseller_id,
                'sub_reseller_id'   => $user->pop->id,
                'client_id'         => $user->id,
                'action'            => $SubResellerAction,
                'amount'            => $total_cost_sub_reseller,
                'remarks'           => 'Created BY ' . Auth::user()->email,
                'reseller_cost' => $totalResellerPackageChangeCost,
                'commission_amount' => getCommissionForSubResellerRecharge($user->id, $total_cost_sub_reseller,),
                'reseller_balance_log_id' => $resellerBalanceLogReportId
            ]
        );

        if ($total_cost_sub_reseller > 0) {
            Balance::balanceDeductForUserRecharge('pop', $user->pop->id, round($total_cost_sub_reseller));
        }
    }


    public function checkBillAndLogBalanceForPackageChange($user, $package_id, $pop, $uniqueString)
    {

        $dt = today();

        $expire = ExpireCheck::expire($pop, $package_id, $user->billing_cycle);

        $reseller_id = $pop->reseller_id;


        $this->resellerPackageChangeProcess($user, $expire, $dt, $reseller_id, $pop, $uniqueString);

        if ($pop->subreseller == 'yes') {
            $this->sub_reseller_package_change_process($user, $expire, $dt, $reseller_id, $pop);
        }
    }


    public function resellerPackageChangeProcess($user, $expire, $R_package, $reseller_id, $cost, $uniqueString)
    {
        $dt = today();

        if ($user->expire_date > today()) {

            $total_cost_reseller = $cost > 0 ? $cost : 0;

            $action = "Package Changed. From Package: " . $R_package['old']['package_name'] .
                " To : " . $R_package['new']['package_name'] .
                " . Expire: " . $expire['exp_date'] .
                " For " . $expire['total_date'] .
                " days cost BDT " . number_format((float) $total_cost_reseller, 2, '.', '');
        } else {

            $total_cost_reseller = 0;
            $total_date = 0;

            // dd($R_package);

            $action = "Package Changed. From Package: " . $R_package['old']['package_name'] .
                " To : " . $R_package['new']['package_name'] .
                " . Expire: " . $expire['exp_date'] .
                " For 0 days" .
                " cost BDT " . number_format((float) $total_cost_reseller, 2, '.', '');
        }



        $resellerBalance =  Balance::getBalance('reseller', $reseller_id);

        if ($resellerBalance >= $total_cost_reseller) {



            $this->doPackageChangeReseller($user, $expire);




            $resellerBalanceLogReportId = $this->reseller_log_and_balance_update($reseller_id, $user, $action, $total_cost_reseller, $uniqueString);


            if ($user->pop->subreseller == 'no') {

                if ($user->pop->bill_generate == 'yes') {

                    $package_change_bill = (new CustomerAccountService)->customer_account_update($user, $total_cost_reseller, 'Customer Package Change', 'package_change', $total_cost_reseller, $this->packageChangeIncomeHead);
                }
            } else {

                $status = $this->sub_reseller_package_change_process($user, $expire, $dt, $reseller_id, $total_cost_reseller, $resellerBalanceLogReportId);
            }


            if ($R_package['new']->commission > 0 || globalPermission('commission_from_manager_pop')) {
                if (isset($package_change_bill->id)) {

                    (new CommissionService())->reseller_commission($total_cost_reseller, $action, $reseller_id, $R_package['new']->commission, $R_package['new']->package_rate, $user->id, 'bill', $package_change_bill->id);
                } else {
                    (new CommissionService())->reseller_commission($total_cost_reseller, $action, $reseller_id, $R_package['new']->commission, $R_package['new']->package_rate, $user->id);
                }
            }


            if (isset($status) && $status == 'balance_error') {

                return 'balance_error';
            } else {
                return 'success';
            }
        } else {
            return 'error';
        }
    }




    public function sub_reseller_package_change_process($user, $expire, $dt, $reseller_id, $totalResellerPackageChangeCost = 0, $resellerBalanceLogReportId = null)
    {

        $dt = today();

        if (config('app.billing_day_count') == false) {
            $days_in_month = $dt->daysInMonth;
        } else {
            $days_in_month = config('app.billing_day_count');
        }


        if ($user->sub_package_id != null) {

            $SR_package = $this->get_sub_reseller_package_info($expire, $user);


            if ($user->expire_date > today()) {
                // dd($SR_package);
                $cost = (($SR_package['new']['rate'] - $SR_package['old']['rate'] ?? 0) / $days_in_month) * $expire['total_date'];

                $total_cost_sub_reseller = $cost > 0 ? $cost : 0;

                // dd($total_cost_sub_reseller);


                $SubResellerAction = "Package Changed. From Package: " . $SR_package['old']['name'] .
                    " To : " . $SR_package['new']['name'] .
                    " . Expire: " . $expire['exp_date'] .
                    " For " . $expire['total_date'] .
                    " days cost BDT " . number_format((float) $total_cost_sub_reseller, 2, '.', '');
            } else {

                $total_date = 0;
                $total_cost_sub_reseller = 0;
                $SubResellerAction = "Package Changed. From Package: " . $SR_package['old']['name'] .
                    " To : " . $SR_package['new']['name'] .
                    " . Expire: " . $expire['exp_date'] .
                    " For 0 " .
                    " days cost BDT " . number_format((float) $total_cost_sub_reseller, 2, '.', '');
            }

            // dd($SubResellerAction);

            if ($SR_package['new']->commission > 0 || globalPermission('commission_from_manager_pop')) {

                (new CommissionService())->subreseller_commission($total_cost_sub_reseller, $SubResellerAction, $user->pop->id, $SR_package['new']->commission, $SR_package['new']->rate, $user->id);
            }

            $popBalance  = Balance::getBalance('pop', $user->pop->id);



            if ($total_cost_sub_reseller > $popBalance) {
                // return redirect()->route('packageChange')->with('error_message','Not Enough Balance');
                return 'balance_error';
            }

            Client::where('id', $user->id)->update(['sub_package_id' => $expire['sub_reseller_package_id']]);

            $this->sub_reseller_log_and_balance_update($expire, $reseller_id, $user, $SubResellerAction, round($total_cost_sub_reseller), $totalResellerPackageChangeCost, $resellerBalanceLogReportId);




            if ($user->pop->bill_generate == 'yes') {

                (new CustomerAccountService)->customer_account_update($user, $total_cost_sub_reseller, 'Customer Package Change', 'package_change', $total_cost_sub_reseller, $this->packageChangeIncomeHead);
            }
        } else {
            Client::where('id', $user->id)->update(['sub_package_id' => $expire['sub_reseller_package_id']]);
        }
    }



    public function doPackageChangeReseller($user, $expire)
    {

        if (checkAPI()) {
            $package = Packages::find($expire['reseller_package_id']);
            $profileName = $package->profile_name;
            $mkIp = $user->pop->nas->nasname;
            $mkUser = $user->pop->nas->mikrotick_user;
            $mkPass = $user->pop->nas->mikrotick_user_password;
            $mkPort = $user->pop->nas->mikrotick_port;

            $mk = new Mikrotik($mkIp, $mkUser, $mkPass, $mkPort ? (int)$mkPort : 8728);
            $alreadyExistUser = $mk->getUserOrNull($user->userid);
            if ($alreadyExistUser) {
                $mk->updateSecret($user->userid, [
                    "username" => $user->userid,
                    "password" => $user->password,
                    "service" => "pppoe",
                    "profile" => $profileName,
                    "remote-address" => $user->ip_address,
                    "caller-id" => $user->mac

                ]);

                try {
                    $mk->disconnectSecret($user->userid);
                } catch (Exception $e) {
                }
            } else {
                $mk->createSecret([
                    "username" => $user->userid,
                    "password" => $user->password,
                    "service" => "pppoe",
                    "profile" => $profileName,
                    "disabled" => $user->clients_status == "active" ? "no" : "yes",
                    "remote-address" => $user->ip_address,
                    "caller-id" => $user->mac

                ]);

                $othersPop = Pop::with("nas")->where('id', '!=', $user->id)->get();
                foreach ($othersPop as $pop) {
                    try {
                        $mkIp = $pop->nas->nasname;
                        $mkUser = $pop->nas->mikrotick_user;
                        $mkPass = $pop->nas->mikrotick_user_password;
                        $mkPort = $pop->nas->mikrotick_port;
                        $newMk = new Mikrotik($mkIp, $mkUser, $mkPass, $mkPort ? (int)$mkPort : 8728);
                        $newMk->deleteSecret($user->userid);
                    } catch (\Exception $err) {
                        continue;
                    }
                }
            }
        }
        Client::where('id', $user->id)
            ->update(['package_id' => $expire['reseller_package_id']]);

        DB::table('radusergroup')
            ->where('username', '=', $user->userid)
            ->update([
                'groupname' => $expire['reseller_package_id']
            ]);
    }

    public function reseller_log_and_balance_update($reseller_id, $user, $action, $total_cost_reseller, $uniqueString)
    {
        $pop = Pop::with('reseller')->find($user->pop_id);

        $resellerBalanceLogRepoer = ReselleBalanceLogReport::create([
            'reseller_id' => $reseller_id,
            'client_id'   => $user->id,
            'action'      => $action,
            'amount'      => $total_cost_reseller,
            'remarks'     => ' BY ' . Auth::user()->email,
            'uniqueId'    => $uniqueString . $user->id,
            'pop_id'       => $user->pop_id,
            'commission_amount' => getCommissionForResellerRecharge($user->id, $total_cost_reseller),
            'reseller_commission_percentage' => $pop->reseller->commission_percentage,
            'pop_commission_percentage' => $pop->commission_percentage,
        ]);

        if ($total_cost_reseller > 0) {
            Balance::balanceDeductForUserRecharge('reseller', $reseller_id, round($total_cost_reseller));
        }

        return $resellerBalanceLogRepoer->id;
    }



    public function get_sub_reseller_package_info($expire, $user)
    {

        if ($user->sub_package_id == $expire['sub_reseller_package_id']) {
            $SR_package['old'] = $SR_package['new'] = SubPackage::find($user->sub_package_id);
        } else {
            $SR_package['old'] = SubPackage::find($user->sub_package_id);
            $SR_package['new'] = SubPackage::find($expire['sub_reseller_package_id']);
        }
        return $SR_package;
    }

    public function package_change_user($user, $mother_package_id)
    {

        if (checkAPI()) {
            $package = Packages::find($mother_package_id);
            $profileName = $package->profile_name;
            $mkIp = $user->pop->nas->nasname;
            $mkUser = $user->pop->nas->mikrotick_user;
            $mkPass = $user->pop->nas->mikrotick_user_password;
            $mkPort = $user->pop->nas->mikrotick_port;

            $mk = new Mikrotik($mkIp, $mkUser, $mkPass, $mkPort ? (int)$mkPort : 8728);
            $alreadyExistUser = $mk->getUserOrNull($user->userid);
            if ($alreadyExistUser) {
                $mk->updateSecret($user->userid, [
                    "username" => $user->userid,
                    "password" => $user->password,
                    "service" => "pppoe",
                    "profile" => $profileName,
                    "remote-address" => $user->ip_address,
                    "caller-id" => $user->mac
                ]);

                try {
                    $mk->disconnectSecret($user->userid);
                } catch (Exception $e) {
                }
            } else {
                $mk->createSecret([
                    "username" => $user->userid,
                    "password" => $user->password,
                    "service" => "pppoe",
                    "profile" => $profileName,
                    "disabled" => $user->clients_status == "active" ? "no" : "yes",
                    "remote-address" => $user->ip_address,
                    "caller-id" => $user->mac
                ]);

                $othersPop = Pop::with("nas")->where('id', '!=', $user->id)->get();
                foreach ($othersPop as $pop) {
                    try {
                        $mkIp = $pop->nas->nasname;
                        $mkUser = $pop->nas->mikrotick_user;
                        $mkPass = $pop->nas->mikrotick_user_password;
                        $mkPort = $pop->nas->mikrotick_port;
                        $newMk = new Mikrotik($mkIp, $mkUser, $mkPass, $mkPort ? (int)$mkPort : 8728);
                        $newMk->deleteSecret($user->userid);
                    } catch (\Exception $err) {
                        continue;
                    }
                }
            }
        }
        Client::where('id', $user->id)
            ->update(['package_id' => $mother_package_id]);

        DB::table('radusergroup')
            ->where('username', '=', $user->userid)
            ->update([
                'groupname' => $mother_package_id
            ]);


        // dd($user->pop->nas);


    }

    public function getClientDetailsForPackageChange(Request $request)
    {

        $client = Client::with('pop.reseller', 'clientsinfo')->where('id', $request->client_id)->first();

        if ($client->pop->subreseller == 'yes') {
            $sub_pack_list = explode(',', $client->pop->sub_package_list);
            $packages = SubPackage::whereIn('id', $sub_pack_list)->get();
        } else {
            $packages_list = explode(',', $client->pop->reseller->package_list);
            $packages = Packages::whereIn('id', $packages_list)->get();
        }
        $data = [
            'client' => $client,
            'packages' => $packages
        ];
        return $data;
    }

    public function getPopWisePackList(Request $request)
    {
        if (request()->ajax() && !empty($request->id)) {
            $option = '<option value="all">All</option>';

            if ($request->id == 'all') {
                echo $option;
            } else {
                $pop = Pop::where('id', $request->id)->first();

                if ($pop->subreseller == 'yes') {

                    $package_list = $pop->sub_package_list;
                    $plist = explode(',', $package_list);
                    $pack = SubPackage::whereIn('id', $plist)->get();

                    foreach ($pack as $p) {
                        $option .= "<option value='" . $p->id . "'>" . $p->name . '-' . $p->rate . "</option>";
                    }
                } else {
                    $package_list = $pop->reseller->package_list;
                    $plist = explode(',', $package_list);
                    $pack = Packages::whereIn('id', $plist)->get();

                    foreach ($pack as $p) {
                        $option .= "<option value='" . $p->id . "'>" . $p->package_name . '-' . ($p->package_rate) . "</option>";
                    }
                }

                echo $option;
            }
        }
    }
    public function packageChangeWithoutCost(Request $request)
    {
        if (auth()->user()->can('package_change_without_cost')) {
            DB::beginTransaction();
            try {
                $old_client = Client::with('clientsinfo')->withTrashed()->find($request->id);
                $client = Client::with('packages', 'subpack')->find($request->id);
                $pop = Pop::find($client->pop_id);
                if ($pop->subreseller == 'yes') {
                    $package = SubPackage::find($request->package_id);
                    $client->sub_package_id = $package->id;
                    $client->package_id = $package->package_id;
                    $client->save();
                } else {
                    $client->package_id = $request->package_id;
                    $client->save();
                }


                $client = Client::with('packages', 'subpack')->find($request->id);

                $new_data_client = Client::with('clientsinfo')->withTrashed()->find($client->id);

                $client_update_log = new ClientEditLog();
                $client_update_log->client_id = $client->id;
                $client_update_log->user_id = Auth::user()->id;
                $client_update_log->old_data = json_encode($old_client);
                $client_update_log->new_data = json_encode($new_data_client);
                $client_update_log->save();


                DB::commit();
            } catch (\Exception $e) {
            }
        } else if (Client::find($request->id)->clients_status == 'expired' && auth()->user()->can('package_change_without_cost_expire')) {
            DB::beginTransaction();
            try {
                $old_client = Client::with('clientsinfo')->withTrashed()->find($request->id);
                $client = Client::with('packages', 'subpack')->find($request->id);
                $pop = Pop::find($client->pop_id);
                if ($pop->subreseller == 'yes') {
                    $package = SubPackage::find($request->package_id);
                    $client->sub_package_id = $package->id;
                    $client->package_id = $package->package_id;
                    $client->save();
                } else {
                    $client->package_id = $request->package_id;
                    $client->save();
                }


                $client = Client::with('packages', 'subpack')->find($request->id);

                $new_data_client = Client::with('clientsinfo')->withTrashed()->find($client->id);

                $client_update_log = new ClientEditLog();
                $client_update_log->client_id = $client->id;
                $client_update_log->user_id = Auth::user()->id;
                $client_update_log->old_data = json_encode($old_client);
                $client_update_log->new_data = json_encode($new_data_client);
                $client_update_log->save();

                DB::commit();
            } catch (\Exception $e) {
            }
        } else {
            Toastr::error('You do not have permission to change package without cost', 'Error');
            return redirect()->back();
        }

        if (checkAPI()) {
            try {
                $syncWithMk = new syncWithMk;
                $syncWithMk->syncSingleClient($client->id);
            } catch (\Exception $e) {
                Toastr::error('Failed to sync with Mikrotik: ' . $e->getMessage(), 'Error');
            }
        } else {
            DB::table('radusergroup')
                ->where('username', '=', $client->userid)
                ->update([
                    'groupname' => $request->package_id
                ]);
        }



        exec('/usr/bin/sudo /etc/init.d/freeradius restart');
        return redirect()->back();
    }
}
