<?php

namespace App\Http\Controllers\Billing;

use App\Classes\BillgenerateUpdate;
use App\Classes\EditLogHistory;
use App\Classes\MikrotikService\Mikrotik;
use App\Classes\MikrotikService\SyncWithMk;
use App\Classes\Notification;
use App\Http\Controllers\ClientController;
use App\Models\Client;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\Http\Controllers\Controller;
use App\Jobs\BatchExpChange;
use App\Jobs\BillGenerateUpdateJob;
use App\Models\BillGenerate;
use App\Models\CustomerBillingCycleInfo;
use App\Models\Pop;
use App\Models\Reseller;
use App\Services\BillingCycleChange;
use Brian2694\Toastr\Facades\Toastr;
use Brian2694\Toastr\Toastr as ToastrToastr;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;

class ClientBillingCycleChangeController extends Controller
{



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

        return view('clients.billCycleChange', [
            'page_title' => 'Bill Cycle Change',
            'confday' => $confday
        ]);
    }

    public function batchUserBillingCycleChange(Request $request)
    {
        // Handle comma-separated string from the new blade implementation
        $clientIds = $request->client_ids;

        // Convert comma-separated string to array
        if (is_string($clientIds)) {
            $inputIds = explode(',', $clientIds);
        } elseif (is_array($clientIds)) {
            // Fallback for old format if still in use
            $inputIds = $clientIds;
        } else {
            $inputIds = [];
        }

        // Prevent duplicate client IDs - filter, validate, and deduplicate
        $inputIds = array_filter($inputIds, function ($id) {
            return !empty($id) && is_numeric(trim($id)) && intval(trim($id)) > 0;
        });

        $uniqueIds = array_values(array_unique(array_map(function ($id) {
            return intval(trim($id));
        }, $inputIds)));

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

        // Log for debugging
        Log::info('Batch Billing Cycle Change - Processing ' . count($uniqueIds) . ' unique clients');

        if ($request->change_without_cost == 'yes') {
            $auth_user = Auth::user()->id;
            $exp_date = $request->expire_date;

            // Validate expire date
            if (empty($exp_date)) {
                Toastr::error('Please select an expire date', 'error');
                return redirect()->back();
            }

            // Use deduplicated IDs to prevent duplicate processing
            $users = Client::with('pop.nas', 'packages', 'subpack', 'pop.reseller', 'customerAccount', "clientsinfo")
                ->whereIn('id', $uniqueIds)
                ->chunk(100, function ($clients) use ($exp_date, $auth_user) {
                    BatchExpChange::dispatch($clients, $exp_date, $auth_user);
                });

            Toastr::success('Request is in processing Queue for ' . count($uniqueIds) . ' clients', 'success');
        } else {
            if (empty($request->billCycle)) {
                Toastr::error('Please Select a Billing Cycle Date', 'error');
                return redirect()->back();
            }

            // Use deduplicated IDs to prevent duplicate processing
            $users = Client::with('pop.nas', 'packages', 'subpack', 'pop.reseller', 'customerAccount', "clientsinfo")
                ->whereIn('id', $uniqueIds)
                ->get();

            // Track processed user IDs to prevent duplicate processing within the same batch
            $processedUserIds = [];
            $successCount = 0;
            $errorCount = 0;

            $baseUniqueId = $request->uuid ;

            foreach ($users as $user) {
                // Double-check: Skip if already processed (safety net)
                if (in_array($user->id, $processedUserIds)) {
                    Toastr::warning('Client ID ' . $user->id . ' was already processed and skipped', 'warning');
                    continue;
                }

                if (checkSettings('expire_billing_change_today_from_billing_date') != 'enable') {
                    if (now()->subMonth() > $user->expire_date) {
                        Toastr::error('Customer ' . $user->userid . ' Expired or Deactivated or expired one month older', 'error');
                        $errorCount++;
                        continue;
                    }
                }

                if ($request->billCycle == $user->billing_cycle) {
                    Toastr::error('Customer ' . $user->userid . ' already has this billing cycle', 'error');
                    $errorCount++;
                    continue;
                }

                $old_bill_cycle = $user->billing_cycle;
                $new_bill_cycle = $request->billCycle;

                // Generate unique ID per user to prevent duplicate entry errors
                // Include user ID, timestamp, and microtime to ensure uniqueness
                $uniqueId = $baseUniqueId . '_' . $user->id;
                // dd( $uniqueId);
                $status = $this->doBillingCycleChange($user, $old_bill_cycle, $new_bill_cycle, $uniqueId);
                // dd($status);
                if ($status['status'] == 'error') {
                    Toastr::error('user: ' . $user->userid . ' | ' . $status['message'], 'error');
                    $errorCount++;
                    continue;
                } elseif ($status['status'] == 'success') {
                    $successCount++;
                }

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

            // Summary message
            if ($successCount > 0) {
                Toastr::success('Successfully changed billing cycle for ' . $successCount . ' user(s)', 'success');
            }

            if ($errorCount > 0) {
                Toastr::warning('Failed to change ' . $errorCount . ' user(s)', 'warning');
            }
        }

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


    public function updateMikrotikUser($user)
    {
        if (checkAPI()) {
            $mk = new SyncWithMk();
            $mk->syncSingleClient($user->id);
        } else {

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


    public function doBillingCycleChange($user, $old_bill_cycle, $new_bill_cycle, $uniqueString)
    {
        DB::beginTransaction();

        try {

            $status = (new BillingCycleChange)->billingCycleChange($user->pop, $user, $new_bill_cycle, $uniqueString);


            if ($status == 'error' || $status == 'balance_error' || $status == 'Insufficient balance client') {

                if ($status == 'Insufficient balance client') {
                    return [
                        'status' => 'error',
                        'message' => 'Insufficient balance to client'
                    ];
                } else {
                    return [
                        'status' => 'error',
                        'message' => 'Not Enough Balance..'
                    ];
                }
            }




            $client = Client::find($user->id);

            $condition = false;
            if (globalPermission('expire_before_expire_date')) {
                $time = collect(json_decode(siteinfo()->settings))->where('type', 'expire_time')->first()->value ?? '00:00:00';
                $expireDate = Carbon::parse($client->expire_date)->toDateString();
                $expDateTime = Carbon::parse($expireDate . ' ' . $time);
                $condition = $expDateTime < now();
            } else {
                $condition = Carbon::parse($client->expire_date) < today();
            }

            if ($condition) {
                $client->clients_status = 'expired';
            } else {
                $client->clients_status = 'active';
            }
            $client->save();




            DB::commit();

            try {
                $this->updateMikrotikUser($user);
            } catch (\Throwable $th) {
            }

            try {
                BillGenerateUpdateJob::dispatch($user->id);

                $this->sendSmsAfterBillgenerate($user);
                $this->saveCustomerBillingCycleInfo($user, $old_bill_cycle, $new_bill_cycle);
            } catch (\Throwable $th) {
            }



            return [
                'status' => 'success',
                'message' => 'Bill Cycle Changed Successfully'
            ];
        } catch (\Throwable $th) {

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

    public function saveCustomerBillingCycleInfo($user, $old_bill_cycle, $new_bill_cycle)
    {
        $customerBillingCycleInfo = new CustomerBillingCycleInfo();
        $customerBillingCycleInfo->client_id = $user->id;
        $customerBillingCycleInfo->previous_billing_cycle = $old_bill_cycle;
        $customerBillingCycleInfo->current_billing_cycle = $new_bill_cycle;
        $customerBillingCycleInfo->save();
    }

    public function sendSmsAfterBillgenerate($user)
    {
        if ($user->pop->bill_generate == "yes") {
            $bill_generate = BillGenerate::where('client_id', $user->id)->orderBy('id', 'desc')->first();
            if ($bill_generate) {
                if ($bill_generate->billing_type == "billing_cycle_change") {
                    sendSmsAfterBillgenerate($bill_generate->id);
                }
            }
        }
    }


    public function userBillingCycleChange(Request $request)
    {

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

            $uniqueString = $request->uuid;

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

            if (checkSettings('expire_billing_change_today_from_billing_date') != 'enable') {
                if (now()->subMonth() > $user->expire_date) {
                    Toastr::error('Customer Expired or Deactivated or expired one month older', 'error');
                    return redirect()->back();
                }
            }

            if ($request->billCycle == $user->billing_cycle) {
                Toastr::error('Select a Different Billing Cycle Date', 'error');
                return redirect()->back();
            }


            if ($user->pop->reseller->reseller_type == 'own' && $user->customerAccount->dueAmount > 0) {
                Toastr::error('Customer Has due amount!! Please Clear Previous Due.', 'error');
                return redirect()->back();
            }

            $old_bill_cycle = $user->billing_cycle;
            $new_bill_cycle = $request->billCycle;

            $status = $this->doBillingCycleChange($user, $old_bill_cycle, $new_bill_cycle, $uniqueString);

            if ($status['status'] == 'success') {
                (new Notification)->notify("..::[Customer Billing Cycle Change]::.. \n Billing Cycle Changed.\n Change By " . auth()->user()->name . " \nCustomer Username : " . $user->userid . " \nPassword : " . $user->password . "\n Change Time : " . now());
                Toastr::success($status['message'], 'success');
            } else {
                Toastr::error($status['message'], 'error');
            }
            return redirect()->back();
        } else {
            return redirect()->back();
        }
    }

    public function autoDeactiveDateStore(Request $request)
    {
        $old_client = Client::with('clientsinfo')->find($request->id);

        $user = Client::find($request->id);
        $user->auto_deactive = $request->auto_deactive;
        $user->save();

        $new_data_client = Client::with('clientsinfo')->find($request->id);
        (new ClientController)->clientEditLog($user, $old_client, $new_data_client);

        Toastr::success('Auto Deactive Date Updated Successfully', 'success');
        return redirect()->back();
    }

    public function getBillingCyclesByPopId(Request $request)
    {
        $pop = Pop::with('reseller')->find($request->pop_id);

        // If POP or reseller or its billing_cycles is missing, return empty list gracefully
        if (! $pop || ! $pop->reseller || empty($pop->reseller->billing_cycles)) {
            return response()->json([
                'status' => 'success',
                'data'   => [],
            ]);
        }

        $billing_cycles = json_decode($pop->reseller->billing_cycles, true) ?: [];

        $billing_cycles = array_map(function ($cycle) {
            return [
                'value' => $cycle,
                'label' => ucfirst($cycle),
            ];
        }, $billing_cycles);

        return response()->json([
            'status' => 'success',
            'data'   => $billing_cycles,
        ]);
    }

    public function getBillingCyclesByResellerId(Request $request)
    {
        $reseller = Reseller::find($request->reseller_id);

        // If reseller or its billing_cycles is missing, return empty list gracefully
        if (! $reseller || empty($reseller->billing_cycles)) {
            return response()->json([
                'status' => 'success',
                'data'   => [],
            ]);
        }

        $billing_cycles = json_decode($reseller->billing_cycles, true) ?: [];

        $billing_cycles = array_map(function ($cycle) {
            return [
                'value' => $cycle,
                'label' => ucfirst($cycle),
            ];
        }, $billing_cycles);

        return response()->json([
            'status' => 'success',
            'data'   => $billing_cycles,
        ]);
    }

    public function getBillingCyclesByClientId(Request $request)
    {
        $client = Client::find($request->client_id);
        $pop = $client ? Pop::with('reseller')->find($client->pop_id) : null;
        $reseller = ($pop && $pop->reseller) ? $pop->reseller : null;

        if (! $client || ! $pop || ! $reseller || empty($reseller->billing_cycles)) {
            return response()->json([
                'status' => 'success',
                'data'   => [],
            ]);
        }

        $billing_cycles = json_decode($reseller->billing_cycles, true) ?: [];

        $billing_cycles = array_map(function ($cycle) {
            return [
                'value' => $cycle,
                'label' => ucfirst($cycle),
            ];
        }, $billing_cycles);

        return response()->json([
            'status' => 'success',
            'data'   => $billing_cycles,
        ]);
    }
}
