<?php

namespace App\Services;

use App\Models\SmsMessage;
use App\Models\SmsTemplate;
use App\Models\User;
use App\Exceptions\SmsException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Collection;

/**
 * SmsCommunicationService
 * 
 * Handles SMS-related business logic for the Communication Module.
 * Integrates with existing SMSService for Africa's Talking API.
 * Implements role-based recipient filtering per requirements.
 * 
 * Requirements: 2.1, 2.3, 2.4, 2.5, 2.6, 2.7, 2.8, 2.9, 2.10, 5.3, 5.4, 5.5
 */
class SmsCommunicationService
{
    /**
     * Create an instance.
     */
    public function __construct(
        protected SMSService $smsService
    ) {}

    /**
     * Send SMS to specified recipients.
     *
     * @param int $senderId User sending the SMS
     * @param array $recipients Array of recipient data
     * @param string $message Message content
     * @param array $options Additional options (template_id, scheduled_at, sender_id_name)
     * @return SmsMessage
     * @throws SmsException
     */
    public function sendSms(int $senderId, array $recipients, string $message, array $options = []): SmsMessage
    {
        $sender = User::findOrFail($senderId);

        // Validate recipients based on user's role
        $allowedRecipients = $this->filterRecipientsByRole($sender, $recipients);

        if (empty($allowedRecipients)) {
            throw new SmsException('No valid recipients available for your role.');
        }

        return DB::transaction(function () use ($senderId, $allowedRecipients, $message, $options) {
            // Create message record
            $smsMessage = SmsMessage::create([
                'sender_id' => $senderId,
                'recipients' => $allowedRecipients,
                'message' => strip_tags($message),
                'template_id' => $options['template_id'] ?? null,
                'sender_id_name' => $options['sender_id_name'] ?? null,
                'scheduled_at' => $options['scheduled_at'] ?? null,
                'status' => isset($options['scheduled_at']) && strtotime($options['scheduled_at']) > time()
                    ? SmsMessage::STATUS_SCHEDULED
                    : SmsMessage::STATUS_DRAFT,
                'recipient_type' => $options['recipient_type'] ?? SmsMessage::RECIPIENT_MANUAL,
                'target_role' => $options['target_role'] ?? null,
            ]);

            // If not scheduled, send immediately
            if (!$smsMessage->scheduled_at || $smsMessage->scheduled_at->isPast()) {
                $this->sendMessage($smsMessage);
            }

            return $smsMessage;
        });
    }

    /**
     * Send SMS using a template.
     *
     * @param int $senderId
     * @param array $recipients
     * @param int $templateId
     * @param array $variables Template variables
     * @param array $options
     * @return SmsMessage
     */
    public function sendFromTemplate(int $senderId, array $recipients, int $templateId, array $variables = [], array $options = []): SmsMessage
    {
        $template = SmsTemplate::findOrFail($templateId);

        if (!$template->is_active) {
            throw new SmsException('Selected template is not active.');
        }

        $message = $template->parse($variables);

        $options['template_id'] = $templateId;

        $result = $this->sendSms($senderId, $recipients, $message, $options);

        // Increment template usage
        $template->incrementUsage();

        return $result;
    }

    /**
     * Send scheduled SMS messages that are due.
     *
     * @return int Number of messages sent
     */
    public function processScheduledMessages(): int
    {
        $messages = SmsMessage::pendingScheduled()->get();
        $sent = 0;

        foreach ($messages as $message) {
            try {
                $this->sendMessage($message);
                $sent++;
            } catch (\Exception $e) {
                Log::error('Failed to send scheduled SMS', [
                    'message_id' => $message->id,
                    'error' => $e->getMessage(),
                ]);
            }
        }

        return $sent;
    }

