<?php

namespace App\Services;

use App\Models\InternetPayment;
use App\Models\AirtimePayment;
use App\Models\NotificationLog;
use Carbon\Carbon;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Schema; 

class PaymentReminderService
{
    protected $smsService;
    protected $reminderSettings;
    
    public function __construct(SMSService $smsService = null)
    {
        $this->smsService = $smsService ?? new SMSService();
        $this->reminderSettings = $this->getReminderSettings();
    }
    
    // reminder settings from config or database
    protected function getReminderSettings(): array
    {
        return config('payment.reminders', [
            'enabled' => true,
            'email_enabled' => true,
            'sms_enabled' => false,
            'reminder_days' => [30, 14, 7, 3, 1, 0],
            'overdue_reminders' => true,
            'batch_size' => 50,
            'throttle_seconds' => 2,
            'from_email' => env('MAIL_FROM_ADDRESS', 'noreply@example.com'),
            'from_name' => env('MAIL_FROM_NAME', 'Payment System'),
        ]);
    }
    
    // Check and send all due reminders with batch processing
    public function sendAllDueReminders(string $type = 'all', bool $force = false): array
    {
        if (!$this->reminderSettings['enabled']) {
            Log::info('Payment reminders are disabled');
            return ['internet' => ['sent' => 0, 'failed' => 0, 'total' => 0], 
                    'airtime' => ['sent' => 0, 'failed' => 0, 'total' => 0], 
                    'total_sent' => 0];
        }
        
        $results = [
            'internet' => ['sent' => 0, 'failed' => 0, 'total' => 0],
            'airtime' => ['sent' => 0, 'failed' => 0, 'total' => 0],
            'total_sent' => 0
        ];
        
        if ($type == 'all' || $type == 'internet') {
            $results['internet'] = $this->processInternetPaymentReminders($force);
        }
        
        if ($type == 'all' || $type == 'airtime') {
            $results['airtime'] = $this->processAirtimeReminders($force);
        }
        
        $results['total_sent'] = $results['internet']['sent'] + $results['airtime']['sent'];
        
        Log::info('Payment reminders processed', $results);
        
        return $results;
    }
    
    // Process internet payment reminders with detailed logging
    protected function processInternetPaymentReminders(bool $force = false): array
    {
        $query = InternetPayment::with(['station', 'provider'])
            ->where('status', 'pending')
            ->where('due_date', '>=', Carbon::today()->subDays(30));
        
        $total = $query->count();
        Log::info("Processing {$total} internet payments for reminders");
        
        $sent = 0;
        $failed = 0;
        $errors = [];
        
        // Process in batches
        $query->chunk($this->reminderSettings['batch_size'] ?? 50, function ($payments) use (&$sent, &$failed, &$errors, $force) {
            foreach ($payments as $payment) {
                try {
                    Log::info("Processing payment ID: {$payment->id}, Station: {$payment->station->name}, Due Date: {$payment->due_date}");
                    
                    $result = $this->sendInternetPaymentReminders($payment, $force);
                    
                    if ($result) {
                        $sent++;
                        Log::info("✓ Reminder sent for payment {$payment->id}");
                    } else {
                        $failed++;
                        $errorMsg = "Failed to send reminder for payment {$payment->id}";
                        $errors[] = $errorMsg;
                        Log::warning($errorMsg);
                    }
                } catch (\Exception $e) {
                    $failed++;
                    $errorMsg = "Exception for payment {$payment->id}: " . $e->getMessage();
                    $errors[] = $errorMsg;
                    Log::error($errorMsg, [
                        'trace' => $e->getTraceAsString()
                    ]);
                }
                
                // Throttle to avoid hitting email/SMS limits
                if ($this->reminderSettings['throttle_seconds'] > 0) {
                    usleep($this->reminderSettings['throttle_seconds'] * 1000000);
                }
            }
        });
        
        Log::info("Internet reminders completed - Sent: {$sent}, Failed: {$failed}", ['errors' => $errors]);
        
        return ['sent' => $sent, 'failed' => $failed, 'total' => $total, 'errors' => $errors];
    }
    
