<?php

namespace App\Controllers;

use App\Models\Payment;
use App\Models\Station;
use App\Models\Vendor;
use Illuminate\Http\Request;
use Illuminate\Http\JsonResponse;
use App\Models\InternetPayment;
use App\Models\AirtimePayment;
use App\Models\PaymentSchedule;
use App\Models\InternetProvider;
use App\Models\Employee;
use Carbon\Carbon;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\ValidationException;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Mail;
use App\Services\PaymentReminderService;
use App\Models\NotificationLog;
use App\Models\MpesaTransaction;

class PaymentController extends Controller
{
    protected $reminderService;

    public function __construct(PaymentReminderService $reminderService)
    {
        $this->reminderService = $reminderService;
    }

    /**
     * Display a listing of payments.
     */
    public function index(Request $request)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Index request started', [
                'request_id' => $requestId,
                'user_id' => Auth::id(),
                'ip' => $request->ip(),
                'is_api' => $this->isApiRequest($request)
            ]);
            
            $payments = Payment::with(['station', 'vendor'])
                ->orderBy('due_date', 'desc')
                ->paginate(20);

            Log::info('[PAYMENT] Payments found', [
                'request_id' => $requestId,
                'total_count' => $payments->total()
            ]);

            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::info('[PAYMENT] Index completed successfully', [
                'request_id' => $requestId,
                'execution_time_ms' => $executionTime,
                'result_count' => $payments->total()
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'payments' => $payments,
                    'total' => $payments->total(),
                    'per_page' => $payments->perPage(),
                    'current_page' => $payments->currentPage(),
                ]);
            }

            return view('payments.index', compact('payments'));

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to fetch payments', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id(),
                'is_api' => $this->isApiRequest($request)
            ]);
            
            return $this->handleError($e, $request, 'Failed to fetch payments');
        }
    }

    /**
     * Show the form for creating a new payment.
     */
    public function create(Request $request, Payment $payment = null)
    {
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Create form loaded', [
                'request_id' => $requestId,
                'user_id' => Auth::id(),
                'has_existing_payment' => !is_null($payment),
                'payment_id' => $payment ? $payment->id : null
            ]);
            
            $stations = Station::all();
            $vendors = Vendor::all();
            
            $types = [
                'utility' => 'Utility Bill',
                'service' => 'Service Payment', 
                'product' => 'Product Purchase',
                'rent' => 'Rent',
                'salary' => 'Salary',
                'other' => 'Other'
            ];
            
            $default_due_date = now()->addDays(30)->format('Y-m-d');
            $statuses = ['pending', 'approved', 'paid', 'rejected'];

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'stations' => $stations,
                    'vendors' => $vendors,
                    'types' => $types,
                    'default_due_date' => $default_due_date,
                    'statuses' => $statuses
                ]);
            }

            return view('payments.create', [
                'stations' => $stations,
                'vendors' => $vendors,
                'types' => $types,
                'default_due_date' => $default_due_date,
                'statuses' => $statuses,
                'payment' => $payment  
            ]);

        } catch (\Exception $e) {
            Log::error('[PAYMENT] Create form error', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'user_id' => Auth::id()
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonError('Failed to load create form: ' . $e->getMessage(), 500);
            }
            
            return back()->with('error', 'Failed to load create form: ' . $e->getMessage());
        }
    }

    /**
     * Store a newly created payment in storage.
     */
    public function store(Request $request)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Store started', [
                'request_id' => $requestId,
                'user_id' => Auth::id(),
                'request_data' => $this->sanitizeLogData($request->all()),
                'ip' => $request->ip()
            ]);
            
            $validated = $this->validatePaymentStore($request);
            Log::debug('[PAYMENT] Validation passed', ['request_id' => $requestId]);
            
            $validated = $this->handlePaymentAttachment($request, $validated);
            $validated = $this->setPaymentApproval($validated);
            $validated['created_by'] = Auth::id();

            $payment = Payment::create($validated);
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Created successfully', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'station_id' => $payment->station_id,
                'amount' => $payment->amount,
                'status' => $payment->status,
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($payment->load(['station', 'vendor']), 'Payment created successfully', 201);
            }

            return redirect()->route('payments.index')
                ->with('success', 'Payment created successfully!');

        } catch (ValidationException $e) {
            Log::warning('[PAYMENT] Validation failed', [
                'request_id' => $requestId,
                'errors' => $e->errors(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            return $this->handleValidationError($e, $request);
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error creating payment', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            return $this->handleError($e, $request, 'Error creating payment');
        }
    }

    /**
     * Display the specified payment.
     */
    public function show(Request $request, Payment $payment)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Show requested', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'user_id' => Auth::id(),
                'is_api' => $this->isApiRequest($request)
            ]);
            
            if ($this->isApiRequest($request)) {
                $executionTime = round((microtime(true) - $startTime) * 1000, 2);
                Log::info('[PAYMENT] Show API response sent', [
                    'request_id' => $requestId,
                    'payment_id' => $payment->id,
                    'execution_time_ms' => $executionTime
                ]);
                return $this->jsonSuccess($payment->load(['station', 'vendor']));
            }

            return view('payments.show', compact('payment'));

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to fetch payment', [
                'request_id' => $requestId,
                'payment_id' => $payment->id ?? null,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to fetch payment');
        }
    }

    /**
     * Show the form for editing the specified payment.
     */
    public function edit(Request $request, Payment $payment)
    {
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Edit form loaded', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'user_id' => Auth::id(),
                'current_status' => $payment->status
            ]);
            
            $stations = Station::all();
            $vendors = Vendor::all();
            
            $types = [
                'utility' => 'Utility Bill',
                'service' => 'Service Payment', 
                'product' => 'Product Purchase',
                'rent' => 'Rent',
                'salary' => 'Salary',
                'other' => 'Other'
            ];
            
            $default_due_date = $payment->due_date ? $payment->due_date->format('Y-m-d') : now()->addDays(30)->format('Y-m-d');
            $statuses = ['pending', 'approved', 'paid', 'rejected'];

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'payment' => $payment,
                    'stations' => $stations,
                    'vendors' => $vendors,
                    'types' => $types,
                    'default_due_date' => $default_due_date,
                    'statuses' => $statuses
                ]);
            }

            return view('payments.create', [
                'stations' => $stations,
                'vendors' => $vendors,
                'types' => $types,
                'default_due_date' => $default_due_date,
                'statuses' => $statuses,
                'payment' => $payment 
            ]);

        } catch (\Exception $e) {
            Log::error('[PAYMENT] Failed to load edit form', [
                'request_id' => $requestId,
                'payment_id' => $payment->id ?? null,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to load edit form');
        }
    }

    /**
     * Update the specified payment in storage.
     */
    public function update(Request $request, Payment $payment)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Update started', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'user_id' => Auth::id(),
                'old_status' => $payment->status,
                'old_amount' => $payment->amount,
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            
            $validated = $this->validatePaymentUpdate($request);
            $validated = $this->handlePaymentAttachment($request, $validated, $payment);

            $payment->update($validated);
            
            $changes = $payment->getChanges();
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Updated successfully', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'changes' => $changes,
                'new_status' => $payment->status,
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($payment->load(['station', 'vendor']), 'Payment updated successfully');
            }

            return redirect()->route('payments.index')
                ->with('success', 'Payment updated successfully!');

        } catch (ValidationException $e) {
            Log::warning('[PAYMENT] Update validation failed', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'errors' => $e->errors(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            return $this->handleValidationError($e, $request);
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error updating payment', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Error updating payment');
        }
    }

    /**
     * Remove the specified payment from storage.
     */
    public function destroy(Request $request, Payment $payment)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Deletion started', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'user_id' => Auth::id(),
                'payment_details' => [
                    'station_id' => $payment->station_id,
                    'amount' => $payment->amount,
                    'status' => $payment->status
                ]
            ]);
            
            if ($payment->attachment_path) {
                Storage::delete($payment->attachment_path);
                Log::debug('[PAYMENT] Attachment deleted', [
                    'request_id' => $requestId,
                    'payment_id' => $payment->id,
                    'attachment_path' => $payment->attachment_path
                ]);
            }

            $payment->delete();
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Deleted successfully', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess(null, 'Payment deleted successfully');
            }

            return redirect()->route('payments.index')
                ->with('success', 'Payment deleted successfully!');

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error deleting payment', [
                'request_id' => $requestId,
                'payment_id' => $payment->id ?? null,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Error deleting payment');
        }
    }

    /**
     * Approve payment
     */
    public function approve(Request $request, Payment $payment)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Approval started', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'user_id' => Auth::id(),
                'current_status' => $payment->status
            ]);
            
            $payment->update([
                'status' => 'approved',
                'approved_by' => Auth::id(),
                'approved_at' => now()
            ]);
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Approved successfully', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'approved_by' => Auth::id(),
                'approved_at' => now()->toDateTimeString(),
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($payment, 'Payment approved successfully');
            }

            return back()->with('success', 'Payment approved!');

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error approving payment', [
                'request_id' => $requestId,
                'payment_id' => $payment->id ?? null,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Error approving payment');
        }
    }

    /**
     * Mark payment as paid
     */
    public function markAsPaid(Request $request, Payment $payment)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Mark as paid started', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'user_id' => Auth::id(),
                'current_status' => $payment->status
            ]);
            
            $payment->update([
                'status' => 'paid',
                'paid_at' => now()
            ]);
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Marked as paid successfully', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'paid_at' => now()->toDateTimeString(),
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($payment, 'Payment marked as paid');
            }

            return back()->with('success', 'Payment marked as paid!');

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error marking payment as paid', [
                'request_id' => $requestId,
                'payment_id' => $payment->id ?? null,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Error marking payment as paid');
        }
    }

    /**
     * Display station payments
     */
    public function stationPayments(Request $request, $stationId)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Fetching station payments', [
                'request_id' => $requestId,
                'station_id' => $stationId,
                'user_id' => Auth::id()
            ]);
            
            $station = Station::with([
                'employees' => function($query) {
                    $query->orderBy('first_name');
                },
                'internetPayments' => function($query) {
                    $query->with('provider')->orderBy('due_date', 'desc')->limit(10);
                },
                'airtimePayments' => function($query) {
                    $query->orderBy('topup_date', 'desc')->limit(10);
                },
                'payments' => function($query) {
                    $query->orderBy('due_date', 'desc')->limit(10);
                },
                'paymentSchedules' => function($query) {
                    $query->where('scheduled_date', '>=', now())->orderBy('scheduled_date');
                },
                'serviceProviders' => function($query) {
                    $query->wherePivot('status', 'active');
                },
                'vendors' => function($query) {
                    $query->wherePivot('status', 'active');
                }
            ])->findOrFail($stationId);
            
            $stats = [
                'total_employees' => $station->employees->count(),
                'active_employees' => $station->employees->where('status', 'active')->count(),
                'total_internet_paid' => $station->internetPayments->where('status', 'paid')->sum('amount'),
                'total_airtime_paid' => $station->airtimePayments->sum('amount'),
                'pending_payments' => $station->internetPayments->where('status', 'pending')->sum('amount'),
                'recent_payments_count' => $station->internetPayments->take(5)->count() + $station->airtimePayments->take(5)->count()
            ];
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::info('[PAYMENT] Station payments fetched successfully', [
                'request_id' => $requestId,
                'station_id' => $stationId,
                'stats' => $stats,
                'execution_time_ms' => $executionTime
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'station' => $station,
                    'statistics' => $stats
                ]);
            }
            
            return view('payments.station', compact('station', 'stats'));
            
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to fetch station payments', [
                'request_id' => $requestId,
                'station_id' => $stationId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to fetch station payments');
        }
    }

    /**
     * Display a listing of internet payments.
     */
    public function indexInternetPayments(Request $request)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Fetching internet payments list', [
                'request_id' => $requestId,
                'user_id' => Auth::id(),
                'filters' => $request->only(['status', 'vendor_id', 'station_id', 'month', 'search'])
            ]);
            
            $query = $this->buildInternetPaymentQuery($request);
            $payments = $query->orderBy('due_date', 'desc')->paginate(20);
            $stations = Station::all();
            $providers = InternetProvider::all();
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::info('[PAYMENT] Internet payments fetched successfully', [
                'request_id' => $requestId,
                'total_count' => $payments->total(),
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'payments' => $payments,
                    'stations' => $stations,
                    'providers' => $providers,
                    'filters' => $request->all()
                ]);
            }

            return view('payments.internet.index', compact('payments', 'stations', 'providers'));

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to fetch internet payments', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to fetch internet payments');
        }
    }

    /**
     * Show form for creating internet payment.
     */
    public function createInternetPayment(Request $request)
    {
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Loading internet payment create form', [
                'request_id' => $requestId,
                'user_id' => Auth::id()
            ]);
            
            $stations = Station::all();
            $providers = InternetProvider::all();

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'stations' => $stations,
                    'providers' => $providers,
                    'form_fields' => [
                        'station_id', 'vendor_id', 'account_number', 'amount',
                        'previous_balance', 'billing_month', 'due_date', 'payment_date',
                        'mpesa_receipt', 'transaction_id', 'invoice_notes',
                        'payment_method', 'status', 'create_schedule'
                    ]
                ]);
            }

            return view('payments.internet.create', compact('stations', 'providers'));

        } catch (\Exception $e) {
            Log::error('[PAYMENT] Failed to load internet payment create form', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to load create form');
        }
    }

    /**
     * Store internet payment.
     */
    public function storeInternetPayment(Request $request)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Store internet payment started', [
                'request_id' => $requestId,
                'user_id' => Auth::id()
            ]);
            Log::debug('[PAYMENT] Request data:', [
                'request_id' => $requestId,
                'data' => $this->sanitizeLogData($request->all())
            ]);

            $validated = $this->validateInternetPaymentStore($request);
            Log::debug('[PAYMENT] Internet payment validation passed', ['request_id' => $requestId]);
            
            $payment = $this->createInternetPaymentRecord($validated);

            if ($request->boolean('create_schedule')) {
                $this->createPaymentSchedule($validated);
                Log::info('[PAYMENT] Payment schedule created', [
                    'request_id' => $requestId,
                    'payment_id' => $payment->id
                ]);
            }

            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Internet payment created successfully', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'station_id' => $payment->station_id,
                'amount' => $payment->amount,
                'status' => $payment->status,
                'execution_time_ms' => $executionTime
            ]);

            $routeName = 'payments.internet.index';
            $routeUrl = route($routeName);
            Log::debug('[PAYMENT] Redirect URL: ' . $routeUrl, ['request_id' => $requestId]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($payment->load(['station', 'provider']), 'Internet payment recorded successfully', 201);
            }

            return redirect()->to($routeUrl)
                ->with('success', 'Internet payment recorded successfully!');

        } catch (ValidationException $e) {
            Log::warning('[PAYMENT] Internet payment validation error', [
                'request_id' => $requestId,
                'errors' => json_encode($e->errors()),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            if ($this->isApiRequest($request)) {
                return $this->jsonError('Validation failed', 422, $e->errors());
            }
            return redirect()->back()
                ->withErrors($e->errors())
                ->withInput();
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error creating internet payment', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonError('Error creating internet payment: ' . $e->getMessage(), 500);
            }
            
            return redirect()->back()
                ->withInput()
                ->with('error', 'Error creating internet payment: ' . $e->getMessage());
        }
    }

    /**
     * Show form for editing internet payment.
     */
    public function editInternetPayment(Request $request, $id)
    {
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Loading internet payment edit form', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'user_id' => Auth::id()
            ]);
            
            $payment = InternetPayment::findOrFail($id);
            $stations = Station::all();
            $providers = InternetProvider::all();

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'payment' => $payment,
                    'stations' => $stations,
                    'providers' => $providers
                ]);
            }

            return view('payments.internet.edit', compact('payment', 'stations', 'providers'));

        } catch (\Exception $e) {
            Log::error('[PAYMENT] Failed to load internet payment edit form', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to load edit form');
        }
    }

    /**
     * Update internet payment.
     */
    public function updateInternetPayment(Request $request, $id)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Updating internet payment', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'user_id' => Auth::id(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            
            $payment = InternetPayment::findOrFail($id);
            $oldData = $payment->toArray();
            
            $validated = $this->validateInternetPaymentUpdate($request, $id);

            $payment->update($this->prepareInternetPaymentData($validated));
            
            $changes = $payment->getChanges();
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Internet payment updated successfully', [
                'request_id' => $requestId,
                'payment_id' => $payment->id,
                'changes' => $changes,
                'old_data' => $oldData,
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($payment->load(['station', 'provider']), 'Internet payment updated successfully');
            }

            return redirect()->route('payments.internet.index')
                ->with('success', 'Internet payment updated successfully!');

        } catch (ValidationException $e) {
            Log::warning('[PAYMENT] Internet payment update validation failed', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'errors' => $e->errors(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            return $this->handleValidationError($e, $request);
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error updating internet payment', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Error updating internet payment');
        }
    }

    /**
     * Delete internet payment.
     */
    public function destroyInternetPayment(Request $request, $id)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Deleting internet payment', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'user_id' => Auth::id()
            ]);
            
            $payment = InternetPayment::findOrFail($id);
            $paymentDetails = $payment->toArray();
            
            $payment->delete();
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Internet payment deleted successfully', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'deleted_payment_details' => $paymentDetails,
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess(null, 'Internet payment deleted successfully');
            }

            return redirect()->route('payments.internet.index')
                ->with('success', 'Internet payment deleted successfully!');

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error deleting internet payment', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Error deleting internet payment');
        }
    }

    /**
     * Display internet payment details.
     */
    public function showInternetPayment(Request $request, $id)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Showing internet payment', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'user_id' => Auth::id()
            ]);
            
            $payment = InternetPayment::with(['station', 'provider'])->findOrFail($id);
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::info('[PAYMENT] Internet payment fetched successfully', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($payment);
            }

            return view('payments.internet.show', compact('payment'));

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to fetch internet payment', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to fetch internet payment');
        }
    }

    /**
     * Show internet payment partial view for modal.
     */
    public function showInternetPaymentPartial($id)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Show internet payment partial', [
                'request_id' => $requestId,
                'payment_id' => $id
            ]);
            
            $payment = InternetPayment::with(['station', 'provider'])->findOrFail($id);
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::info('[PAYMENT] Internet payment partial rendered successfully', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'execution_time_ms' => $executionTime
            ]);
            
            return view('payments.internet.partials.details', compact('payment'))->render();
            
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error in showInternetPaymentPartial', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime
            ]);
            return response()->json([
                'success' => false,
                'message' => 'Failed to load payment details: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Show airtime payment partial view for modal.
     */
    public function getAirtimeDetailsPartial($id)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Show airtime payment partial', [
                'request_id' => $requestId,
                'payment_id' => $id
            ]);
            
            $payment = AirtimePayment::with('station')->findOrFail($id);
            $payment->updateStatusIfExpired();
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::info('[PAYMENT] Airtime payment partial rendered successfully', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'status' => $payment->status,
                'execution_time_ms' => $executionTime
            ]);
            
            return view('payments.airtime.partials.details', compact('payment'))->render();
            
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error in getAirtimeDetailsPartial', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime
            ]);
            return response()->json([
                'success' => false,
                'message' => 'Failed to load payment details: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Display a listing of airtime payments.
     */
    public function indexAirtimePayments(Request $request)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Fetching airtime payments list', [
                'request_id' => $requestId,
                'user_id' => Auth::id(),
                'filters' => $request->only(['station_id', 'network_provider', 'status', 'month', 'search'])
            ]);
            
            $this->updateExpiredAirtimePayments();

            $query = $this->buildAirtimePaymentQuery($request);
            $payments = $query->orderBy('expected_expiry', 'desc')->paginate(20);
            $stations = Station::all();
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::info('[PAYMENT] Airtime payments fetched successfully', [
                'request_id' => $requestId,
                'total_count' => $payments->total(),
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'payments' => $payments,
                    'stations' => $stations,
                    'filters' => $request->all()
                ]);
            }

            return view('payments.airtime.index', compact('payments', 'stations'));

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to fetch airtime payments', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to fetch airtime payments');
        }
    }

    /**
     * Show form for creating airtime payment.
     */
    public function createAirtimePayment(Request $request)
    {
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Loading airtime payment create form', [
                'request_id' => $requestId,
                'user_id' => Auth::id()
            ]);
            
            $stations = Station::all();
            $stats = $this->getAirtimeStats();
            $recentPayments = $this->getRecentAirtimePayments();
            $recentNumbers = $this->getRecentMobileNumbers();

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'stations' => $stations,
                    'stats' => $stats,
                    'recent_payments' => $recentPayments,
                    'recent_numbers' => $recentNumbers,
                    'form_fields' => [
                        'station_id', 'mobile_number', 'amount', 'topup_date',
                        'network_provider', 'transaction_id', 'expected_expiry',
                        'payment_method', 'notes', 'auto_renew', 'send_notification'
                    ]
                ]);
            }

            return view('payments.airtime.create', compact('stations', 'stats', 'recentPayments', 'recentNumbers'));

        } catch (\Exception $e) {
            Log::error('[PAYMENT] Failed to load airtime payment create form', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to load create form');
        }
    }

    /**
     * Store airtime payment.
     */
    public function storeAirtimePayment(Request $request)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Storing airtime payment', [
                'request_id' => $requestId,
                'user_id' => Auth::id(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            
            $validated = $this->validateAirtimePaymentStore($request);
            $validated = $this->prepareAirtimePaymentData($validated);

            $airtimePayment = AirtimePayment::create($validated);
            
            Log::debug('[PAYMENT] Airtime payment created', [
                'request_id' => $requestId,
                'payment_id' => $airtimePayment->id,
                'transaction_id' => $airtimePayment->transaction_id
            ]);

            if ($validated['send_notification']) {
                $this->sendAirtimeNotification($airtimePayment);
                Log::info('[PAYMENT] Airtime notification sent', [
                    'request_id' => $requestId,
                    'payment_id' => $airtimePayment->id
                ]);
            }
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Airtime payment saved successfully', [
                'request_id' => $requestId,
                'payment_id' => $airtimePayment->id,
                'station_id' => $airtimePayment->station_id,
                'amount' => $airtimePayment->amount,
                'mobile_number' => $airtimePayment->mobile_number,
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($airtimePayment->load('station'), 'Airtime payment saved successfully', 201);
            }

            if ($request->input('action') === 'save_and_new') {
                return redirect()->route('payments.airtime.create')
                    ->with('success', 'Airtime payment saved successfully!')
                    ->with('old_data', $validated);
            }

            return redirect()->route('payments.airtime.index')
                ->with('success', 'Airtime payment saved successfully!');

        } catch (ValidationException $e) {
            Log::warning('[PAYMENT] Airtime payment validation failed', [
                'request_id' => $requestId,
                'errors' => $e->errors(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            return $this->handleValidationError($e, $request);
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error saving airtime payment', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            return $this->handleError($e, $request, 'Error saving airtime payment');
        }
    }

    /**
     * Display airtime payment details.
     */
    public function showAirtimeDetails(Request $request, $id)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Show airtime details', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'is_ajax' => $request->ajax(),
                'has_partial' => $request->has('partial')
            ]);
            
            $payment = AirtimePayment::with('station')->findOrFail($id);
            $payment->updateStatusIfExpired();
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::info('[PAYMENT] Airtime details fetched successfully', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'status' => $payment->status,
                'execution_time_ms' => $executionTime
            ]);

            if (request()->ajax() || request()->has('partial')) {
                Log::debug('[PAYMENT] Returning partial view', ['request_id' => $requestId]);
                $view = view('payments.airtime.partials.details', compact('payment'))->render();
                Log::debug('[PAYMENT] View rendered successfully', ['request_id' => $requestId]);
                return $view;
            }

            return view('payments.airtime.show', compact('payment'));

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error in showAirtimeDetails', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime
            ]);
            return $this->handleError($e, $request, 'Failed to fetch airtime payment');
        }
    }

    /**
     * Renew airtime payment.
     */
    public function renewAirtimePayment(Request $request, $id)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Renewing airtime payment', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'user_id' => Auth::id()
            ]);
            
            $payment = AirtimePayment::findOrFail($id);

            $oldData = [
                'station_id' => $payment->station_id,
                'mobile_number' => $payment->mobile_number,
                'network_provider' => $payment->network_provider,
                'notes' => "Renewal of previous payment #{$payment->id}"
            ];
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::info('[PAYMENT] Airtime renewal prepared', [
                'request_id' => $requestId,
                'original_payment_id' => $id,
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($oldData, 'Renewal data prepared');
            }

            return redirect()->route('payments.airtime.create')
                ->with('old_data', $oldData);

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to prepare renewal', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to prepare renewal');
        }
    }

    /**
     * Delete airtime payment.
     */
    public function destroyAirtimePayment(Request $request, $id)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Deleting airtime payment', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'user_id' => Auth::id()
            ]);
            
            $payment = AirtimePayment::findOrFail($id);

            if ($payment->status != 'expired') {
                Log::warning('[PAYMENT] Attempted to delete non-expired airtime payment', [
                    'request_id' => $requestId,
                    'payment_id' => $id,
                    'status' => $payment->status,
                    'user_id' => Auth::id()
                ]);
                
                if ($this->isApiRequest($request)) {
                    return $this->jsonError('Only expired payments can be deleted', 422);
                }
                return back()->with('error', 'Only expired payments can be deleted.');
            }
            
            $paymentDetails = $payment->toArray();
            $payment->delete();
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Expired airtime payment deleted successfully', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'deleted_payment_details' => $paymentDetails,
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess(null, 'Expired payment deleted successfully');
            }

            return redirect()->route('payments.airtime.index')
                ->with('success', 'Expired payment deleted successfully.');

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error deleting airtime payment', [
                'request_id' => $requestId,
                'payment_id' => $id,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Error deleting airtime payment');
        }
    }

    /**
     * Get due soon payments.
     */
    public function getDueSoonPayments(Request $request)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Fetching due soon payments', [
                'request_id' => $requestId,
                'user_id' => Auth::id()
            ]);
            
            $today = Carbon::today();
            $nextWeek = Carbon::today()->addWeek();

            $dueSoonPayments = InternetPayment::whereBetween('due_date', [$today, $nextWeek])
                ->where('status', 'pending')
                ->with(['station', 'provider'])
                ->orderBy('due_date')
                ->get();

            $formattedPayments = $this->formatDueSoonPayments($dueSoonPayments, $today);
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::info('[PAYMENT] Due soon payments fetched successfully', [
                'request_id' => $requestId,
                'count' => $formattedPayments->count(),
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'payments' => $formattedPayments,
                    'count' => $formattedPayments->count(),
                    'date_range' => [
                        'start' => $today->format('Y-m-d'),
                        'end' => $nextWeek->format('Y-m-d')
                    ]
                ]);
            }

            $dueSoon = $formattedPayments;
            return view('payments.due-soon', compact('dueSoon'));

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to fetch due soon payments', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to fetch due soon payments');
        }
    }

    /**
     * Display upcoming payments.
     */
    public function upcomingPayments(Request $request)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Fetching upcoming payments', [
                'request_id' => $requestId,
                'user_id' => Auth::id()
            ]);
            
            $today = Carbon::today();
            $nextWeek = Carbon::today()->addWeek();

            $upcomingInternet = $this->getUpcomingInternetPayments($today, $nextWeek);
            $upcomingAirtime = $this->getUpcomingAirtimePayments($today, $nextWeek);
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::info('[PAYMENT] Upcoming payments fetched successfully', [
                'request_id' => $requestId,
                'internet_count' => $upcomingInternet->count(),
                'airtime_count' => $upcomingAirtime->count(),
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'internet' => $this->formatUpcomingInternetPayments($upcomingInternet, $today),
                    'airtime' => $this->formatUpcomingAirtimePayments($upcomingAirtime, $today),
                    'internet_count' => $upcomingInternet->count(),
                    'airtime_count' => $upcomingAirtime->count()
                ]);
            }

            return view('payments.upcoming', compact('upcomingInternet', 'upcomingAirtime'));

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to fetch upcoming payments', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to fetch upcoming payments');
        }
    }

    /**
     * Display overdue payments.
     */
    public function overduePayments(Request $request)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Fetching overdue payments', [
                'request_id' => $requestId,
                'user_id' => Auth::id()
            ]);
            
            $today = Carbon::today();

            $overdueInternet = $this->getOverdueInternetPayments($today);
            $overdueAirtime = $this->getOverdueAirtimePayments($today);
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::info('[PAYMENT] Overdue payments fetched successfully', [
                'request_id' => $requestId,
                'internet_count' => $overdueInternet->count(),
                'airtime_count' => $overdueAirtime->count(),
                'internet_total' => (float) $overdueInternet->sum('total_due'),
                'airtime_total' => (float) $overdueAirtime->sum('amount'),
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'internet' => $this->formatOverdueInternetPayments($overdueInternet, $today),
                    'airtime' => $this->formatOverdueAirtimePayments($overdueAirtime, $today),
                    'internet_count' => $overdueInternet->count(),
                    'airtime_count' => $overdueAirtime->count(),
                    'internet_total' => (float) $overdueInternet->sum('total_due'),
                    'airtime_total' => (float) $overdueAirtime->sum('amount')
                ]);
            }

            return view('payments.overdue', compact('overdueInternet', 'overdueAirtime'));

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to fetch overdue payments', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to fetch overdue payments');
        }
    }

    /**
     * Display a listing of payment schedules.
     */
    public function indexSchedules(Request $request)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Fetching payment schedules', [
                'request_id' => $requestId,
                'user_id' => Auth::id()
            ]);
            
            $schedules = PaymentSchedule::with('station')
                ->orderBy('scheduled_date')
                ->paginate(20);
                
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::info('[PAYMENT] Payment schedules fetched successfully', [
                'request_id' => $requestId,
                'total_count' => $schedules->total(),
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'schedules' => $schedules,
                    'total' => $schedules->total(),
                    'per_page' => $schedules->perPage()
                ]);
            }

            return view('payments.schedules.index', compact('schedules'));

        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to fetch schedules', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to fetch schedules');
        }
    }

    /**
     * Show form for creating payment schedule.
     */
    public function createSchedule(Request $request)
    {
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Loading schedule create form', [
                'request_id' => $requestId,
                'user_id' => Auth::id()
            ]);
            
            $stations = Station::all();

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'stations' => $stations,
                    'form_fields' => [
                        'station_id', 'payment_type', 'scheduled_date',
                        'scheduled_amount', 'frequency', 'is_recurring',
                        'auto_pay', 'description'
                    ]
                ]);
            }

            return view('payments.schedules.create', compact('stations'));

        } catch (\Exception $e) {
            Log::error('[PAYMENT] Failed to load schedule create form', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to load create form');
        }
    }

    /**
     * Store payment schedule.
     */
    public function storeSchedule(Request $request)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Storing payment schedule', [
                'request_id' => $requestId,
                'user_id' => Auth::id(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            
            $validated = $request->validate([
                'station_id' => 'required|exists:stations,station_id',
                'payment_type' => 'required|in:internet,airtime',
                'scheduled_date' => 'required|date',
                'scheduled_amount' => 'required|numeric|min:0',
                'frequency' => 'required|in:monthly,quarterly,yearly,custom',
                'is_recurring' => 'boolean',
                'auto_pay' => 'boolean',
                'description' => 'nullable|string'
            ]);

            $schedule = PaymentSchedule::create($validated);
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Payment schedule created successfully', [
                'request_id' => $requestId,
                'schedule_id' => $schedule->id,
                'station_id' => $schedule->station_id,
                'payment_type' => $schedule->payment_type,
                'amount' => $schedule->scheduled_amount,
                'execution_time_ms' => $executionTime
            ]);

            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($schedule->load('station'), 'Payment schedule created successfully', 201);
            }

            return redirect()->route('payments.schedules.index')
                ->with('success', 'Payment schedule created successfully!');

        } catch (ValidationException $e) {
            Log::warning('[PAYMENT] Schedule validation failed', [
                'request_id' => $requestId,
                'errors' => $e->errors(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            return $this->handleValidationError($e, $request);
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error creating schedule', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Error creating schedule');
        }
    }

    /**
     * Display station details with all payment information.
     */
    public function showStationDetails(Request $request, $stationId)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Fetching station details with payments', [
                'request_id' => $requestId,
                'station_id' => $stationId,
                'user_id' => Auth::id(),
                'is_api' => $this->isApiRequest($request)
            ]);
            
            $station = Station::with([
                'manager',
                'employees' => function($query) {
                    $query->orderBy('first_name');
                },
                'internetPayments' => function($query) {
                    $query->with('provider')->orderBy('due_date', 'desc')->limit(10);
                },
                'airtimePayments' => function($query) {
                    $query->orderBy('topup_date', 'desc')->limit(10);
                },
                'payments' => function($query) {
                    $query->orderBy('due_date', 'desc')->limit(10);
                },
                'paymentSchedules' => function($query) {
                    $query->where('scheduled_date', '>=', now())->orderBy('scheduled_date');
                },
                'serviceProviders' => function($query) {
                    $query->wherePivot('status', 'active');
                },
                'vendors' => function($query) {
                    $query->wherePivot('status', 'active');
                }
            ])->findOrFail($stationId);
            
            $stats = [
                'total_employees' => $station->employees->count(),
                'active_employees' => $station->employees->where('status', 'active')->count(),
                'total_internet_paid' => $station->internetPayments->where('status', 'paid')->sum('amount'),
                'total_airtime_paid' => $station->airtimePayments->sum('amount'),
                'pending_payments' => $station->internetPayments->where('status', 'pending')->sum('amount'),
                'recent_payments_count' => $station->internetPayments->take(5)->count() + $station->airtimePayments->take(5)->count()
            ];
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Station details fetched successfully', [
                'request_id' => $requestId,
                'station_id' => $stationId,
                'station_name' => $station->name,
                'stats' => $stats,
                'execution_time_ms' => $executionTime
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'station' => $station,
                    'statistics' => $stats,
                    'contact_person' => $station->contact_person_name ?? null,
                    'contact_email' => $station->contact_email_address ?? null,
                    'contact_phone' => $station->contact_phone_number ?? null,
                    'location' => $station->station_location ?? null
                ]);
            }
            
            return view('payments.station-details', compact('station', 'stats'));
            
        } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::warning('[PAYMENT] Station not found', [
                'request_id' => $requestId,
                'station_id' => $stationId,
                'error_message' => $e->getMessage(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonError('Station not found', 404);
            }
            
            abort(404, 'Station not found');
            
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to fetch station details', [
                'request_id' => $requestId,
                'station_id' => $stationId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to fetch station details');
        }
    }

    /**
     * Get employee details via AJAX for the modal.
     */
    public function getEmployeeDetails(Request $request, $employeeId)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Fetching employee details', [
                'request_id' => $requestId,
                'employee_id' => $employeeId,
                'user_id' => Auth::id(),
                'is_api' => $this->isApiRequest($request)
            ]);
            
            $employee = Employee::findOrFail($employeeId);
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Employee details fetched successfully', [
                'request_id' => $requestId,
                'employee_id' => $employeeId,
                'employee_name' => $employee->first_name . ' ' . $employee->last_name,
                'execution_time_ms' => $executionTime
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($employee);
            }
            
            return response()->json([
                'success' => true,
                'data' => $employee
            ]);
            
        } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
            Log::warning('[PAYMENT] Employee not found', [
                'request_id' => $requestId,
                'employee_id' => $employeeId,
                'error_message' => $e->getMessage(),
                'user_id' => Auth::id()
            ]);
            
            return response()->json([
                'success' => false,
                'message' => 'Employee not found'
            ], 404);
            
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to fetch employee details', [
                'request_id' => $requestId,
                'employee_id' => $employeeId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            
            return response()->json([
                'success' => false,
                'message' => 'Failed to fetch employee details: ' . $e->getMessage()
            ], 500);
        }
    }

    /**
     * Display payment schedule details.
     */
    public function showSchedule(Request $request, $id)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Showing payment schedule', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'user_id' => Auth::id(),
                'is_api' => $this->isApiRequest($request)
            ]);
            
            $schedule = PaymentSchedule::with('station')->findOrFail($id);
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Payment schedule fetched successfully', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'station_id' => $schedule->station_id,
                'payment_type' => $schedule->payment_type,
                'amount' => $schedule->scheduled_amount,
                'execution_time_ms' => $executionTime
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($schedule);
            }
            
            return view('payments.schedules.show', compact('schedule'));
            
        } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
            Log::warning('[PAYMENT] Schedule not found', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'user_id' => Auth::id()
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonError('Schedule not found', 404);
            }
            
            abort(404, 'Schedule not found');
            
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to fetch schedule', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to fetch schedule');
        }
    }

    /**
     * Show form for editing payment schedule.
     */
    public function editSchedule(Request $request, $id)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Loading schedule edit form', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'user_id' => Auth::id(),
                'is_api' => $this->isApiRequest($request)
            ]);
            
            $schedule = PaymentSchedule::findOrFail($id);
            $stations = Station::all();
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Schedule edit form loaded', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'execution_time_ms' => $executionTime
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'schedule' => $schedule,
                    'stations' => $stations
                ]);
            }
            
            return view('payments.schedules.edit', compact('schedule', 'stations'));
            
        } catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
            Log::warning('[PAYMENT] Schedule not found for edit', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'user_id' => Auth::id()
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonError('Schedule not found', 404);
            }
            
            abort(404, 'Schedule not found');
            
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to load edit form', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to load edit form');
        }
    }

    /**
     * Update payment schedule.
     */
    public function updateSchedule(Request $request, $id)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Updating payment schedule', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'user_id' => Auth::id(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            
            $schedule = PaymentSchedule::findOrFail($id);
            $oldData = $schedule->toArray();
            
            $validated = $request->validate([
                'station_id' => 'required|exists:stations,station_id',
                'payment_type' => 'required|in:internet,airtime',
                'scheduled_date' => 'required|date',
                'scheduled_amount' => 'required|numeric|min:0',
                'frequency' => 'required|in:monthly,quarterly,yearly,custom',
                'is_recurring' => 'boolean',
                'auto_pay' => 'boolean',
                'description' => 'nullable|string'
            ]);
            
            $schedule->update($validated);
            $changes = $schedule->getChanges();
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Schedule updated successfully', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'changes' => $changes,
                'old_data' => $oldData,
                'execution_time_ms' => $executionTime
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($schedule->load('station'), 'Schedule updated successfully');
            }
            
            return redirect()->route('payments.schedules.index')
                ->with('success', 'Schedule updated successfully!');
                
        } catch (ValidationException $e) {
            Log::warning('[PAYMENT] Schedule update validation failed', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'errors' => $e->errors(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            return $this->handleValidationError($e, $request);
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error updating schedule', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Error updating schedule');
        }
    }

    /**
     * Delete payment schedule.
     */
    public function destroySchedule(Request $request, $id)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Deleting payment schedule', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'user_id' => Auth::id()
            ]);
            
            $schedule = PaymentSchedule::findOrFail($id);
            $scheduleDetails = $schedule->toArray();
            
            $schedule->delete();
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Schedule deleted successfully', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'deleted_schedule_details' => $scheduleDetails,
                'execution_time_ms' => $executionTime
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess(null, 'Schedule deleted successfully');
            }
            
            return redirect()->route('payments.schedules.index')
                ->with('success', 'Schedule deleted successfully!');
                
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error deleting schedule', [
                'request_id' => $requestId,
                'schedule_id' => $id,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Error deleting schedule');
        }
    }

    /**
     * Send reminder for a specific payment.
     */
    public function sendReminder(Request $request, $paymentId)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Sending payment reminder', [
                'request_id' => $requestId,
                'payment_id' => $paymentId,
                'user_id' => Auth::id()
            ]);
            
            $payment = InternetPayment::with(['station', 'provider'])->findOrFail($paymentId);
            
            $dueDate = Carbon::parse($payment->due_date);
            $daysUntilDue = Carbon::today()->diffInDays($dueDate, false);
            
            if ($daysUntilDue < 0) {
                $reminderType = 'overdue';
            } elseif ($daysUntilDue == 0) {
                $reminderType = 'due_today';
            } elseif ($daysUntilDue <= 1) {
                $reminderType = '1_day';
            } elseif ($daysUntilDue <= 3) {
                $reminderType = '3_days';
            } else {
                $reminderType = '1_week';
            }
            
            Log::debug('[PAYMENT] Reminder details', [
                'request_id' => $requestId,
                'payment_id' => $paymentId,
                'days_until_due' => $daysUntilDue,
                'reminder_type' => $reminderType
            ]);
            
            $sent = $this->reminderService->sendReminder($payment, $reminderType, $daysUntilDue);
            
            if ($sent) {
                $this->reminderService->logReminder($payment, $reminderType, $daysUntilDue);
            }
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::info('[PAYMENT] Reminder sending completed', [
                'request_id' => $requestId,
                'payment_id' => $paymentId,
                'sent' => $sent,
                'reminder_type' => $reminderType,
                'execution_time_ms' => $executionTime
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'payment_id' => $payment->id,
                    'reminder_sent' => $sent,
                    'reminder_type' => $reminderType,
                    'sent_at' => now()
                ], $sent ? 'Reminder sent successfully' : 'Failed to send reminder');
            }
            
            $message = $sent ? 'Reminder sent successfully!' : 'Failed to send reminder';
            $type = $sent ? 'success' : 'error';
            
            return redirect()->back()->with($type, $message);
            
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to send reminder', [
                'request_id' => $requestId,
                'payment_id' => $paymentId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to send reminder');
        }
    }

    /**
     * Show bulk reminder form.
     */
    public function showBulkReminderForm(Request $request)
    {
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Loading bulk reminder form', [
                'request_id' => $requestId,
                'user_id' => Auth::id()
            ]);
            
            $this->authorize('sendBulkReminders', Payment::class);
            
            $stations = Station::orderBy('name')->get();
            $reminderTypes = [
                'upcoming' => 'Upcoming Payments (7 days)',
                'due_soon' => 'Due Soon (3 days)',
                'due_today' => 'Due Today',
                'overdue' => 'Overdue Payments',
                'custom' => 'Custom Date Range'
            ];
            
            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'stations' => $stations,
                    'reminder_types' => $reminderTypes
                ]);
            }
            
            return view('payments.bulk-reminder', compact('stations', 'reminderTypes'));
            
        } catch (\Exception $e) {
            Log::error('[PAYMENT] Failed to load bulk reminder form', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to load bulk reminder form');
        }
    }

    /**
     * Send bulk reminders.
     */
    public function sendBulkReminders(Request $request)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Sending bulk reminders', [
                'request_id' => $requestId,
                'user_id' => Auth::id(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            
            $validated = $request->validate([
                'reminder_type' => 'required|in:upcoming,due_soon,due_today,overdue,custom',
                'station_id' => 'nullable|exists:stations,station_id',
                'start_date' => 'required_if:reminder_type,custom|nullable|date',
                'end_date' => 'required_if:reminder_type,custom|nullable|date|after_or_equal:start_date',
                'payment_type' => 'nullable|in:internet,airtime,both'
            ]);
            
            $payments = $this->buildReminderQuery($validated)->get();
            
            if ($payments->isEmpty()) {
                Log::warning('[PAYMENT] No payments found for bulk reminders', [
                    'request_id' => $requestId,
                    'criteria' => $validated
                ]);
                
                if ($this->isApiRequest($request)) {
                    return $this->jsonError('No payments found matching the criteria', 404);
                }
                return back()->with('warning', 'No payments found matching the criteria');
            }
            
            Log::debug('[PAYMENT] Found payments for bulk reminders', [
                'request_id' => $requestId,
                'count' => $payments->count()
            ]);
            
            $reminderType = $validated['reminder_type'];
            $results = $this->reminderService->sendBulkEmailReminders($payments, $reminderType);
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            $message = "Bulk reminders sent: {$results['sent']} successful, {$results['failed']} failed";
            
            Log::info('[PAYMENT] Bulk reminders completed', [
                'request_id' => $requestId,
                'total_payments' => $payments->count(),
                'sent_count' => $results['sent'],
                'failed_count' => $results['failed'],
                'errors' => $results['errors'] ?? [],
                'execution_time_ms' => $executionTime
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($results, $message);
            }
            
            if ($results['failed'] > 0) {
                return back()->with('warning', $message . '. Errors: ' . implode(', ', array_slice($results['errors'], 0, 3)));
            }
            
            return redirect()->route('payments.index')->with('success', $message);
            
        } catch (ValidationException $e) {
            Log::warning('[PAYMENT] Bulk reminder validation failed', [
                'request_id' => $requestId,
                'errors' => $e->errors(),
                'request_data' => $this->sanitizeLogData($request->all())
            ]);
            return $this->handleValidationError($e, $request);
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to send bulk reminders', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to send bulk reminders');
        }
    }

    /**
     * Get reminder schedule status.
     */
    public function getReminderScheduleStatus(Request $request)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Fetching reminder schedule status', [
                'request_id' => $requestId,
                'user_id' => Auth::id()
            ]);
            
            $schedule = [
                'daily_reminders' => '06:00 AM',
                'urgent_reminders' => '09:00 AM', 
                'overdue_reminders' => '06:00 PM',
                'next_run' => $this->getNextScheduledRun(),
                'last_run' => NotificationLog::max('sent_at'),
                'enabled' => config('payment.reminders.enabled', true)
            ];
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::info('[PAYMENT] Reminder schedule status fetched', [
                'request_id' => $requestId,
                'execution_time_ms' => $executionTime
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess($schedule);
            }
            
            return view('payments.schedule-status', compact('schedule'));
            
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Failed to fetch schedule status', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            return $this->handleError($e, $request, 'Failed to fetch schedule status');
        }
    }

    /**
     * Check airtime expiries.
     */
    public function checkAirtimeExpiries(Request $request)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Checking airtime expiries', [
                'request_id' => $requestId,
                'user_id' => Auth::id()
            ]);
            
            $today = Carbon::today();
            
            $expiringSoon = AirtimePayment::where('status', 'active')
                ->whereBetween('expected_expiry', [$today, $today->copy()->addDays(3)])
                ->count();
                
            $expired = AirtimePayment::where('status', 'active')
                ->where('expected_expiry', '<', $today)
                ->update(['status' => 'expired']);
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            $message = "Found {$expiringSoon} payments expiring soon. Updated {$expired} expired payments.";
            
            Log::info('[PAYMENT] Airtime expiry check completed', [
                'request_id' => $requestId,
                'expiring_soon_count' => $expiringSoon,
                'expired_updated_count' => $expired,
                'execution_time_ms' => $executionTime
            ]);
            
            if ($request->ajax()) {
                return response()->json([
                    'success' => true,
                    'message' => $message,
                    'expiring_soon' => $expiringSoon,
                    'expired_updated' => $expired
                ]);
            }
            
            return redirect()->back()->with('success', $message);
            
        } catch (\Exception $e) {
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            Log::error('[PAYMENT] Error checking airtime expiries', [
                'request_id' => $requestId,
                'error_message' => $e->getMessage(),
                'error_code' => $e->getCode(),
                'file' => $e->getFile(),
                'line' => $e->getLine(),
                'trace' => $e->getTraceAsString(),
                'execution_time_ms' => $executionTime,
                'user_id' => Auth::id()
            ]);
            
            if ($request->ajax()) {
                return response()->json([
                    'success' => false,
                    'message' => 'Failed to check expiries: ' . $e->getMessage()
                ], 500);
            }
            return redirect()->back()->with('error', 'Failed to check expiries');
        }
    }

    // ==================== PRIVATE HELPER METHODS ====================

    /**
     * Generate unique request ID for tracing.
     */
    private function generateRequestId(): string
    {
        return uniqid('req_', true) . '_' . bin2hex(random_bytes(8));
    }

    /**
     * Sanitize sensitive data before logging.
     */
    private function sanitizeLogData(array $data): array
    {
        $sensitiveFields = ['password', 'token', 'api_key', 'secret', 'mpesa_receipt', 'transaction_id'];
        
        foreach ($sensitiveFields as $field) {
            if (isset($data[$field])) {
                $data[$field] = '***REDACTED***';
            }
        }
        
        return $data;
    }

    /**
     * Handle errors consistently across the controller.
     */
    private function handleError(\Exception $e, Request $request, string $message, array $context = [])
    {
        if ($this->isApiRequest($request)) {
            $statusCode = 500;
            if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
                $statusCode = 404;
            } elseif ($e instanceof \Illuminate\Auth\Access\AuthorizationException) {
                $statusCode = 403;
            } elseif ($e instanceof \Symfony\Component\HttpKernel\Exception\HttpException) {
                $statusCode = $e->getStatusCode();
            }
            
            return response()->json([
                'success' => false,
                'message' => $message . ': ' . $e->getMessage(),
                'error_code' => $statusCode
            ], $statusCode);
        }

        if ($request->isMethod('post') || $request->isMethod('put') || $request->isMethod('patch') || $request->isMethod('delete')) {
            return redirect()->back()
                ->withInput()
                ->with('error', $message . ': ' . $e->getMessage());
        }

        if ($e instanceof \Illuminate\Database\Eloquent\ModelNotFoundException) {
            abort(404, $message);
        }
        
        throw $e;
    }

    /**
     * Handle validation errors consistently.
     */
    private function handleValidationError(ValidationException $e, Request $request)
    {
        Log::warning('[PAYMENT] Validation failed', [
            'errors' => $e->errors(),
            'user_id' => Auth::id(),
            'url' => $request->fullUrl(),
            'method' => $request->method()
        ]);

        if ($this->isApiRequest($request)) {
            return response()->json([
                'success' => false,
                'message' => 'Validation failed',
                'errors' => $e->errors()
            ], 422);
        }
        
        throw $e;
    }

    /**
     * Check if request is API request.
     */
    private function isApiRequest(Request $request): bool
    {
        return $request->wantsJson() || $request->ajax() || $request->get('format') === 'json';
    }

    /**
     * Return JSON success response.
     */
    private function jsonSuccess($data = null, string $message = '', int $status = 200): JsonResponse
    {
        $response = ['success' => true];

        if ($message) {
            $response['message'] = $message;
        }

        if ($data !== null) {
            $response['data'] = $data;
        }

        return response()->json($response, $status);
    }

    /**
     * Return JSON error response.
     */
    private function jsonError(string $message, int $status = 500, array $errors = []): JsonResponse
    {
        $response = [
            'success' => false,
            'message' => $message
        ];

        if (!empty($errors)) {
            $response['errors'] = $errors;
        }

        return response()->json($response, $status);
    }

    /**
     * Validate payment store request.
     */
    private function validatePaymentStore(Request $request): array
    {
        $rules = [
            'station_id' => 'required|exists:stations,station_id',
            'vendor_id' => 'nullable|exists:vendors,id',
            'title' => 'required|string|max:255',
            'description' => 'nullable|string',
            'amount' => 'required|numeric|min:0',
            'due_date' => 'required|date',
            'status' => 'required|in:pending,approved,paid,rejected',
            'type' => 'required|in:utility,service,product,other',
            'attachment' => 'nullable|file|mimes:pdf,jpg,png|max:2048',
            'is_recurring' => 'boolean',
        ];
        
        $isRecurring = $request->input('is_recurring');
        if ($isRecurring === 'on' || $isRecurring === '1' || $isRecurring === true) {
            $rules['is_recurring'] = 'boolean';
            $rules['recurrence'] = 'required|in:weekly,monthly,yearly';
            $rules['recurrence_ends_at'] = 'nullable|date|after:due_date';
        }
        
        return $request->validate($rules);
    }

    /**
     * Validate payment update request.
     */
    private function validatePaymentUpdate(Request $request): array
    {
        $typesString = 'utility,service,product,rent,salary,other';
        
        return $request->validate([
            'station_id' => 'required|exists:stations,station_id',
            'vendor_id' => 'nullable|exists:vendors,id',
            'title' => 'required|string|max:255',
            'description' => 'nullable|string',
            'amount' => 'required|numeric|min:0',
            'due_date' => 'required|date',
            'status' => 'required|in:pending,approved,paid,rejected',
            'type' => 'required|in:' . $typesString,
            'attachment' => 'nullable|file|mimes:pdf,jpg,png|max:2048',
            'is_recurring' => 'boolean',
            'recurrence' => 'required_if:is_recurring,true|in:weekly,monthly,yearly',
            'recurrence_ends_at' => 'nullable|date|after:due_date'
        ]);
    }

    /**
     * Handle payment attachment upload.
     */
    private function handlePaymentAttachment(Request $request, array $data, $payment = null): array
    {
        if ($request->hasFile('attachment')) {
            if ($payment && $payment->attachment_path) {
                Storage::delete($payment->attachment_path);
                Log::debug('[PAYMENT] Deleted old attachment', ['path' => $payment->attachment_path]);
            }
            $data['attachment_path'] = $request->file('attachment')->store('payments/attachments');
            Log::debug('[PAYMENT] New attachment uploaded', ['path' => $data['attachment_path']]);
        }

        return $data;
    }

    /**
     * Set payment approval details if applicable.
     */
    private function setPaymentApproval(array $data): array
    {
        if (Auth::user() && Auth::user()->role === 'admin' && isset($data['status']) && $data['status'] === 'approved') {
            $data['approved_by'] = Auth::id();
            $data['approved_at'] = now();
            Log::debug('[PAYMENT] Payment auto-approved by admin', ['admin_id' => Auth::id()]);
        }

        return $data;
    }

    /**
     * Build internet payment query with filters.
     */
    private function buildInternetPaymentQuery(Request $request): Builder
    {
        $query = InternetPayment::with(['station', 'provider']);

        if ($request->filled('status')) {
            $query->where('status', $request->status);
        }

        if ($request->filled('vendor_id')) {
            $query->where('vendor_id', $request->vendor_id);
        }

        if ($request->filled('station_id')) {
            $query->where('station_id', $request->station_id);
        }

        if ($request->filled('month')) {
            $month = Carbon::parse($request->month . '-01')->format('Y-m-d');
            $query->where('billing_month', $month);
        }

        if ($request->filled('search')) {
            $query->where('account_number', 'like', '%' . $request->search . '%');
        }

        return $query;
    }

    /**
     * Validate internet payment store request.
     */
    private function validateInternetPaymentStore(Request $request): array
    {
        return $request->validate([
            'station_id' => 'required|exists:stations,station_id',
            'vendor_id' => 'required|exists:internet_providers,vendor_id',
            'account_number' => 'required|string|max:255',
            'amount' => 'required|numeric|min:0',
            'previous_balance' => 'nullable|numeric|min:0',
            'billing_month' => 'required|date',
            'due_date' => 'required|date',
            'payment_date' => 'nullable|date',
            'mpesa_receipt' => 'nullable|string|max:255',
            'transaction_id' => 'nullable|string|max:255',
            'invoice_notes' => 'nullable|string',
            'payment_method' => 'nullable|in:M-Pesa,Bank Transfer,Cash,Cheque',
            'status' => 'required|in:pending,paid,overdue,cancelled',
            'create_schedule' => 'nullable|boolean'
        ]);
    }

    /**
     * Validate internet payment update request.
     */
    private function validateInternetPaymentUpdate(Request $request, $id): array
    {
        return $request->validate([
            'station_id' => 'required|exists:stations,station_id',
            'vendor_id' => 'required|exists:internet_providers,vendor_id',
            'account_number' => 'required|string|max:255|unique:internet_payments,account_number,' . $id,
            'amount' => 'required|numeric|min:0',
            'previous_balance' => 'nullable|numeric|min:0',
            'billing_month' => 'required|date',
            'due_date' => 'required|date',
            'payment_date' => 'nullable|date',
            'status' => 'required|in:pending,paid,overdue,cancelled',
            'mpesa_receipt' => 'nullable|string|max:255',
            'transaction_id' => 'nullable|string|max:255',
            'invoice_notes' => 'nullable|string',
            'payment_method' => 'nullable|in:M-Pesa,Bank Transfer,Cash,Cheque'
        ]);
    }

    /**
     * Create internet payment record.
     */
    private function createInternetPaymentRecord(array $data): InternetPayment
    {
        $paymentData = [
            'station_id' => $data['station_id'],
            'vendor_id' => $data['vendor_id'],
            'account_number' => $data['account_number'],
            'amount' => (float) $data['amount'],
            'previous_balance' => isset($data['previous_balance']) ? (float) $data['previous_balance'] : 0.00,
            'billing_month' => Carbon::parse($data['billing_month'])->format('Y-m-d'),
            'due_date' => Carbon::parse($data['due_date'])->format('Y-m-d'),
            'payment_date' => isset($data['payment_date']) && $data['payment_date'] ? Carbon::parse($data['payment_date'])->format('Y-m-d') : null,
            'mpesa_receipt' => $data['mpesa_receipt'] ?? null,
            'transaction_id' => $data['transaction_id'] ?? null,
            'status' => $data['status'],
            'invoice_notes' => $data['invoice_notes'] ?? null,
            'payment_method' => $data['payment_method'] ?? null
        ];

        $paymentData = array_filter($paymentData, function($value) {
            return $value !== null;
        });

        return InternetPayment::create($paymentData);
    }

    /**
     * Prepare internet payment data for update.
     */
    private function prepareInternetPaymentData(array $data): array
    {
        $preparedData = [
            'station_id' => $data['station_id'],
            'vendor_id' => $data['vendor_id'],
            'account_number' => $data['account_number'],
            'amount' => (float) $data['amount'],
            'previous_balance' => isset($data['previous_balance']) ? (float) $data['previous_balance'] : 0.00,
            'billing_month' => Carbon::parse($data['billing_month'])->format('Y-m-d'),
            'due_date' => Carbon::parse($data['due_date'])->format('Y-m-d'),
            'payment_date' => isset($data['payment_date']) && $data['payment_date'] ? Carbon::parse($data['payment_date'])->format('Y-m-d') : null,
            'status' => $data['status'],
            'mpesa_receipt' => $data['mpesa_receipt'] ?? null,
            'transaction_id' => $data['transaction_id'] ?? null,
            'invoice_notes' => $data['invoice_notes'] ?? null,
            'payment_method' => $data['payment_method'] ?? null
        ];

        return array_filter($preparedData, function($value) {
            return $value !== null;
        });
    }

    /**
     * Create payment schedule from internet payment.
     */
    private function createPaymentSchedule(array $data): void
    {
        $provider = InternetProvider::find($data['vendor_id']);

        if ($provider) {
            PaymentSchedule::create([
                'station_id' => $data['station_id'],
                'payment_type' => 'internet',
                'scheduled_date' => Carbon::parse($data['due_date'])->subDays(7)->format('Y-m-d'),
                'scheduled_amount' => (float) $data['amount'],
                'frequency' => 'monthly',
                'is_recurring' => true,
                'description' => "Monthly internet payment to {$provider->name} for account {$data['account_number']}",
                'vendor_id' => $provider->vendor_id
            ]);
            
            Log::info('[PAYMENT] Payment schedule created from internet payment', [
                'station_id' => $data['station_id'],
                'provider_id' => $data['vendor_id'],
                'amount' => $data['amount']
            ]);
        }
    }

    /**
     * Build airtime payment query with filters.
     */
    private function buildAirtimePaymentQuery(Request $request): Builder
    {
        $query = AirtimePayment::with('station');

        if ($request->filled('station_id')) {
            $query->where('station_id', $request->station_id);
        }

        if ($request->filled('network_provider')) {
            $query->where('network_provider', $request->network_provider);
        }

        if ($request->filled('status')) {
            $query->where('status', $request->status);
        }

        if ($request->filled('month')) {
            $month = $request->month;
            $query->whereYear('topup_date', substr($month, 0, 4))
                ->whereMonth('topup_date', substr($month, 5, 2));
        }

        if ($request->filled('search')) {
            $query->where('mobile_number', 'like', '%' . $request->search . '%');
        }

        return $query;
    }

    /**
     * Validate airtime payment store request.
     */
    private function validateAirtimePaymentStore(Request $request): array
    {
        return $request->validate([
            'station_id' => 'required|exists:stations,station_id',
            'mobile_number' => 'required|string|max:15',
            'amount' => 'required|numeric|min:0',
            'topup_date' => 'required|date',
            'network_provider' => 'required|string|in:Safaricom,Airtel,Telkom,Faiba',
            'transaction_id' => 'nullable|string|unique:airtime_payments',
            'expected_expiry' => 'nullable|date',
            'payment_method' => 'nullable|string',
            'notes' => 'nullable|string',
            'auto_renew' => 'nullable|boolean',
            'send_notification' => 'nullable|boolean',
        ]);
    }

    /**
     * Prepare airtime payment data.
     */
    private function prepareAirtimePaymentData(array $data): array
    {
        if (empty($data['transaction_id'])) {
            $data['transaction_id'] = 'TXN' . date('YmdHis') . rand(100, 999);
        }

        if (empty($data['expected_expiry'])) {
            $data['expected_expiry'] = Carbon::parse($data['topup_date'])->addDays(30);
        }

        $data['auto_renew'] = $data['auto_renew'] ?? false;
        $data['send_notification'] = $data['send_notification'] ?? false;

        return $data;
    }

    /**
     * Get airtime statistics.
     */
    private function getAirtimeStats(): array
    {
        return [
            'active_topups' => AirtimePayment::where('status', 'active')->count(),
            'expiring_soon' => AirtimePayment::where('status', 'active')
                ->where('expected_expiry', '<=', Carbon::now()->addDays(3))
                ->count(),
            'monthly_total' => AirtimePayment::whereMonth('topup_date', Carbon::now()->month)
                ->sum('amount')
        ];
    }

    /**
     * Get recent airtime payments.
     */
    private function getRecentAirtimePayments(): Collection
    {
        return AirtimePayment::with('station')
            ->orderBy('created_at', 'desc')
            ->limit(5)
            ->get();
    }

    /**
     * Get recent mobile numbers for autocomplete.
     */
    private function getRecentMobileNumbers(): Collection
    {
        return AirtimePayment::select('mobile_number')
            ->selectRaw('MAX(created_at) as latest_created_at')
            ->groupBy('mobile_number')
            ->orderBy('latest_created_at', 'desc')
            ->limit(10)
            ->pluck('mobile_number');
    }

    /**
     * Send airtime notification (to be implemented).
     */
    private function sendAirtimeNotification(AirtimePayment $payment): void
    {
        Log::info('[PAYMENT] Airtime notification would be sent for payment: ' . $payment->id);
        // TODO: Implement actual notification logic (SMS, email, etc.)
    }

    /**
     * Update expired airtime payments.
     */
    private function updateExpiredAirtimePayments(): void
    {
        $today = Carbon::today()->format('Y-m-d');

        $updated = AirtimePayment::where('status', 'active')
            ->where('expected_expiry', '<', $today)
            ->update(['status' => 'expired']);

        if ($updated > 0) {
            Log::info("[PAYMENT] Updated {$updated} airtime payments to expired status.");
        }
    }

    /**
     * Format due soon payments for display.
     */
    private function formatDueSoonPayments(Collection $payments, Carbon $today): Collection
    {
        return $payments->map(function (InternetPayment $payment) use ($today) {
            $dueDate = $payment->due_date instanceof Carbon ? $payment->due_date : Carbon::parse($payment->due_date);

            return [
                'id' => $payment->id,
                'station' => $payment->station->name,
                'station_id' => $payment->station_id,
                'provider' => $payment->provider->name,
                'provider_id' => $payment->vendor_id,
                'account_number' => $payment->account_number,
                'amount_due' => (float) $payment->total_due,
                'due_date' => $dueDate->format('Y-m-d'),
                'formatted_due_date' => $dueDate->format('d/m/Y'),
                'contact_person' => $payment->station->contact_person ?? null,
                'contact_phone' => $payment->station->contact_phone ?? null,
                'contact_email' => $payment->station->contact_email ?? null,
                'paybill_number' => $payment->provider->paybill_number ?? null,
                'support_contact' => $payment->provider->support_contact ?? null,
                'is_due_today' => $dueDate->isToday(),
                'days_until_due' => $today->diffInDays($dueDate),
                'reminder_message' => $this->generateReminderMessage($payment)
            ];
        });
    }

    /**
     * Format upcoming internet payments.
     */
    private function formatUpcomingInternetPayments(Collection $payments, Carbon $today): Collection
    {
        return $payments->map(function (InternetPayment $payment) use ($today) {
            $dueDate = Carbon::parse($payment->due_date);

            return [
                'id' => $payment->id,
                'station' => $payment->station->name,
                'provider' => $payment->provider->name,
                'account_number' => $payment->account_number,
                'amount_due' => (float) $payment->total_due,
                'due_date' => $dueDate->format('Y-m-d'),
                'formatted_due_date' => $dueDate->format('d/m/Y'),
                'days_until_due' => $today->diffInDays($dueDate),
                'payment_type' => 'internet'
            ];
        });
    }

    /**
     * Format upcoming airtime payments.
     */
    private function formatUpcomingAirtimePayments(Collection $payments, Carbon $today): Collection
    {
        return $payments->map(function (AirtimePayment $payment) use ($today) {
            $expiryDate = Carbon::parse($payment->expected_expiry);

            return [
                'id' => $payment->id,
                'station' => $payment->station->name,
                'mobile_number' => $payment->mobile_number,
                'amount' => (float) $payment->amount,
                'expiry_date' => $expiryDate->format('Y-m-d'),
                'formatted_expiry_date' => $expiryDate->format('d/m/Y'),
                'days_until_expiry' => $today->diffInDays($expiryDate),
                'network_provider' => $payment->network_provider,
                'payment_type' => 'airtime'
            ];
        });
    }

    /**
     * Format overdue internet payments.
     */
    private function formatOverdueInternetPayments(Collection $payments, Carbon $today): Collection
    {
        return $payments->map(function (InternetPayment $payment) use ($today) {
            $dueDate = Carbon::parse($payment->due_date);

            return [
                'id' => $payment->id,
                'station' => $payment->station->name,
                'provider' => $payment->provider->name,
                'account_number' => $payment->account_number,
                'amount_due' => (float) $payment->total_due,
                'due_date' => $dueDate->format('Y-m-d'),
                'formatted_due_date' => $dueDate->format('d/m/Y'),
                'days_overdue' => $dueDate->diffInDays($today),
                'status' => $payment->status,
                'payment_type' => 'internet'
            ];
        });
    }

    /**
     * Format overdue airtime payments.
     */
    private function formatOverdueAirtimePayments(Collection $payments, Carbon $today): Collection
    {
        return $payments->map(function (AirtimePayment $payment) use ($today) {
            $expiryDate = Carbon::parse($payment->expected_expiry);

            return [
                'id' => $payment->id,
                'station' => $payment->station->name,
                'mobile_number' => $payment->mobile_number,
                'amount' => (float) $payment->amount,
                'expiry_date' => $expiryDate->format('Y-m-d'),
                'formatted_expiry_date' => $expiryDate->format('d/m/Y'),
                'days_overdue' => $expiryDate->diffInDays($today),
                'network_provider' => $payment->network_provider,
                'status' => $payment->status,
                'payment_type' => 'airtime'
            ];
        });
    }

    /**
     * Get upcoming internet payments.
     */
    private function getUpcomingInternetPayments(Carbon $today, Carbon $nextWeek): Collection
    {
        return InternetPayment::whereBetween('due_date', [$today, $nextWeek])
            ->where('status', 'pending')
            ->with(['station', 'provider'])
            ->orderBy('due_date')
            ->get();
    }

    /**
     * Get upcoming airtime payments.
     */
    private function getUpcomingAirtimePayments(Carbon $today, Carbon $nextWeek): Collection
    {
        return AirtimePayment::whereBetween('expected_expiry', [$today, $nextWeek])
            ->where('status', 'active')
            ->with('station')
            ->orderBy('expected_expiry')
            ->get();
    }

    /**
     * Get overdue internet payments.
     */
    private function getOverdueInternetPayments(Carbon $today): Collection
    {
        return InternetPayment::where('due_date', '<', $today->format('Y-m-d'))
            ->whereIn('status', ['pending', 'overdue'])
            ->with(['station', 'provider'])
            ->orderBy('due_date')
            ->get();
    }

    /**
     * Get overdue airtime payments.
     */
    private function getOverdueAirtimePayments(Carbon $today): Collection
    {
        return AirtimePayment::where('expected_expiry', '<', $today->format('Y-m-d'))
            ->where('status', 'active')
            ->with('station')
            ->orderBy('expected_expiry')
            ->get();
    }

    /**
     * Generate reminder message for payment.
     */
    private function generateReminderMessage(InternetPayment $payment): string
    {
        $totalDue = (float) ($payment->total_due ?? 0);
        $formattedAmount = number_format($totalDue, 2);
        $dueDate = Carbon::parse($payment->due_date);

        return "Reminder: Internet bill for {$payment->station->name} is due on "
            . $dueDate->format('M d, Y')
            . ". Amount: KES " . $formattedAmount;
    }

    /**
     * Get next scheduled run time for reminders.
     */
    private function getNextScheduledRun(): ?string
    {
        $next6AM = now()->setTime(6, 0, 0);
        if (now()->hour >= 6) {
            $next6AM = $next6AM->addDay();
        }
        
        return $next6AM->format('Y-m-d H:i:s');
    }

    /**
     * Build query for reminders.
     */
    private function buildReminderQuery(array $params): Builder
    {
        $today = Carbon::today();
        $paymentType = $params['payment_type'] ?? 'both';
        
        $query = null;
        
        if ($paymentType == 'internet' || $paymentType == 'both') {
            $internetQuery = InternetPayment::with(['station', 'provider'])
                ->where('status', 'pending');
            
            if ($params['reminder_type'] == 'upcoming') {
                $internetQuery->whereBetween('due_date', [$today, $today->copy()->addDays(7)]);
            } elseif ($params['reminder_type'] == 'due_soon') {
                $internetQuery->whereBetween('due_date', [$today, $today->copy()->addDays(3)]);
            } elseif ($params['reminder_type'] == 'due_today') {
                $internetQuery->whereDate('due_date', $today);
            } elseif ($params['reminder_type'] == 'overdue') {
                $internetQuery->where('due_date', '<', $today);
            } elseif ($params['reminder_type'] == 'custom') {
                $internetQuery->whereBetween('due_date', [$params['start_date'], $params['end_date']]);
            }
            
            if (!empty($params['station_id'])) {
                $internetQuery->where('station_id', $params['station_id']);
            }
            
            $query = $internetQuery;
        }
        
        if ($paymentType == 'airtime' || $paymentType == 'both') {
            $airtimeQuery = AirtimePayment::with('station')
                ->where('status', 'active');
            
            if ($params['reminder_type'] == 'upcoming') {
                $airtimeQuery->whereBetween('expected_expiry', [$today, $today->copy()->addDays(7)]);
            } elseif ($params['reminder_type'] == 'due_soon') {
                $airtimeQuery->whereBetween('expected_expiry', [$today, $today->copy()->addDays(3)]);
            } elseif ($params['reminder_type'] == 'due_today') {
                $airtimeQuery->whereDate('expected_expiry', $today);
            } elseif ($params['reminder_type'] == 'overdue') {
                $airtimeQuery->where('expected_expiry', '<', $today);
            } elseif ($params['reminder_type'] == 'custom') {
                $airtimeQuery->whereBetween('expected_expiry', [$params['start_date'], $params['end_date']]);
            }
            
            if (!empty($params['station_id'])) {
                $airtimeQuery->where('station_id', $params['station_id']);
            }
            
            if ($query) {
                return $query->union($airtimeQuery);
            }
            
            $query = $airtimeQuery;
        }
        
        return $query;
    }

    /**
     * Register M-Pesa routes (call from routes file)
     */
    public static function registerMpesaRoutes()
    {
        Route::prefix('mpesa')->group(function () {
            Route::post('/callback', [self::class, 'mpesaCallback'])->name('mpesa.callback');
            Route::get('/transaction/{checkoutRequestId}', [self::class, 'checkTransactionStatus'])->name('mpesa.check');
            Route::post('/initiate/{payment}', [self::class, 'initiateMpesaPayment'])->name('mpesa.initiate');
            Route::get('/pay/{payment}', [self::class, 'getMpesaPaymentForm'])->name('mpesa.pay.form');
        });
    }

    /**
     * Initiate M-Pesa payment for an internet payment
     */
    public function initiateMpesaPayment(Request $request, $paymentId)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            Log::info('[PAYMENT] Initiating M-Pesa payment', [
                'request_id' => $requestId,
                'payment_id' => $paymentId,
                'user_id' => Auth::id()
            ]);
            
            $request->validate([
                'phone_number' => 'required|string|regex:/^[0-9]{9,12}$/',
                'amount' => 'required|numeric|min:1'
            ]);
            
            $payment = InternetPayment::findOrFail($paymentId);
            
            // Format phone number to 254XXXXXXXXX
            $phoneNumber = $this->formatPhoneNumber($request->phone_number);
            
            $mpesaService = new \App\Services\MpesaService();
            
            $response = $mpesaService->stkPush(
                $phoneNumber,
                $request->amount,
                $payment->account_number,
                'Payment for ' . ($payment->station->name ?? 'Internet')
            );
            
            if ($response->ResponseCode == '0') {
                MpesaTransaction::create([
                    'checkout_request_id' => $response->CheckoutRequestID,
                    'payment_id' => $payment->id,
                    'phone_number' => $phoneNumber,
                    'amount' => $request->amount,
                    'status' => 'pending',
                    'merchant_request_id' => $response->MerchantRequestID ?? null,
                    'response_code' => $response->ResponseCode,
                    'response_description' => $response->ResponseDescription ?? null
                ]);
                
                $executionTime = round((microtime(true) - $startTime) * 1000, 2);
                
                Log::info('[PAYMENT] M-Pesa STK Push initiated', [
                    'request_id' => $requestId,
                    'payment_id' => $paymentId,
                    'checkout_request_id' => $response->CheckoutRequestID,
                    'execution_time_ms' => $executionTime
                ]);
                
                if ($this->isApiRequest($request)) {
                    return $this->jsonSuccess([
                        'checkout_request_id' => $response->CheckoutRequestID,
                        'message' => 'STK Push sent to your phone'
                    ], 'Payment initiated successfully');
                }
                
                return back()->with('success', 'STK Push sent to ' . $phoneNumber);
            }
            
            Log::warning('[PAYMENT] M-Pesa STK Push failed', [
                'request_id' => $requestId,
                'payment_id' => $paymentId,
                'response' => $response
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonError($response->ResponseDescription ?? 'Failed to initiate payment', 400);
            }
            
            return back()->with('error', $response->ResponseDescription ?? 'Failed to initiate payment');
            
        } catch (ValidationException $e) {
            return $this->handleValidationError($e, $request);
        } catch (\Exception $e) {
            Log::error('[PAYMENT] M-Pesa initiation error', [
                'request_id' => $requestId,
                'payment_id' => $paymentId,
                'error' => $e->getMessage(),
                'trace' => $e->getTraceAsString()
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonError($e->getMessage(), 500);
            }
            
            return back()->with('error', 'Failed to initiate payment: ' . $e->getMessage());
        }
    }

    /**
     * Check M-Pesa transaction status (AJAX polling)
     */
    public function checkTransactionStatus(Request $request, $checkoutRequestId)
    {
        $startTime = microtime(true);
        $requestId = $this->generateRequestId();
        
        try {
            $transaction = MpesaTransaction::where('checkout_request_id', $checkoutRequestId)
                ->with('payment')
                ->first();
            
            if (!$transaction) {
                return response()->json([
                    'status' => 'not_found',
                    'message' => 'Transaction not found'
                ], 404);
            }
            
            $response = ['status' => $transaction->status];
            
            if ($transaction->status === 'completed') {
                $response['mpesa_receipt'] = $transaction->mpesa_receipt;
                $response['amount'] = $transaction->amount;
                $response['payment_status'] = $transaction->payment?->status;
            } elseif ($transaction->status === 'failed') {
                $response['message'] = $transaction->result_desc ?? 'Payment failed';
            }
            
            $executionTime = round((microtime(true) - $startTime) * 1000, 2);
            
            Log::debug('[PAYMENT] Transaction status checked', [
                'request_id' => $requestId,
                'checkout_request_id' => $checkoutRequestId,
                'status' => $transaction->status,
                'execution_time_ms' => $executionTime
            ]);
            
            return response()->json($response);
            
        } catch (\Exception $e) {
            Log::error('[PAYMENT] Transaction status check error', [
                'request_id' => $requestId,
                'checkout_request_id' => $checkoutRequestId,
                'error' => $e->getMessage()
            ]);
            
            return response()->json([
                'status' => 'error',
                'message' => $e->getMessage()
            ], 500);
        }
    }

    /**
     * Format phone number to international format
     */
    private function formatPhoneNumber($phoneNumber)
    {
        // Remove any non-numeric characters
        $phoneNumber = preg_replace('/[^0-9]/', '', $phoneNumber);
        
        // Remove leading 0 or 254 if present
        if (substr($phoneNumber, 0, 1) == '0') {
            $phoneNumber = '254' . substr($phoneNumber, 1);
        } elseif (substr($phoneNumber, 0, 3) == '254') {
            $phoneNumber = $phoneNumber;
        } else {
            $phoneNumber = '254' . $phoneNumber;
        }
        
        return $phoneNumber;
    }

    /**
     * Get M-Pesa payment form for a payment
     */
    public function getMpesaPaymentForm(Request $request, $paymentId)
    {
        $requestId = $this->generateRequestId();
        
        try {
            $payment = InternetPayment::with('station')->findOrFail($paymentId);
            
            // Check if already paid
            if ($payment->status === 'paid') {
                if ($this->isApiRequest($request)) {
                    return $this->jsonError('Payment already completed', 422);
                }
                return redirect()->route('payments.internet.show', $payment->id)
                    ->with('warning', 'This payment has already been completed');
            }
            
            // Get existing pending transactions
            $existingTransaction = MpesaTransaction::where('payment_id', $payment->id)
                ->where('status', 'pending')
                ->first();
            
            $executionTime = round((microtime(true) - $startTime ?? 0) * 1000, 2);
            
            Log::info('[PAYMENT] M-Pesa payment form loaded', [
                'request_id' => $requestId,
                'payment_id' => $paymentId,
                'amount' => $payment->amount,
                'execution_time_ms' => $executionTime
            ]);
            
            if ($this->isApiRequest($request)) {
                return $this->jsonSuccess([
                    'payment' => $payment,
                    'existing_transaction' => $existingTransaction,
                    'phone_number_format' => '0712345678'
                ]);
            }
            
            return view('payments.mpesa-payment', compact('payment', 'existingTransaction'));
            
        } catch (\Exception $e) {
            Log::error('[PAYMENT] Failed to load M-Pesa payment form', [
                'request_id' => $requestId,
                'payment_id' => $paymentId,
                'error' => $e->getMessage()
            ]);
            return $this->handleError($e, $request, 'Failed to load payment form');
        }
    }
}