<?php

namespace App\Http\Controllers\Admin;

use App\DataTables\CustomerDataTable;
use App\Http\Controllers\Controller;
use App\Http\Requests\StoreCustomerRequest;
use App\Http\Requests\UpdateCustomerRequest;
use App\Jobs\UploadImportedCustomersToMikrotikJob;
use App\Jobs\DeleteCustomerFromMikrotikJob;
use App\Models\Account;
use App\Models\ActivityLog;
use App\Models\Customer;
use App\Models\CustomerGracePeriod;
use App\Models\Mikrotik;
use App\Models\Package;
use App\Models\Zone;
use App\Services\BillingService;
use App\Services\CustomerService;
use App\Services\MikrotikRouterCacheService;
use App\Services\MikrotikApiService;
use App\Services\SmsMessageService;
use App\Services\SmsTemplateService;
use App\Services\SettingService;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\Rule;
use Yajra\DataTables\Facades\DataTables;

class CustomerController extends Controller
{
    public function __construct(protected CustomerService $service, protected BillingService $billingService)
    {
        $this->authorizeResource(Customer::class, 'customer');
        $this->middleware('can:customer.create')->only(['import', 'importTemplate']);
        $this->middleware('can:customer.delete')->only(['deleted', 'restore', 'forceDelete']);
    }

    public function index()
    {
        $packages = Package::orderBy('name')->get();
        $mikrotiks = Mikrotik::where('status', 1)->orderBy('name')->get();

        return view('admin.customers.index', compact('packages', 'mikrotiks'));
    }

    public function create()
    {
        $packages = Package::orderBy('name')->get();
        $mikrotiks = Mikrotik::where('status', 1)->orderBy('name')->get();
        $zones = $this->customerFormZones();
        $defaultCustomerCode = $this->service->nextCustomerCode();

        return view('admin.customers.create', compact('packages', 'mikrotiks', 'zones', 'defaultCustomerCode'));
    }

    public function data(Request $request, CustomerDataTable $dataTable)
    {
        return $dataTable->render($request);
    }

    public function quickSearch(Request $request)
    {
        $this->authorize('viewAny', Customer::class);
        $validated = $request->validate([
            'q' => ['required', 'string', 'min:2', 'max:100'],
        ]);
        $search = trim($validated['q']);

        $customers = Customer::query()
            ->visibleTo($request->user())
            ->where(function ($query) use ($search) {
                $query->where('customer_code', 'like', "%{$search}%")
                    ->orWhere('contact_phone', 'like', "%{$search}%")
                    ->orWhere('secondary_phone', 'like', "%{$search}%")
                    ->orWhere('ip_address', 'like', "%{$search}%");

                if (ctype_digit($search)) {
                    $query->orWhereKey((int) $search);
                }
            })
            ->orderByRaw('CASE WHEN customer_code = ? THEN 0 ELSE 1 END', [$search])
            ->limit(8)
            ->get(['id', 'customer_code', 'name', 'contact_phone', 'ip_address']);

        return response()->json([
            'data' => $customers->map(fn (Customer $customer) => [
                'id' => $customer->id,
                'code' => $customer->customer_code,
                'name' => $customer->name,
                'phone' => $customer->contact_phone,
                'ip' => $customer->ip_address,
                'ledger_url' => route('admin.customers.ledger', $customer),
            ]),
        ]);
    }

    public function importTemplate()
    {
        $csv = "customer_code,name,contact_phone,email,ip_address,package,package_speed,monthly_bill,pppoe_password,status,connection_date,disconnect_date,address\n";
        $csv .= 'CUS0001,Sample Customer,01700000000,customer@example.com,10.0.0.2,10 Mbps,10M/10M,800,password123,active,'.now()->toDateString().','.now()->addMonth()->toDateString().",Dhaka\n";

        return response($csv, 200, [
            'Content-Type' => 'text/csv; charset=UTF-8',
            'Content-Disposition' => 'attachment; filename="customer-import-template.csv"',
        ]);
    }