    /**
     * Get SMS history with filters.
     *
     * @param int $userId User requesting history
     * @param array $filters
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    public function getHistory(int $userId, array $filters = [])
    {
        $user = User::findOrFail($userId);
        
        $query = SmsMessage::with(['sender', 'template']);

        // Non-admins can only see their own sent messages
        if (!$user->hasAnyRole(['Super Manager', 'Manager'])) {
            $query->where('sender_id', $userId);
        }

        return $this->applyFilters($query, $filters);
    }

    /**
     * Delete SMS message (admin only).
     *
     * @param int $messageId
     * @param int $userId User attempting to delete
     * @return bool
     */
    public function deleteMessage(int $messageId, int $userId): bool
    {
        $user = User::findOrFail($userId);

        if (!$user->hasAnyRole(['Super Manager', 'Manager'])) {
            throw new SmsException('Only administrators can delete SMS history.');
        }

        return SmsMessage::where('id', $messageId)->delete();
    }

    /**
     * Bulk delete SMS messages (admin only).
     *
     * @param array $messageIds
     * @param int $userId
     * @return int Number of deleted messages
     */
    public function bulkDelete(array $messageIds, int $userId): int
    {
        $user = User::findOrFail($userId);

        if (!$user->hasAnyRole(['Super Manager', 'Manager'])) {
            throw new SmsException('Only administrators can delete SMS history.');
        }

        return SmsMessage::whereIn('id', $messageIds)->delete();
    }

    /**
     * Get available templates.
     *
     * @return Collection
     */
    public function getTemplates(): Collection
    {
        return SmsTemplate::active()
            ->orderBy('name')
            ->get();
    }

    /**
     * Get users by role for recipient selection.
     *
     * @param string $role
     * @return Collection
     */
    public function getUsersByRole(string $role): Collection
    {
        return User::role($role)
            ->whereNotNull('phone')
            ->select(['id', 'name', 'phone', 'email'])
            ->get();
    }

    /**
     * Get available recipient roles based on sender's role.
     *
     * @param User $sender
     * @return array
     */
    public function getAvailableRecipientRoles(User $sender): array
    {
        // Trainers can only send to Trainees
        if ($sender->hasRole('Trainers')) {
            return ['Trainees'];
        }

        // Mentors can only send to Mentees
        if ($sender->hasRole('Mentor')) {
            return ['Mentee'];
        }

        // Super Manager and Manager can send to all
        if ($sender->hasAnyRole(['Super Manager', 'Manager'])) {
            return [
                'Super Manager',
                'Manager',
                'Members',
                'Trainers',
                'Trainees',
                'Mentor',
                'Mentee',
                'Partners',
                'Contributor',
                'Health Marketer',
            ];
        }

        // Default: no role-based sending
        return [];
    }

    /**
     * Filter recipients based on sender's role permissions.
     *
     * @param User $sender
     * @param array $recipients
     * @return array
     */
    protected function filterRecipientsByRole(User $sender, array $recipients): array
    {
        // Trainers can only send to Trainees
        if ($sender->hasRole('Trainers')) {
            return $this->filterRecipientsToRole($recipients, 'Trainees');
        }

        // Mentors can only send to Mentees
        if ($sender->hasRole('Mentor')) {
            return $this->filterRecipientsToRole($recipients, 'Mentee');
        }

        // Super Manager and Manager can send to anyone
        if ($sender->hasAnyRole(['Super Manager', 'Manager'])) {
            return $recipients;
        }

        // Others can only send to manually entered phone numbers (not role-based)
        return array_filter($recipients, function ($recipient) {
            return !isset($recipient['role_group']) && isset($recipient['phone']);
        });
    }

    /**
     * Filter recipients to a specific role.
     *
     * @param array $recipients
     * @param string $role
     * @return array
     */
    protected function filterRecipientsToRole(array $recipients, string $role): array
    {
        $roleUserPhones = User::role($role)
            ->whereNotNull('phone')
            ->pluck('phone')
            ->map(fn($phone) => $this->normalizePhone($phone))
            ->toArray();

        return array_filter($recipients, function ($recipient) use ($roleUserPhones, $role) {
            // If it's a role group selection, only allow the matching role
            if (isset($recipient['role_group'])) {
                return $recipient['role_group'] === $role;
            }

            // For manual phone entries, verify the phone belongs to the allowed role
            $phone = $this->normalizePhone($recipient['phone'] ?? '');
            return in_array($phone, $roleUserPhones);
        });
    }