    // Send bulk email reminders to multiple stations
    public function sendBulkEmailReminders(Collection $payments, string $reminderType): array
    {
        $results = ['sent' => 0, 'failed' => 0, 'errors' => []];
        
        // Group by station to avoid duplicate emails
        $groupedPayments = $payments->groupBy('station_id');
        
        foreach ($groupedPayments as $stationId => $stationPayments) {
            $station = $stationPayments->first()->station;
            
            if (empty($station->contact_email)) {
                $results['failed']++;
                $results['errors'][] = "Station {$station->name} has no email address";
                continue;
            }
            
            try {
                $emailData = $this->prepareBulkReminderEmail($stationPayments, $reminderType);
                
                Mail::to($station->contact_email)
                    ->send(new \App\Mail\BulkPaymentReminderMail($emailData));
                
                $results['sent']++;
                Log::info("Bulk reminder sent to {$station->contact_email} for {$stationPayments->count()} payments");
                
                // Log each payment reminder
                foreach ($stationPayments as $payment) {
                    $this->logReminder($payment, $reminderType, $this->getDaysUntilDue($payment));
                }
                
            } catch (\Exception $e) {
                $results['failed']++;
                $results['errors'][] = "Failed for {$station->name}: " . $e->getMessage();
                Log::error("Bulk email failed for station {$station->id}: " . $e->getMessage());
            }
        }
        
        return $results;
    }

    // bulk reminder email
    protected function prepareBulkReminderEmail(Collection $payments, string $reminderType): array
    {
        $station = $payments->first()->station;
        $internetPayments = [];
        $airtimePayments = [];
        $totalDue = 0;
        
        foreach ($payments as $payment) {
            if ($payment instanceof InternetPayment) {
                $amount = (float) ($payment->total_due ?? $payment->amount);
                $internetPayments[] = [
                    'provider' => $payment->provider->name,
                    'account_number' => $payment->account_number,
                    'amount' => $amount,
                    'due_date' => Carbon::parse($payment->due_date)->format('M d, Y'),
                    'days_until_due' => $this->getDaysUntilDue($payment),
                    'paybill' => $payment->provider->paybill_number ?? 'N/A'
                ];
                $totalDue += $amount;
            } elseif ($payment instanceof AirtimePayment) {
                $airtimePayments[] = [
                    'mobile_number' => $payment->mobile_number,
                    'amount' => $payment->amount,
                    'expiry_date' => Carbon::parse($payment->expected_expiry)->format('M d, Y'),
                    'network_provider' => $payment->network_provider
                ];
                $totalDue += $payment->amount;
            }
        }
        
        return [
            'station_name' => $station->name,
            'contact_person' => $station->contact_person_name ?? 'Manager',
            'reminder_type' => $reminderType,
            'reminder_date' => now()->format('F d, Y'),
            'internet_payments' => $internetPayments,
            'airtime_payments' => $airtimePayments,
            'total_payments' => $payments->count(),
            'total_due' => $totalDue,
            'internet_count' => count($internetPayments),
            'airtime_count' => count($airtimePayments),
            'urgent' => in_array($reminderType, ['1_day', 'due_today', 'overdue'])
        ];
    }
    
    // Get days until due for payment
    protected function getDaysUntilDue($payment): int
    {
        if ($payment instanceof InternetPayment) {
            $date = Carbon::parse($payment->due_date);
        } else {
            $date = Carbon::parse($payment->expected_expiry);
        }
        return Carbon::today()->diffInDays($date, false);
    }
    
    // Send individual reminder
    protected function sendInternetPaymentReminders(InternetPayment $payment, bool $force = false): bool
    {
        $dueDate = Carbon::parse($payment->due_date);
        $today = Carbon::today();
        $daysUntilDue = $today->diffInDays($dueDate, false);
        
        // If not forcing, check if we should send reminders
        if (!$force) {
            $reminderTypes = $this->determineRemindersToSend($daysUntilDue, $payment);
            if (empty($reminderTypes)) {
                return false;
            }
        } else {
            $reminderTypes = ['manual_reminder'];
        }
        
        $success = false;
        
        foreach ($reminderTypes as $reminderType) {
            if ($this->sendReminder($payment, $reminderType, $daysUntilDue)) {
                $success = true;
                $this->logReminder($payment, $reminderType, $daysUntilDue);
            }
        }
        
        return $success;
    }
    
    // Determine which reminders need to be sent
    protected function determineRemindersToSend(int $daysUntilDue, InternetPayment $payment): array
    {
        $reminders = [];
        
        try {
            $sentReminders = NotificationLog::where('payment_id', $payment->id)
                ->where('payment_type', 'internet')
                ->whereNotNull('reminder_type') 
                ->pluck('reminder_type')
                ->toArray();
        } catch (\Exception $e) {
            Log::warning('Could not check sent reminders: ' . $e->getMessage());
            $sentReminders = [];
        }
        
        $reminderDays = $this->reminderSettings['reminder_days'] ?? [30, 14, 7, 3, 1];
        
        foreach ($reminderDays as $day) {
            $reminderKey = $day == 0 ? 'due_today' : "{$day}_days";
            if ($daysUntilDue == $day && !in_array($reminderKey, $sentReminders)) {
                $reminders[] = $reminderKey;
            }
        }
        
        // Due today reminder
        if ($daysUntilDue == 0 && !in_array('due_today', $sentReminders)) {
            $reminders[] = 'due_today';
        }
        
        // Overdue reminder
        if ($daysUntilDue < 0 && $this->reminderSettings['overdue_reminders'] && !in_array('overdue', $sentReminders)) {
            $reminders[] = 'overdue';
        }
        
        return $reminders;
    }
        
