<?php

namespace App\Services;

use App\Models\EmailMessage;
use App\Models\EmailTemplate;
use App\Models\User;
use App\Exceptions\EmailException;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Collection;
use Illuminate\Support\HtmlString;

/**
 * EmailCommunicationService
 * 
 * Handles email-related business logic for the Communication Module.
 * Integrates with existing EmailService.
 * Implements role-based recipient filtering per requirements.
 * 
 * Requirements: 3.1, 3.3, 3.4, 3.5, 3.6, 3.7, 3.8, 3.9, 3.10, 5.3, 5.4, 5.5
 */
class EmailCommunicationService
{
    /**
     * Create an instance.
     */
    public function __construct(
        protected EmailService $emailService
    ) {}

    /**
     * Send email to specified recipients.
     *
     * @param int $senderId User sending the email
     * @param array $toRecipients Primary recipients
     * @param string $subject Email subject
     * @param string $htmlBody HTML content
     * @param array $options Additional options
     * @return EmailMessage
     * @throws EmailException
     */
    public function sendEmail(int $senderId, array $toRecipients, string $subject, string $htmlBody, array $options = []): EmailMessage
    {
        $sender = User::findOrFail($senderId);

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

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

        return DB::transaction(function () use ($senderId, $allowedRecipients, $subject, $htmlBody, $options) {
            // Create message record
            $emailMessage = EmailMessage::create([
                'sender_id' => $senderId,
                'to_recipients' => $allowedRecipients,
                'cc_recipients' => $options['cc_recipients'] ?? null,
                'bcc_recipients' => $options['bcc_recipients'] ?? null,
                'subject' => strip_tags($subject),
                'html_body' => $htmlBody,
                'text_body' => $options['text_body'] ?? strip_tags($htmlBody),
                'template_id' => $options['template_id'] ?? null,
                'scheduled_at' => $options['scheduled_at'] ?? null,
                'status' => isset($options['scheduled_at']) && strtotime($options['scheduled_at']) > time()
                    ? EmailMessage::STATUS_SCHEDULED
                    : EmailMessage::STATUS_DRAFT,
                'priority' => $options['priority'] ?? EmailMessage::PRIORITY_NORMAL,
                'recipient_type' => $options['recipient_type'] ?? 'manual',
                'target_role' => $options['target_role'] ?? null,
                'attachments' => $options['attachments'] ?? null,
            ]);

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

            return $emailMessage;
        });
    }

    /**
     * Send email using a template.
     *
     * @param int $senderId
     * @param array $toRecipients
     * @param int $templateId
     * @param array $variables Template variables
     * @param array $options
     * @return EmailMessage
     */
    public function sendFromTemplate(int $senderId, array $toRecipients, int $templateId, array $variables = [], array $options = []): EmailMessage
    {
        $template = EmailTemplate::findOrFail($templateId);

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

        $subject = $template->parseSubject($variables);
        $htmlBody = $template->parseHtmlContent($variables);
        $textBody = $template->parseTextContent($variables);

        $options['template_id'] = $templateId;
        $options['text_body'] = $textBody;

        $result = $this->sendEmail($senderId, $toRecipients, $subject, $htmlBody, $options);

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

        return $result;
    }

    /**
     * Reply to an email.
     *
     * @param int $originalMessageId
     * @param int $senderId
     * @param string $replyContent
     * @param bool $replyAll
     * @return EmailMessage
     */
    public function replyToEmail(int $originalMessageId, int $senderId, string $replyContent, bool $replyAll = false): EmailMessage
    {
        $original = EmailMessage::findOrFail($originalMessageId);

        $sender = User::findOrFail($senderId);

        // Only Super Manager and Manager can reply
        if (!$sender->hasAnyRole(['Super Manager', 'Manager'])) {
            throw new EmailException('Only administrators can reply to emails.');
        }

        $toRecipients = $original->to_recipients;
        
        if ($replyAll) {
            // Include CC recipients
            $ccRecipients = array_merge(
                $original->cc_recipients ?? [],
                [['email' => $original->sender->email, 'name' => $original->sender->name]]
            );
        }

        // Build reply content with quoted original
        $quotedContent = $this->buildQuotedReply($original);
        $fullContent = $replyContent . $quotedContent;

        return $this->sendEmail($senderId, $toRecipients, 'Re: ' . $original->subject, $fullContent, [
            'reply_to_id' => $original->id,
            'in_reply_to' => $original->message_id,
            'references' => trim(($original->references ?? '') . ' ' . $original->message_id),
            'cc_recipients' => $ccRecipients ?? null,
        ]);
    }

    /**
     * Process scheduled email messages that are due.
     *
     * @return int Number of messages sent
     */
    public function processScheduledMessages(): int
    {
        $messages = EmailMessage::where('status', EmailMessage::STATUS_SCHEDULED)
            ->where('scheduled_at', '<=', now())
            ->get();

        $sent = 0;

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

        return $sent;
    }