    /**
     * Normalize phone number format.
     *
     * @param string $phone
     * @return string
     */
    protected function normalizePhone(string $phone): string
    {
        // Remove non-numeric characters
        $phone = preg_replace('/[^0-9]/', '', $phone);

        // Convert local format to international
        if (strlen($phone) === 10 && str_starts_with($phone, '07')) {
            $phone = '+254' . substr($phone, 1);
        }

        if (strlen($phone) === 9) {
            $phone = '+254' . $phone;
        }

        if (!str_starts_with($phone, '+')) {
            $phone = '+' . $phone;
        }

        return $phone;
    }

    /**
     * Actually send the message via SMS service.
     *
     * @param SmsMessage $message
     */
    protected function sendMessage(SmsMessage $message): void
    {
        $message->markAsSending();

        try {
            $phones = collect($message->recipients)
                ->pluck('phone')
                ->map(fn($phone) => $this->normalizePhone($phone))
                ->filter()
                ->toArray();

            if (empty($phones)) {
                throw new SmsException('No valid phone numbers to send to.');
            }

            // Use existing SMSService
            $result = $this->smsService->sendBulk($phones, $message->message);

            if (($result['sent'] ?? 0) > 0) {
                $message->markAsSent(
                    null,
                    $result['results'][0]['cost'] ?? 0,
                    $result['results'] ?? []
                );
            } else {
                $message->markAsFailed($result['results'][0]['message'] ?? 'All recipients failed');
            }

        } catch (\Exception $e) {
            Log::error('SMS send failed', [
                'message_id' => $message->id,
                'error' => $e->getMessage(),
            ]);

            $message->markAsFailed($e->getMessage());
            
            throw new SmsException('Failed to send SMS: ' . $e->getMessage());
        }
    }

    /**
     * Apply filters to SMS query.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param array $filters
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    protected function applyFilters($query, array $filters)
    {
        if (!empty($filters['status'])) {
            $query->where('status', $filters['status']);
        }

        if (!empty($filters['search'])) {
            $query->search($filters['search']);
        }

        if (!empty($filters['date_from'])) {
            $query->whereDate('created_at', '>=', $filters['date_from']);
        }

        if (!empty($filters['date_to'])) {
            $query->whereDate('created_at', '<=', $filters['date_to']);
        }

        if (!empty($filters['sender_id'])) {
            $query->where('sender_id', $filters['sender_id']);
        }

        $sortBy = $filters['sort_by'] ?? 'created_at';
        $sortOrder = $filters['sort_order'] ?? 'desc';

        return $query->orderBy($sortBy, $sortOrder)
            ->paginate($filters['per_page'] ?? 15);
    }

    /**
     * Get SMS statistics.
     *
     * @return array
     */
    public function getStatistics(): array
    {
        return [
            'total' => SmsMessage::count(),
            'sent' => SmsMessage::sent()->count(),
            'failed' => SmsMessage::failed()->count(),
            'scheduled' => SmsMessage::scheduled()->count(),
            'total_cost' => SmsMessage::sent()->sum('cost'),
            'today' => SmsMessage::whereDate('created_at', today())->count(),
            'this_month' => SmsMessage::whereMonth('created_at', now()->month)->count(),
            'templates_count' => SmsTemplate::active()->count(),
        ];
    }

    /**
     * Process uploaded CSV file for recipients.
     *
     * @param \Illuminate\Http\UploadedFile $file
     * @return array Array of recipient data
     */
    public function processCsvRecipients($file): array
    {
        $recipients = [];
        $path = $file->getRealPath();
        
        if (($handle = fopen($path, 'r')) !== false) {
            // Skip header row
            fgetcsv($handle, 1000, ',');
            
            while (($data = fgetcsv($handle, 1000, ',')) !== false) {
                if (!empty($data[0])) {
                    $recipients[] = [
                        'phone' => $data[0],
                        'name' => $data[1] ?? null,
                    ];
                }
            }
            
            fclose($handle);
        }

        return $recipients;
    }
}