    protected function sendReminder(InternetPayment $payment, string $reminderType, int $daysUntilDue): bool
    {
        $station = $payment->station;
        $provider = $payment->provider;
        $totalDue = (float) ($payment->total_due ?? $payment->amount);
        $formattedAmount = number_format($totalDue, 2);
        $dueDate = Carbon::parse($payment->due_date);
        
        $messageData = $this->getReminderMessage($reminderType, $station, $provider, $formattedAmount, $dueDate, $daysUntilDue);
        
        $sent = false;
        
        // Send Email
        if (!empty($station->contact_email)) {
            try {
                Mail::to($station->contact_email)
                    ->send(new \App\Mail\PaymentReminderMail($payment, $messageData));
                $sent = true;
                Log::info("{$reminderType} email reminder sent to {$station->contact_email} for payment {$payment->id}");
            } catch (\Exception $e) {
                Log::error("Email failed for payment {$payment->id}: " . $e->getMessage());
            }
        }
        
        // Send SMS
        if (!empty($station->contact_phone) && config('services.sms.enabled', false)) {
            try {
                $smsSent = $this->smsService->sendSMS($station->contact_phone, $messageData['sms_message']);
                if ($smsSent) {
                    $sent = true;
                    Log::info("{$reminderType} SMS reminder sent to {$station->contact_phone} for payment {$payment->id}");
                }
            } catch (\Exception $e) {
                Log::error("SMS failed for payment {$payment->id}: " . $e->getMessage());
            }
        }
        
        return $sent;
    }
    
    // Get reminder message based on type
    protected function getReminderMessage(string $type, $station, $provider, string $amount, Carbon $dueDate, int $daysUntilDue): array
    {
        $stationName = $station->name;
        $paybill = $provider->paybill_number ?? 'XXXXXX';
        $accountNo = $payment->account_number ?? 'N/A';
        
        $messages = [
            '1_week' => [
                'subject' => "Payment Reminder: Internet Bill Due in 1 Week - {$stationName}",
                'email_message' => "Dear {$stationName} team,\n\nThis is a friendly reminder that your internet bill of KES {$amount} is due in 7 days on {$dueDate->format('l, F j, Y')}.\n\nPlease ensure payment is made on time to avoid service interruption.\n\nPayment Details:\n• Paybill Number: {$paybill}\n• Account Number: {$accountNo}\n• Amount: KES {$amount}\n\nThank you for your prompt payment.",
                'sms_message' => "REMINDER: {$stationName} internet bill KES {$amount} due in 7 days ({$dueDate->format('M j')}). Pay via Paybill {$paybill} Acc {$accountNo}"
            ],
            '3_days' => [
                'subject' => "URGENT: Internet Payment Due in 3 Days - {$stationName}",
                'email_message' => "Dear {$stationName} team,\n\nYour internet bill of KES {$amount} is due in 3 days on {$dueDate->format('l, F j, Y')}.\n\nPlease make payment immediately to avoid late fees or service disruption.\n\nPayment Details:\n• Paybill Number: {$paybill}\n• Account Number: {$accountNo}\n• Amount: KES {$amount}\n\nThank you for your attention.",
                'sms_message' => "URGENT: {$stationName} internet KES {$amount} due in 3 days ({$dueDate->format('M j')}). Pay via Paybill {$paybill} Acc {$accountNo}"
            ],
            '1_day' => [
                'subject' => "TOMORROW: Internet Payment Due - {$stationName}",
                'email_message' => "Dear {$stationName} team,\n\nIMPORTANT: Your internet bill of KES {$amount} is due TOMORROW ({$dueDate->format('l, F j, Y')}).\n\nPlease make payment today to ensure uninterrupted service.\n\nPayment Details:\n• Paybill Number: {$paybill}\n• Account Number: {$accountNo}\n• Amount: KES {$amount}\n\nThank you.",
                'sms_message' => "TOMORROW: {$stationName} internet KES {$amount} due {$dueDate->format('M j')}. Pay today via Paybill {$paybill} Acc {$accountNo}"
            ],
            'due_today' => [
                'subject' => "DUE TODAY: Internet Payment Required - {$stationName}",
                'email_message' => "Dear {$stationName} team,\n\nPAYMENT DUE TODAY: Your internet bill of KES {$amount} is due today, {$dueDate->format('l, F j, Y')}.\n\nPlease make payment immediately to avoid service interruption.\n\nPayment Details:\n• Paybill Number: {$paybill}\n• Account Number: {$accountNo}\n• Amount: KES {$amount}\n\nThank you for your prompt payment.",
                'sms_message' => "DUE TODAY: {$stationName} internet KES {$amount}. Pay NOW via Paybill {$paybill} Acc {$accountNo}"
            ],
            'overdue' => [
                'subject' => "OVERDUE: Internet Payment Past Due - {$stationName}",
                'email_message' => "Dear {$stationName} team,\n\nYOUR PAYMENT IS OVERDUE: Your internet bill of KES {$amount} was due on {$dueDate->format('l, F j, Y')} and is now {$daysUntilDue} days overdue.\n\nPlease make payment immediately to avoid service disconnection.\n\nPayment Details:\n• Paybill Number: {$paybill}\n• Account Number: {$accountNo}\n• Amount: KES {$amount}\n\nThank you for your prompt attention.",
                'sms_message' => "OVERDUE: {$stationName} internet KES {$amount} is {$daysUntilDue} days overdue. Pay NOW via Paybill {$paybill} Acc {$accountNo}"
            ]
        ];
        
        return $messages[$type] ?? $messages['1_week'];
    }
    