    /**
     * Get email history with filters.
     *
     * @param int $userId
     * @param array $filters
     * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator
     */
    public function getHistory(int $userId, array $filters = [])
    {
        $user = User::findOrFail($userId);
        
        $query = EmailMessage::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);
    }

    /**
     * Get received emails (Super Manager and Manager only).
     *
     * @param int $userId
     * @param array $filters
     * @return array
     */
    public function getReceivedEmails(int $userId, array $filters = []): array
    {
        $user = User::findOrFail($userId);

        if (!$user->hasAnyRole(['Super Manager', 'Manager'])) {
            throw new EmailException('Only administrators can view received emails.');
        }

        try {
            $result = $this->emailService->receive($filters['limit'] ?? 20);

            return $result['data']['emails'] ?? [];
        } catch (\Exception $e) {
            Log::error('Failed to fetch received emails', [
                'user_id' => $userId,
                'error' => $e->getMessage(),
            ]);

            return [];
        }
    }

    /**
     * Delete email message.
     *
     * @param int $messageId
     * @param int $userId
     * @return bool
     */
    public function deleteMessage(int $messageId, int $userId): bool
    {
        $user = User::findOrFail($userId);
        $message = EmailMessage::findOrFail($messageId);

        // Only sender or admin can delete
        if ($message->sender_id !== $userId && !$user->hasAnyRole(['Super Manager', 'Manager'])) {
            throw new EmailException('You do not have permission to delete this email.');
        }

        return $message->delete();
    }

    /**
     * Bulk delete email messages.
     *
     * @param array $messageIds
     * @param int $userId
     * @return int
     */
    public function bulkDelete(array $messageIds, int $userId): int
    {
        $user = User::findOrFail($userId);

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

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

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

    /**
     * Create or update template.
     *
     * @param array $data Template data
     * @param int $userId User creating/updating
     * @param int|null $templateId Template ID for update
     * @return EmailTemplate
     */
    public function saveTemplate(array $data, int $userId, ?int $templateId = null): EmailTemplate
    {
        $user = User::findOrFail($userId);

        if (!$user->hasAnyRole(['Super Manager', 'Manager'])) {
            throw new EmailException('Only administrators can manage templates.');
        }

        $data['created_by'] = $userId;

        if (empty($data['slug'])) {
            $data['slug'] = \Str::slug($data['name']);
        }

        if ($templateId) {
            $template = EmailTemplate::findOrFail($templateId);
            
            // Cannot modify system templates
            if ($template->is_system) {
                throw new EmailException('System templates cannot be modified.');
            }
            
            $template->update($data);
            return $template;
        }

        return EmailTemplate::create($data);
    }

    /**
     * Delete template.
     *
     * @param int $templateId
     * @param int $userId
     * @return bool
     */
    public function deleteTemplate(int $templateId, int $userId): bool
    {
        $user = User::findOrFail($userId);

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

        $template = EmailTemplate::findOrFail($templateId);

        if ($template->is_system) {
            throw new EmailException('System templates cannot be deleted.');
        }

        return $template->delete();
    }

    /**
     * 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',
            ];
        }

        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 emails
        return array_filter($recipients, function ($recipient) {
            return !isset($recipient['role_group']) && isset($recipient['email']);
        });
    }

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

        return array_filter($recipients, function ($recipient) use ($roleUserEmails, $role) {
            if (isset($recipient['role_group'])) {
                return $recipient['role_group'] === $role;
            }

            $email = strtolower($recipient['email'] ?? '');
            return in_array($email, array_map('strtolower', $roleUserEmails));
        });
    }

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

        try {
            $toEmails = collect($message->to_recipients)
                ->pluck('email')
                ->filter()
                ->toArray();

            if (empty($toEmails)) {
                throw new EmailException('No valid email addresses to send to.');
            }

            // Build email data
            $emailData = [
                'to' => $toEmails,
                'cc' => $message->cc_recipients ? collect($message->cc_recipients)->pluck('email')->filter()->toArray() : [],
                'bcc' => $message->bcc_recipients ? collect($message->bcc_recipients)->pluck('email')->filter()->toArray() : [],
                'subject' => $message->subject,
                'html' => $message->html_body,
                'text' => $message->text_body,
            ];

            // Use existing EmailService
            $result = $this->emailService->sendHtml(
                implode(',', $toEmails),
                $message->subject,
                $message->html_body
            );

            if ($result['success']) {
                $message->markAsSent($result['messageId'] ?? null);
            } else {
                $message->markAsFailed($result['message'] ?? 'Unknown error');
            }

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

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

    /**
     * Build quoted reply content.
     *
     * @param EmailMessage $original
     * @return string
     */
    protected function buildQuotedReply(EmailMessage $original): string
    {
        $date = $original->created_at->format('F j, Y, g:i a');
        $from = collect($original->to_recipients)->first()['name'] ?? collect($original->to_recipients)->first()['email'] ?? 'Unknown';
        
        return '<br><br><blockquote style="border-left: 2px solid #ccc; margin-left: 0; padding-left: 10px; color: #666;">' .
               '<p>On ' . $date . ', ' . htmlspecialchars($from) . ' wrote:</p>' .
               $original->html_body .
               '</blockquote>';
    }

    /**
     * Apply filters to email 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->where(function ($q) use ($filters) {
                $q->where('subject', 'like', "%{$filters['search']}%")
                  ->orWhere('html_body', 'like', "%{$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 email statistics.
     *
     * @return array
     */
    public function getStatistics(): array
    {
        return [
            'total' => EmailMessage::count(),
            'sent' => EmailMessage::sent()->count(),
            'failed' => EmailMessage::failed()->count(),
            'scheduled' => EmailMessage::scheduled()->count(),
            'today' => EmailMessage::whereDate('created_at', today())->count(),
            'this_month' => EmailMessage::whereMonth('created_at', now()->month)->count(),
            'templates_count' => EmailTemplate::active()->count(),
        ];
    }

    /**
     * Process uploaded CSV file for recipients.
     *
     * @param \Illuminate\Http\UploadedFile $file
     * @return array
     */
    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[] = [
                        'email' => $data[0],
                        'name' => $data[1] ?? null,
                    ];
                }
            }
            
            fclose($handle);
        }

        return $recipients;
    }
}