    public function import(Request $request)
    {
        $validated = $request->validate([
            'csv_file' => ['required', 'file', 'mimes:csv,txt', 'max:5120'],
            'mikrotik_id' => ['nullable', 'integer', 'exists:mikrotiks,id'],
        ]);

        $router = filled($validated['mikrotik_id'] ?? null)
            ? Mikrotik::where('status', true)->find($validated['mikrotik_id'])
            : null;
        $handle = fopen($validated['csv_file']->getRealPath(), 'rb');
        abort_unless(is_resource($handle), 422, 'CSV file could not be read.');

        $headers = fgetcsv($handle);
        $headers = array_map(fn ($header) => strtolower(trim(preg_replace('/^\xEF\xBB\xBF/', '', (string) $header))), $headers ?: []);
        $requiredHeaders = ['name', 'contact_phone', 'ip_address', 'package', 'monthly_bill'];
        if (array_diff($requiredHeaders, $headers)) {
            fclose($handle);

            return back()->withErrors(['csv_file' => 'Required columns: '.implode(', ', $requiredHeaders).'. Download the template and match the format exactly.']);
        }

        $packages = Package::all()->keyBy(fn (Package $package) => mb_strtolower(trim($package->name)));
        $customerCodes = Customer::withTrashed()->whereNotNull('customer_code')->pluck('customer_code')->map(fn ($value) => mb_strtolower(trim($value)))->flip();
        $customerIps = Customer::withTrashed()->whereNotNull('ip_address')->pluck('ip_address')->map(fn ($value) => mb_strtolower(trim($value)))->flip();
        $summary = ['created' => 0, 'packages_created' => 0, 'skipped' => 0, 'failed' => 0, 'messages' => []];
        $importedIds = [];
        $rowNumber = 1;

        while (($values = fgetcsv($handle)) !== false) {
            $rowNumber++;
            if ($rowNumber > 5001) {
                $summary['failed']++;
                $summary['messages'][] = 'Maximum 5,000 data rows are allowed per import.';
                break;
            }
            if (count(array_filter($values, fn ($value) => trim((string) $value) !== '')) === 0) {
                continue;
            }
            $values = array_pad(array_slice($values, 0, count($headers)), count($headers), null);
            $row = array_combine($headers, $values);
            $row = array_map(fn ($value) => is_string($value) ? trim($value) : $value, $row);
            $status = mb_strtolower((string) ($row['status'] ?? 'active'));
            $row['status_value'] = in_array($status, ['1', 'active', 'yes', 'enabled'], true) ? 1 : (in_array($status, ['2', 'disconnected'], true) ? 2 : 0);

            $validator = Validator::make($row, [
                'customer_code' => ['nullable', 'string', 'max:255'],
                'name' => ['required', 'string', 'max:255'],
                'contact_phone' => ['required', 'string', 'max:30'],
                'email' => ['nullable', 'email', 'max:255'],
                'ip_address' => ['required', 'string', 'max:191', 'regex:/^[^\s\x00-\x1F\x7F]+$/u'],
                'package' => ['required', 'string', 'max:255'],
                'package_speed' => ['nullable', 'string', 'max:100'],
                'monthly_bill' => ['required', 'numeric', 'min:0'],
                'pppoe_password' => ['nullable', 'string', 'max:255'],
                'connection_date' => ['nullable', 'date'],
                'disconnect_date' => ['nullable', 'date'],
                'address' => ['nullable', 'string', 'max:500'],
                'status_value' => [Rule::in([0, 1, 2])],
            ]);
            if ($validator->fails()) {
                $summary['failed']++;
                $summary['messages'][] = "Row {$rowNumber}: ".$validator->errors()->first();

                continue;
            }

            $codeKey = mb_strtolower((string) ($row['customer_code'] ?? ''));
            $ipKey = mb_strtolower((string) $row['ip_address']);
            if (($codeKey !== '' && $customerCodes->has($codeKey)) || $customerIps->has($ipKey)) {
                $summary['skipped']++;
                $summary['messages'][] = "Row {$rowNumber}: customer code or IP already exists.";

                continue;
            }

            try {
                $packageKey = mb_strtolower($row['package']);
                $package = $packages->get($packageKey);
                if (! $package) {
                    $package = Package::create([
                        'name' => $row['package'],
                        'net_speed' => $row['package_speed'] ?: null,
                        'price' => $row['monthly_bill'],
                        'status' => true,
                        'mikrotik_server_id' => $router?->id,
                        'mikrotik_profile_name' => $row['package'],
                        'description' => 'Automatically created during customer CSV import',
                    ]);
                    $packages->put($packageKey, $package);
                    $summary['packages_created']++;
                } elseif ($router && blank($package->mikrotik_profile_name)) {
                    $package->update(['mikrotik_profile_name' => $row['package']]);
                }

                $customer = $this->service->create([
                    'customer_code' => $row['customer_code'] ?: null,
                    'name' => $row['name'],
                    'contact_phone' => $row['contact_phone'],
                    'email' => $row['email'] ?: null,
                    'ip_address' => $row['ip_address'],
                    'office_address' => $row['address'] ?: null,
                    'package_id' => $package->id,
                    'monthly_bill' => $row['monthly_bill'],
                    'status' => $row['status_value'],
                    'connection_type' => 'PPPoE',
                    'connection_date' => $row['connection_date'] ?: now()->toDateString(),
                    'mikrotik_disconnect' => $row['disconnect_date'] ?: null,
                    'mikrotik_id' => $router?->id,
                    'queue_password' => $row['pppoe_password'] ?: null,
                    'billing_person_id' => $request->user()->isEntryOperator() ? $request->user()->id : auth()->id(),
                    'created_by' => auth()->id(),
                    'remark' => 'Imported from CSV',
                ]);
                $importedIds[] = $customer->id;
                $customerCodes->put(mb_strtolower((string) $customer->customer_code), true);
                $customerIps->put($ipKey, true);
                $summary['created']++;
            } catch (\Throwable $exception) {
                report($exception);
                $summary['failed']++;
                $summary['messages'][] = "Row {$rowNumber}: could not be imported.";
            }
        }
        fclose($handle);

        $mikrotikQueued = false;
        if ($router?->connected && $importedIds) {
            UploadImportedCustomersToMikrotikJob::dispatch($router->id, $importedIds);
            $mikrotikQueued = true;
        }

        return back()->with('customer_import', array_merge($summary, [
            'mikrotik_queued' => $mikrotikQueued,
            'mikrotik_message' => $router
                ? ($mikrotikQueued ? 'Background upload has been queued for the connected MikroTik.' : 'MikroTik is disconnected. The software import was completed and no router request was sent.')
                : 'Software-only import completed.',
        ]));
    }