    protected function logReminder(InternetPayment $payment, string $reminderType, int $daysUntilDue): void
    {
        try {
            $hasColumns = $this->checkNotificationLogColumns();
            
            $logData = [
                'payment_id' => $payment->id,
                'payment_type' => 'internet',
                'sent_at' => now(),
            ];
            
            // Only add columns if they exist
            if ($hasColumns['reminder_type']) {
                $logData['reminder_type'] = $reminderType;
            }
            
            if ($hasColumns['days_until_due']) {
                $logData['days_until_due'] = $daysUntilDue;
            }
            
            if ($hasColumns['station_id']) {
                $logData['station_id'] = $payment->station_id;
            }
            
            if ($hasColumns['provider_id']) {
                $logData['provider_id'] = $payment->vendor_id;
            }
            
            NotificationLog::create($logData);
            
            // Update last_reminder_sent date on payment if column exists
            if (Schema::hasColumn('internet_payments', 'last_reminder_sent')) {
                $payment->update(['last_reminder_sent' => now()]);
            }
            
        } catch (\Exception $e) {
            Log::error('Failed to log reminder: ' . $e->getMessage());
            // Don't throw the exception - just log it so reminders still work
        }
    }

    protected function checkNotificationLogColumns(): array
    {
        static $columns = null;
        
        if ($columns === null) {
            try {
                $columns = [];
                $tableColumns = Schema::getColumnListing('notification_logs');
                
                $columns['reminder_type'] = in_array('reminder_type', $tableColumns);
                $columns['days_until_due'] = in_array('days_until_due', $tableColumns);
                $columns['station_id'] = in_array('station_id', $tableColumns);
                $columns['provider_id'] = in_array('provider_id', $tableColumns);
                $columns['metadata'] = in_array('metadata', $tableColumns);
                
            } catch (\Exception $e) {
                Log::warning('Could not check notification_logs columns: ' . $e->getMessage());
                $columns = ['reminder_type' => false, 'days_until_due' => false, 'station_id' => false, 'provider_id' => false, 'metadata' => false];
            }
        }
        
        return $columns;
    }
    
    protected function processAirtimeReminders(): array
    {
        $payments = AirtimePayment::with('station')
            ->where('status', 'active')
            ->where('expected_expiry', '>=', Carbon::today()->subDays(30))
            ->get();
        
        $sent = 0;
        $failed = 0;
        
        foreach ($payments as $payment) {
            $result = $this->sendAirtimeReminders($payment);
            if ($result) {
                $sent++;
            } else {
                $failed++;
            }
        }
        
        return ['sent' => $sent, 'failed' => $failed, 'total' => $payments->count()];
    }
    
