<?php

namespace App\Services;

use App\Jobs\SendBulkEmailJob;
use App\Mail\BulkEmail;
use App\Models\EmailLog;
use App\Models\User;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Str;

class EmailService
{
    /**
     * Send a single email to one user (immediate, no queue)
     */
    public function sendToUser(User $user, string $subject, string $body, ?int $sentBy = null): EmailLog
    {
        $emailLog = EmailLog::create([
            'batch_id' => null,
            'user_id' => $user->id,
            'recipient_email' => $user->email,
            'recipient_name' => $user->name,
            'subject' => $subject,
            'body' => $body,
            'type' => EmailLog::TYPE_SINGLE,
            'status' => EmailLog::STATUS_PENDING,
            'sent_by' => $sentBy,
        ]);

        try {
            Mail::to($user->email)
                ->send(new BulkEmail($subject, $body, $user->name ?? 'User'));

            $emailLog->markAsSent();
            Log::info("Single email sent successfully to {$user->email} [EmailLog #{$emailLog->id}]");
        } catch (\Exception $e) {
            $emailLog->markAsFailed($e->getMessage());
            Log::error("Failed to send single email to {$user->email}: " . $e->getMessage());
        }

        return $emailLog;
    }

    /**
     * Send a single email to an arbitrary email address (no user model required)
     */
    public function sendToEmail(string $email, string $name, string $subject, string $body, ?int $sentBy = null): EmailLog
    {
        $emailLog = EmailLog::create([
            'batch_id' => null,
            'user_id' => null,
            'recipient_email' => $email,
            'recipient_name' => $name,
            'subject' => $subject,
            'body' => $body,
            'type' => EmailLog::TYPE_SINGLE,
            'status' => EmailLog::STATUS_PENDING,
            'sent_by' => $sentBy,
        ]);

        try {
            Mail::to($email)
                ->send(new BulkEmail($subject, $body, $name));

            $emailLog->markAsSent();
            Log::info("Single email sent successfully to {$email} [EmailLog #{$emailLog->id}]");
        } catch (\Exception $e) {
            $emailLog->markAsFailed($e->getMessage());
            Log::error("Failed to send single email to {$email}: " . $e->getMessage());
        }

        return $emailLog;
    }

    /**
     * Send bulk emails to multiple users via queue
     *
     * @param array $userIds Array of user IDs
     * @param string $subject Email subject
     * @param string $body Email body (HTML allowed)
     * @param int|null $sentBy The admin/user ID who initiated the send
     * @param int $delayBetweenSeconds Delay between each queued email (in seconds)
     * @return array{batch_id: string, total: int, queued: int, failed: int}
     */
    public function sendBulk(array $userIds, string $subject, string $body, ?int $sentBy = null, int $delayBetweenSeconds = 10): array
    {
        $batchId = $this->generateBatchId();
        $users = User::whereIn('id', $userIds)->get();

        $totalQueued = 0;
        $totalFailed = 0;
        $delayAccumulator = 0;

        foreach ($users as $user) {
            try {
                // Create the email log entry
                $emailLog = EmailLog::create([
                    'batch_id' => $batchId,
                    'user_id' => $user->id,
                    'recipient_email' => $user->email,
                    'recipient_name' => $user->name,
                    'subject' => $subject,
                    'body' => $body,
                    'type' => EmailLog::TYPE_BULK,
                    'status' => EmailLog::STATUS_QUEUED,
                    'sent_by' => $sentBy,
                    'queued_at' => now(),
                ]);

                // Dispatch the job with delay to avoid rate limiting
                SendBulkEmailJob::dispatch(
                    $emailLog->id,
                    $user->email,
                    $user->name ?? 'User',
                    $subject,
                    $body
                )->delay(now()->addSeconds($delayAccumulator));

                $delayAccumulator += $delayBetweenSeconds;
                $totalQueued++;

                Log::info("Queued email for {$user->email} [Batch: {$batchId}, EmailLog #{$emailLog->id}]");
            } catch (\Exception $e) {
                // Log creation failure
                $totalFailed++;
                Log::error("Failed to queue email for {$user->email} [Batch: {$batchId}]: " . $e->getMessage());

                // Still create a log entry for tracking
                EmailLog::create([
                    'batch_id' => $batchId,
                    'user_id' => $user->id,
                    'recipient_email' => $user->email,
                    'recipient_name' => $user->name,
                    'subject' => $subject,
                    'body' => $body,
                    'type' => EmailLog::TYPE_BULK,
                    'status' => EmailLog::STATUS_FAILED,
                    'error_message' => 'Failed to queue: ' . $e->getMessage(),
                    'sent_by' => $sentBy,
                    'failed_at' => now(),
                ]);
            }
        }

        return [
            'batch_id' => $batchId,
            'total' => $users->count(),
            'queued' => $totalQueued,
            'failed' => $totalFailed,
            'estimated_completion' => now()->addSeconds($delayAccumulator)->format('Y-m-d H:i:s'),
        ];
    }