    public function store(StoreCustomerRequest $request)
    {
        $data = $request->validated();
        $this->applyPackageBilling($data);
        $data['created_by'] = auth()->id();
        $data['billing_person_id'] = $request->user()->isEntryOperator()
            ? $request->user()->id
            : ($data['billing_person_id'] ?? auth()->id());
        $data['send_sms'] = ! empty($data['send_sms']);

        $customer = $this->service->create($data);

        if ($customer->mikrotik_id) {
            UploadImportedCustomersToMikrotikJob::dispatch($customer->mikrotik_id, [$customer->id]);
        }

        if (! empty($data['generate_bill_this_month'])) {
            $this->billingService->generateMonthlyBillingForCustomer($customer, now());
        }

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

    public function edit(Customer $customer)
    {
        $packages = Package::orderBy('name')->get();
        $mikrotiks = Mikrotik::where('status', 1)->orderBy('name')->get();
        $zones = $this->customerFormZones();

        return view('admin.customers.create', compact('customer', 'packages', 'mikrotiks', 'zones'));
    }

    private function customerFormZones()
    {
        return Zone::query()
            ->whereNull('parent_id')
            ->where('status', true)
            ->with(['children' => fn ($query) => $query->where('status', true)->orderBy('name')])
            ->orderBy('name')
            ->get();
    }

    public function update(UpdateCustomerRequest $request, Customer $customer)
    {
        $data = $request->validated();
        // Router and PPPoE username identify the remote secret and are immutable here.
        $data['mikrotik_id'] = $customer->mikrotik_id;
        $data['ip_address'] = $customer->ip_address;
        if (blank($data['queue_password'] ?? null)) {
            unset($data['queue_password']);
        }
        $this->applyPackageBilling($data, $customer->mikrotik_id);
        $data['updated_by'] = auth()->id();
        if ($request->user()->isEntryOperator()) {
            $data['billing_person_id'] = $customer->billing_person_id ?: $request->user()->id;
        }

        $this->service->update($customer, $data);

        if ($customer->mikrotik_id) {
            UploadImportedCustomersToMikrotikJob::dispatch($customer->mikrotik_id, [$customer->id]);
        }

        $billing = null;
        if (! empty($data['generate_bill_this_month'])) {
            $billing = $this->billingService->generateMonthlyBillingForCustomer($customer->fresh(), now());
        }

        return response()->json([
            'success' => true,
            'data' => $customer->fresh(),
            'billing_generated' => $billing !== null,
            'message' => $billing
                ? 'Customer updated and current month bill is available.'
                : 'Customer updated successfully.',
        ]);
    }

    private function applyPackageBilling(array &$data, ?int $mikrotikId = null): void
    {
        if (empty($data['package_id'])) {
            return;
        }

        $package = Package::findOrFail($data['package_id']);
        $routerId = $mikrotikId ?? ($data['mikrotik_id'] ?? null);

        if ($routerId && ($package->mikrotik_server_id && (int) $package->mikrotik_server_id !== (int) $routerId)) {
            throw \Illuminate\Validation\ValidationException::withMessages([
                'package_id' => 'The selected package belongs to a different MikroTik server.',
            ]);
        }
        if ($routerId && blank($package->mikrotik_profile_name)) {
            throw \Illuminate\Validation\ValidationException::withMessages([
                'package_id' => 'Select a package that has a MikroTik profile.',
            ]);
        }

        $data['monthly_bill'] = $package->price;
    }

    public function ledger(Customer $customer)
    {
        $this->authorize('view', $customer);
        $customer->load(['gracePeriods' => fn ($query) => $query->whereDate('grace_until', '>=', today())->latest('grace_until')->limit(1)]);

        return view('admin.customers.ledger', compact('customer'));
    }

    public function ledgerData(Request $request, Customer $customer)
    {
        $this->authorize('view', $customer);

        $query = $customer->accounts()->with('entryBy')->orderByDesc('entry_date')->orderByDesc('id');

        if ($request->filled('date_from')) {
            $query->whereDate('entry_date', '>=', $request->input('date_from'));
        }

        if ($request->filled('date_to')) {
            $query->whereDate('entry_date', '<=', $request->input('date_to'));
        }

        $totalAmount = (clone $query)->sum('amount');
        $customerTotalPaid = $customer->accounts()->where(function ($query) {
            $query->whereNull('description')
                ->orWhere(function ($query) {
                    $query->where('description', 'not like', '%discount%')
                        ->where('description', 'not like', '%bonus%')
                        ->where('description', 'not like', '%rebate%');
                });
        })->sum('amount');

        $totalDiscount = $customer->accounts()->where(function ($query) {
            $query->where('description', 'like', '%discount%')
                ->orWhere('description', 'like', '%bonus%')
                ->orWhere('description', 'like', '%rebate%');
        })->sum('amount');

        $totalBilling = $customer->billings()->sum('amount');
        $totalOutstanding = $customer->billings()
            ->whereIn('status', ['pending', 'partial'])
            ->sum(DB::raw('COALESCE(due_amount, amount)'));
        $dueAdvance = $totalOutstanding;
        $previousDue = max($dueAdvance - $customer->monthly_bill, 0);

        $billingInfo = [
            'monthlybill' => $customer->monthly_bill ? number_format($customer->monthly_bill, 2) : '0.00',
            'totalpaid' => number_format($customerTotalPaid, 2),
            'totaldiscount' => number_format($totalDiscount, 2),
            'dueadvance' => number_format($dueAdvance, 2),
            'previousdue' => number_format($previousDue, 2),
        ];

        $agentInfo = [
            'ag_id' => $customer->id,
            'cus_id' => $customer->customer_code,
            'ag_name' => $customer->name,
            'ag_email' => $customer->email,
            'ag_mobile_no' => $customer->contact_phone,
            'mb' => $customer->package?->name ?? 'Standard',
            'ip' => $customer->ip_address,
            'taka' => $customer->monthly_bill ? number_format($customer->monthly_bill, 2) : '0.00',
            'ag_office_address' => $customer->office_address,
            'national_id' => $customer->national_id,
            'zone_name' => $customer->zone,
            'onumac' => $customer->onumac,
            'mikrotik_disconnect' => optional($customer->mikrotik_disconnect)->toDateString(),
        ];

        return DataTables::eloquent($query)
            ->addIndexColumn()
            ->addColumn('entry_by', fn ($account) => $account->entryBy?->name ?? '-')
            ->addColumn('amount', fn ($account) => number_format($account->amount, 2))
            ->addColumn('description', fn ($account) => $account->description ?? '-')
            ->addColumn('entry_date', fn ($account) => optional($account->entry_date)->format('Y-m-d'))
            ->addColumn('entry_type', fn ($account) => (int) $account->type === 99 ? 'Discount' : ((int) $account->type === 1 ? 'Payment' : 'Other'))
            ->addColumn('action', function ($account) use ($customer) {
                $buttons = [];
                if ((int) $account->type === 1) {
                    $receiptUrl = route('admin.customers.ledger.receipt', [$customer, $account]);
                    $buttons[] = '<a target="_blank" class="btn btn-sm btn-outline-primary" href="'.e($receiptUrl).'" title="Print POS receipt"><i class="fas fa-print"></i> Print</a>';
                }

                if (auth()->user()?->can('billing.delete') && in_array((int) $account->type, [1, 99], true)) {
                    $url = route('admin.customers.ledger.destroy', [$customer, $account]);
                    $label = (int) $account->type === 99 ? 'discount' : 'payment';
                    $buttons[] = '<button type="button" class="btn btn-sm btn-outline-danger delete-ledger-entry" data-url="'.e($url).'" data-label="'.$label.'" data-amount="'.e(number_format((float) $account->amount, 2)).'">Delete</button>';
                }

                return $buttons ? '<div class="d-flex flex-wrap gap-1">'.implode('', $buttons).'</div>' : '-';
            })
            ->with([
                'totalAmount' => number_format($totalAmount, 2),
                'billing_info' => $billingInfo,
                'agentInfo' => $agentInfo,
            ])
            ->rawColumns(['action'])
            ->make(true);
    }

    public function ledgerReceipt(Customer $customer, Account $account)
    {
        $this->authorize('view', $customer);
        abort_unless($account->customer_id === $customer->id && (int) $account->type === 1, 404);

        $settings = app(SettingService::class);
        $business = [
            'name' => $settings->get('business.name', config('app.name', 'ISP Billing')),
            'phone' => $settings->get('business.phone', ''),
            'address' => $settings->get('business.address', ''),
            'logo' => $settings->get('business.logo'),
        ];
        $paymentMonth = null;
        if (preg_match('/payment for:\s*(\d{4}-\d{2})/i', (string) $account->description, $matches)) {
            try {
                $paymentMonth = Carbon::createFromFormat('Y-m', $matches[1])->format('F Y');
            } catch (\Throwable) {
                $paymentMonth = $matches[1];
            }
        }

        return view('admin.customers.ledger-receipt', compact('customer', 'account', 'business', 'paymentMonth'));
    }

    public function destroy(Customer $customer)
    {
        $this->service->delete($customer);

        return response()->json(['success' => true, 'message' => 'Customer moved to Delete Log. MikroTik removal has been queued.']);
    }

    public function deleted(Request $request)
    {
        $search = trim((string) $request->input('q'));
        $customers = Customer::onlyTrashed()
            ->with(['package', 'mikrotik'])
            ->visibleTo($request->user())
            ->when($search !== '', function ($query) use ($search) {
                $query->where(function ($query) use ($search) {
                    $query->where('customer_code', 'like', '%'.$search.'%')
                        ->orWhere('name', 'like', '%'.$search.'%')
                        ->orWhere('contact_phone', 'like', '%'.$search.'%')
                        ->orWhere('ip_address', 'like', '%'.$search.'%');
                });
            })
            ->latest('deleted_at')
            ->paginate(25)
            ->withQueryString();

        return view('admin.customers.deleted', compact('customers', 'search'));
    }

    public function restore(Request $request, Customer $customer)
    {
        abort_unless($customer->trashed(), 404);
        $this->authorize('restore', $customer);
        $customer->restore();

        ActivityLog::create([
            'user_id' => $request->user()->id,
            'action' => 'customer_restored',
            'resource_type' => Customer::class,
            'resource_id' => $customer->id,
            'description' => 'Customer restored: '.$customer->customer_code,
        ]);

        if ($customer->mikrotik_id) {
            UploadImportedCustomersToMikrotikJob::dispatch($customer->mikrotik_id, [$customer->id], true);
        }

        return response()->json(['success' => true, 'message' => 'Customer restored. MikroTik setup has been queued.']);
    }

    public function forceDelete(Request $request, Customer $customer)
    {
        abort_unless($customer->trashed(), 404);
        $this->authorize('forceDelete', $customer);
        $customerId = $customer->id;
        $code = $customer->customer_code;
        $mikrotikId = $customer->mikrotik_id;
        $username = trim((string) ($customer->ip_address ?: $code));

        if ($mikrotikId && $username !== '') {
            DeleteCustomerFromMikrotikJob::dispatch($customerId, (int) $mikrotikId, $username);
        }

        $customer->forceDelete();
        ActivityLog::create([
            'user_id' => $request->user()->id,
            'action' => 'customer_permanently_deleted',
            'resource_type' => Customer::class,
            'resource_id' => $customerId,
            'description' => 'Customer permanently deleted: '.$code,
        ]);

        return response()->json(['success' => true, 'message' => 'Customer permanently deleted.']);
    }

    public function bulkDestroy(Request $request)
    {
        $validated = $request->validate([
            'customer_ids' => ['required', 'array', 'min:1', 'max:200'],
            'customer_ids.*' => ['required', 'integer', 'distinct', 'exists:customers,id'],
        ]);

        $customers = Customer::visibleTo($request->user())
            ->whereIn('id', $validated['customer_ids'])
            ->get();

        abort_unless($customers->count() === count($validated['customer_ids']), 403);
        foreach ($customers as $customer) {
            $this->authorize('delete', $customer);
        }

        DB::transaction(function () use ($customers): void {
            foreach ($customers as $customer) {
                $this->service->delete($customer);
            }
        });

        return response()->json(['success' => true, 'deleted' => $customers->count()]);
    }

    public function updateDisconnect(Request $request, Customer $customer)
    {
        $this->authorize('update', $customer);

        $data = $request->validate([
            'mikrotik_disconnect' => ['nullable', 'date'],
        ]);

        $customer->mikrotik_disconnect = $data['mikrotik_disconnect'] ?? null;
        $customer->updated_by = auth()->id();
        $customer->save();

        ActivityLog::where('action', 'mikrotik_due_disconnected')
            ->where('resource_type', Customer::class)
            ->where('resource_id', $customer->id)
            ->delete();

        // activity log
        ActivityLog::create([
            'user_id' => auth()->id(),
            'action' => 'mikrotik_disconnect_updated',
            'resource_type' => Customer::class,
            'resource_id' => $customer->id,
            'description' => 'Updated mikrotik disconnect date to '.($customer->mikrotik_disconnect?->toDateString() ?? 'N/A'),
        ]);

        return response()->json(['success' => true, 'mikrotik_disconnect' => optional($customer->mikrotik_disconnect)->toDateString()]);
    }

    public function extendDisconnectGrace(Request $request, Customer $customer, SmsTemplateService $smsTemplates, SmsMessageService $smsMessages)
    {
        $this->authorize('update', $customer);

        $data = $request->validate([
            'days' => ['required', 'integer', 'min:1', 'max:30'],
            'reason' => ['nullable', 'string', 'max:255'],
            'send_sms' => ['nullable', 'boolean'],
        ]);

        $originalDate = $customer->mikrotik_disconnect?->copy();
        $activeGrace = CustomerGracePeriod::where('customer_id', $customer->id)
            ->whereDate('grace_until', '>=', today())
            ->latest('grace_until')
            ->first();
        $baseDate = $activeGrace?->grace_until?->copy()
            ?: (($originalDate && $originalDate->gte(today())) ? $originalDate->copy() : today()->copy());
        $newDate = $baseDate->copy()->addDays((int) $data['days']);

        CustomerGracePeriod::create([
            'customer_id' => $customer->id,
            'disconnect_date' => $originalDate?->toDateString(),
            'grace_from' => today()->toDateString(),
            'grace_until' => $newDate->toDateString(),
            'days' => (int) $data['days'],
            'reason' => trim((string) ($data['reason'] ?? '')),
            'created_by' => auth()->id(),
        ]);

        $customer->forceFill([
            'updated_by' => auth()->id(),
        ])->save();

        ActivityLog::where('action', 'mikrotik_due_disconnected')
            ->where('resource_type', Customer::class)
            ->where('resource_id', $customer->id)
            ->delete();

        ActivityLog::create([
            'user_id' => auth()->id(),
            'action' => 'customer_grace_extended',
            'resource_type' => Customer::class,
            'resource_id' => $customer->id,
            'description' => 'Added grace for '.(int) $data['days'].' day(s). Original disconnect date '.($originalDate?->toDateString() ?? 'not set').', grace active until '.$newDate->toDateString(),
            'metadata' => [
                'disconnect_date' => $originalDate?->toDateString(),
                'previous_grace_until' => $activeGrace?->grace_until?->toDateString(),
                'new_grace_until' => $newDate->toDateString(),
                'days' => (int) $data['days'],
                'reason' => trim((string) ($data['reason'] ?? '')),
            ],
        ]);

        if (! empty($data['send_sms']) && filled($customer->contact_phone)) {
            $reason = trim((string) ($data['reason'] ?? 'temporary grace approved'));
            $message = "Dear {$customer->name}, your line grace period is active until {$newDate->format('d-m-Y')}. Original disconnect date remains {$originalDate?->format('d-m-Y')}. Reason: {$reason}.";
            $smsMessages->createAndSend([
                'recipient' => $customer->contact_phone,
                'message' => $message,
                'source' => 'customer_grace_extension',
            ]);
        }

        return response()->json([
            'success' => true,
            'message' => 'Grace days added successfully.',
            'disconnect_date' => $originalDate?->toDateString(),
            'grace_until' => $newDate->toDateString(),
        ]);
    }

    public function liveTraffic(Customer $customer, MikrotikApiService $mikrotikApi)
    {
        $this->authorize('view', $customer);

        $mikrotik = $customer->mikrotik;
        if (! $mikrotik) {
            return response()->json(['connected' => false, 'message' => 'No MikroTik assigned to this customer']);
        }

        // Live Traffic is an explicit user action, so use a fresh RouterOS read.
        // Customer lists/dashboard continue using background cache to stay fast.
        try {
            $active = $mikrotikApi->fetchActiveSessions($mikrotik);
            Cache::put('mikrotik:active:'.$mikrotik->id, $active, now()->addMinutes(10));
            if (! $mikrotik->connected) {
                $mikrotik->forceFill(['connected' => true, 'last_connected_at' => now(), 'last_checked_at' => now()])->save();
            }
        } catch (\Throwable $exception) {
            $mikrotik->forceFill(['connected' => false, 'last_checked_at' => now()])->save();

            return response()->json([
                'connected' => false,
                'message' => 'MikroTik connection failed. Please check the router connection.',
            ], 503);
        }

        $identifiers = collect([$customer->ip_address, $customer->customer_code, $customer->queue_password])
            ->map(fn ($value) => strtolower(trim((string) $value)))
            ->filter()
            ->unique();
        $match = null;
        foreach ($active as $session) {
            $name = strtolower(trim((string) ($session['name'] ?? '')));
            if ($identifiers->contains($name)) {
                $match = $session;
                break;
            }
        }

        if (! $match) {
            return response()->json(['connected' => true, 'live' => null, 'message' => 'Customer not active on MikroTik']);
        }

        try {
            $traffic = $mikrotikApi->fetchPppTraffic($mikrotik, (string) $match['name']);
        } catch (\Throwable $exception) {
            return response()->json([
                'connected' => true,
                'live' => $match,
                'traffic' => null,
                'message' => 'Customer is online, but traffic rate is temporarily unavailable.',
            ]);
        }

        return response()->json(['connected' => true, 'live' => $match, 'traffic' => $traffic]);
    }
}