    protected function sendAirtimeReminders(AirtimePayment $payment): bool
    {
        $expiryDate = Carbon::parse($payment->expected_expiry);
        $today = Carbon::today();
        $daysUntilExpiry = $today->diffInDays($expiryDate, false);
        
        $sentReminders = NotificationLog::where('payment_id', $payment->id)
            ->where('payment_type', 'airtime')
            ->pluck('reminder_type')
            ->toArray();
        
        $reminderTypes = [];
        
        if ($daysUntilExpiry == 7 && !in_array('1_week', $sentReminders)) {
            $reminderTypes[] = '1_week';
        }
        if ($daysUntilExpiry == 3 && !in_array('3_days', $sentReminders)) {
            $reminderTypes[] = '3_days';
        }
        if ($daysUntilExpiry == 1 && !in_array('1_day', $sentReminders)) {
            $reminderTypes[] = '1_day';
        }
        if ($daysUntilExpiry == 0 && !in_array('expiry_today', $sentReminders)) {
            $reminderTypes[] = 'expiry_today';
        }
        if ($daysUntilExpiry < 0 && !in_array('expired', $sentReminders)) {
            $reminderTypes[] = 'expired';
        }
        
        if (empty($reminderTypes)) {
            return false;
        }
        
        $success = false;
        foreach ($reminderTypes as $type) {
            if ($this->sendAirtimeReminder($payment, $type, $daysUntilExpiry)) {
                $success = true;
                $this->logAirtimeReminder($payment, $type, $daysUntilExpiry);
            }
        }
        
        return $success;
    }
    
    protected function sendAirtimeReminder(AirtimePayment $payment, string $type, int $daysUntilExpiry): bool
    {
        $station = $payment->station;
        $amount = number_format($payment->amount, 2);
        $expiryDate = Carbon::parse($payment->expected_expiry);
        
        $messages = [
            '1_week' => "REMINDER: {$station->name} airtime of KES {$amount} expires in 7 days ({$expiryDate->format('M j')}). Please top up.",
            '3_days' => "URGENT: {$station->name} airtime KES {$amount} expires in 3 days ({$expiryDate->format('M j')}). Top up now.",
            '1_day' => "TOMORROW: {$station->name} airtime KES {$amount} expires tomorrow ({$expiryDate->format('M j')}). Please top up today.",
            'expiry_today' => "EXPIRES TODAY: {$station->name} airtime KES {$amount} expires today. Please top up immediately.",
            'expired' => "EXPIRED: {$station->name} airtime of KES {$amount} has expired. Please top up to restore service."
        ];
        
        $sent = false;
        
        if (!empty($station->contact_email)) {
            try {
                Mail::to($station->contact_email)
                    ->send(new \App\Mail\AirtimeReminderMail($payment, $messages[$type]));
                $sent = true;
            } catch (\Exception $e) {
                Log::error("Airtime email failed: " . $e->getMessage());
            }
        }
        
        if (!empty($station->contact_phone) && config('services.sms.enabled', false)) {
            try {
                $smsSent = $this->smsService->sendSMS($station->contact_phone, $messages[$type]);
                if ($smsSent) $sent = true;
            } catch (\Exception $e) {
                Log::error("Airtime SMS failed: " . $e->getMessage());
            }
        }
        
        return $sent;
    }

    protected function logAirtimeReminder(AirtimePayment $payment, string $type, int $daysUntilExpiry): void
    {
        NotificationLog::create([
            'payment_id' => $payment->id,
            'payment_type' => 'airtime',
            'reminder_type' => $type,
            'days_until_due' => $daysUntilExpiry,
            'sent_at' => now(),
            'station_id' => $payment->station_id
        ]);
    }

    public function shouldRunReminders(string $type = 'all'): bool
    {
        $now = now();
        $hour = (int) $now->format('H');
        
        // Different reminder types for different times
        if ($type === 'internet' && $hour === 6) {
            return true; // 6 AM for regular internet reminders
        }
        
        if ($type === 'airtime' && $hour === 18) {
            return true; // 6 PM for airtime reminders
        }
        
        if ($type === 'urgent' && $hour === 9) {
            return true; // 9 AM for urgent reminders
        }
        
        return false;
    }

    public function getPendingRemindersCount(): array
    {
        $today = Carbon::today();
        $nextWeek = $today->copy()->addDays(7);
        
        return [
            'internet_due_soon' => InternetPayment::whereBetween('due_date', [$today, $nextWeek])
                ->where('status', 'pending')
                ->count(),
            'airtime_expiring_soon' => AirtimePayment::whereBetween('expected_expiry', [$today, $nextWeek])
                ->where('status', 'active')
                ->count(),
            'internet_overdue' => InternetPayment::where('due_date', '<', $today)
                ->where('status', 'pending')
                ->count(),
            'airtime_expired' => AirtimePayment::where('expected_expiry', '<', $today)
                ->where('status', 'active')
                ->count(),
        ];
    }
}