    /**
     * Send bulk emails to ALL users via queue
     */
    public function sendToAllUsers(string $subject, string $body, ?int $sentBy = null, int $delayBetweenSeconds = 10): array
    {
        $allUserIds = User::where('status', 1)->pluck('id')->toArray();
        return $this->sendBulk($allUserIds, $subject, $body, $sentBy, $delayBetweenSeconds);
    }

    /**
     * Get batch status/statistics
     */
    public function getBatchStatus(string $batchId): array
    {
        $logs = EmailLog::batch($batchId);

        return [
            'batch_id' => $batchId,
            'total' => $logs->count(),
            'pending' => (clone $logs)->where('status', EmailLog::STATUS_PENDING)->count(),
            'queued' => (clone $logs)->where('status', EmailLog::STATUS_QUEUED)->count(),
            'sent' => (clone $logs)->where('status', EmailLog::STATUS_SENT)->count(),
            'failed' => (clone $logs)->where('status', EmailLog::STATUS_FAILED)->count(),
        ];
    }

    /**
     * Retry all failed emails in a batch
     */
    public function retryBatch(string $batchId, int $delayBetweenSeconds = 10): array
    {
        $failedLogs = EmailLog::batch($batchId)
            ->where('status', EmailLog::STATUS_FAILED)
            ->get();

        $retried = 0;
        $delayAccumulator = 0;

        foreach ($failedLogs as $emailLog) {
            try {
                $emailLog->incrementRetry();
                $emailLog->markAsQueued();

                SendBulkEmailJob::dispatch(
                    $emailLog->id,
                    $emailLog->recipient_email,
                    $emailLog->recipient_name ?? 'User',
                    $emailLog->subject,
                    $emailLog->body
                )->delay(now()->addSeconds($delayAccumulator));

                $delayAccumulator += $delayBetweenSeconds;
                $retried++;
            } catch (\Exception $e) {
                Log::error("Failed to retry email #{$emailLog->id}: " . $e->getMessage());
            }
        }

        return [
            'batch_id' => $batchId,
            'total_failed' => $failedLogs->count(),
            'retried' => $retried,
        ];
    }

    /**
     * Retry a single failed email
     */
    public function retrySingle(int $emailLogId): EmailLog
    {
        $emailLog = EmailLog::findOrFail($emailLogId);

        $emailLog->incrementRetry();
        $emailLog->markAsQueued();

        SendBulkEmailJob::dispatch(
            $emailLog->id,
            $emailLog->recipient_email,
            $emailLog->recipient_name ?? 'User',
            $emailLog->subject,
            $emailLog->body
        );

        return $emailLog->fresh();
    }

    /**
     * Generate a unique batch ID
     */
    protected function generateBatchId(): string
    {
        return 'batch_' . Str::uuid()->toString();
    }
}
