<?php

namespace App\Services\Notification;

use App\Models\BroadcastCampaign;
use App\Models\BroadcastRecipient;
use App\Models\NotificationTemplate;
use App\Models\User;
use App\Models\UserNotificationSetting;
use App\Services\Core\TelegramBot;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use DefStudio\Telegraph\Models\TelegraphBot;

class NotificationService
{
    public function __construct(
        private TelegramBot $telegramBot
    ) {}

    /**
     * Create a new broadcast campaign
     */
    public function createCampaign(array $data): BroadcastCampaign
    {
        $campaign = BroadcastCampaign::create($data);
        
        // Generate recipients based on target filters
        $this->generateRecipients($campaign);
        
        return $campaign;
    }

    /**
     * Generate recipients for a campaign based on filters
     */
    public function generateRecipients(BroadcastCampaign $campaign): int
    {
        $query = $this->buildUserQuery($campaign->target_filters ?? []);
        
        $userIds = $query->pluck('id');
        $recipients = [];
        
        foreach ($userIds as $userId) {
            $recipients[] = [
                'campaign_id' => $campaign->id,
                'user_id' => $userId,
                'status' => BroadcastRecipient::STATUS_PENDING,
                'created_at' => now(),
                'updated_at' => now(),
            ];
        }
        
        // Bulk insert recipients
        if (!empty($recipients)) {
            BroadcastRecipient::insert($recipients);
        }
        
        // Update campaign total count
        $campaign->update(['total_recipients' => count($recipients)]);
        
        return count($recipients);
    }

    /**
     * Build user query based on filters
     */
    private function buildUserQuery(array $filters): Builder
    {
        $query = User::query()
            ->where('notifications_enabled', true)
            ->whereHas('notificationSettings', function ($q) {
                $q->where('broadcasts_enabled', true)
                  ->whereNull('opted_out_at');
            });

        // Apply filters
        if (isset($filters['active_since'])) {
            $query->where('last_activity_at', '>=', $filters['active_since']);
        }

        if (isset($filters['registered_since'])) {
            $query->where('created_at', '>=', $filters['registered_since']);
        }

        if (isset($filters['telegram_id'])) {
            $query->whereIn('telegram_id', (array) $filters['telegram_id']);
        }

        if (isset($filters['exclude_recent_broadcast'])) {
            $hours = $filters['exclude_recent_broadcast'];
            $query->where(function ($q) use ($hours) {
                $q->whereNull('last_broadcast_at')
                  ->orWhere('last_broadcast_at', '<', now()->subHours($hours));
            });
        }

        return $query;
    }

    /**
     * Send a campaign
     */
    public function sendCampaign(BroadcastCampaign $campaign): bool
    {
        if (!$campaign->start()) {
            return false;
        }

        // Dispatch job to send messages
        dispatch(new \App\Jobs\Notification\SendBroadcastJob($campaign->id));
        
        return true;
    }

    /**
     * Send messages to pending recipients
     */
    public function processCampaign(BroadcastCampaign $campaign, int $batchSize = 50): array
    {
        $stats = ['sent' => 0, 'failed' => 0, 'skipped' => 0];
        
        $recipients = $campaign->recipients()
            ->pending()
            ->with('user')
            ->limit($batchSize)
            ->get();

        foreach ($recipients as $recipient) {
            try {
                $result = $this->sendToRecipient($campaign, $recipient);
                
                if ($result['success']) {
                    $recipient->markAsSent($result['data'] ?? []);
                    $stats['sent']++;
                } else {
                    $recipient->markAsFailed($result['error'] ?? 'Unknown error');
                    $stats['failed']++;
                }
                
                // Update user's last broadcast timestamp
                $recipient->user->update(['last_broadcast_at' => now()]);
                
            } catch (\Exception $e) {
                $recipient->markAsFailed($e->getMessage());
                $stats['failed']++;
                
                Log::error("Broadcast delivery failed", [
                    'campaign_id' => $campaign->id,
                    'user_id' => $recipient->user_id,
                    'error' => $e->getMessage()
                ]);
            }
            
            // Rate limiting - pause between messages
            usleep(100000); // 0.1 second delay
        }

        // Update campaign statistics
        $this->updateCampaignStats($campaign);
        
        // Check if campaign is complete
        if ($campaign->recipients()->pending()->count() === 0) {
            $campaign->complete();
        }

        return $stats;
    }

    /**
     * Send message to individual recipient
     */
    private function sendToRecipient(BroadcastCampaign $campaign, BroadcastRecipient $recipient): array
    {
        $user = $recipient->user;
        
        if (!$user->telegram_id) {
            return ['success' => false, 'error' => 'No Telegram ID'];
        }

        try {
            $bot = TelegraphBot::first();
            
            if (!$bot) {
                return ['success' => false, 'error' => 'No bot configured'];
            }

            $message = $this->prepareMessage($campaign, $user);
            
            // Send message
            $response = $bot->message($user->telegram_id)
                ->html($message['text']);

            // Add inline keyboard if present
            if (!empty($message['keyboard'])) {
                $response->keyboard($message['keyboard']);
            }

            $result = $response->send();
            
            return [
                'success' => true,
                'data' => [
                    'message_id' => $result->telegraphMessageId(),
                    'sent_at' => now()->toISOString()
                ]
            ];
            
        } catch (\Exception $e) {
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }

    /**
     * Prepare message content for user
     */
    private function prepareMessage(BroadcastCampaign $campaign, User $user): array
    {
        $message = $campaign->message;
        
        // Replace user variables
        $variables = [
            'name' => $user->name ?? 'کاربر',
            'first_name' => $user->first_name ?? 'کاربر',
            'user_id' => $user->id,
        ];

        foreach ($variables as $key => $value) {
            $message = str_replace("{{$key}}", $value, $message);
        }

        return [
            'text' => $message,
            'keyboard' => $campaign->inline_keyboard,
            'attachments' => $campaign->attachments,
        ];
    }

    /**
     * Update campaign statistics
     */
    private function updateCampaignStats(BroadcastCampaign $campaign): void
    {
        $stats = $campaign->recipients()
            ->selectRaw('
                COUNT(CASE WHEN status = ? THEN 1 END) as sent_count,
                COUNT(CASE WHEN status = ? THEN 1 END) as failed_count
            ', [BroadcastRecipient::STATUS_SENT, BroadcastRecipient::STATUS_FAILED])
            ->first();

        $campaign->update([
            'sent_count' => $stats->sent_count ?? 0,
            'failed_count' => $stats->failed_count ?? 0,
        ]);
    }

    /**
     * Send notification using template
     */
    public function sendTemplate(
        string $templateKey, 
        array $recipients, 
        array $variables = []
    ): array {
        $template = NotificationTemplate::active()
            ->byKey($templateKey)
            ->first();

        if (!$template) {
            throw new \Exception("Template not found: {$templateKey}");
        }

        // Validate variables
        $validation = $template->validateVariables($variables);
        if (!$validation['valid']) {
            throw new \Exception('Missing template variables: ' . implode(', ', $validation['missing']));
        }

        $results = [];
        
        foreach ($recipients as $recipient) {
            $user = is_object($recipient) ? $recipient : User::find($recipient);
            
            if (!$user || !$user->telegram_id) {
                $results[] = ['user_id' => $user?->id, 'success' => false, 'error' => 'Invalid user'];
                continue;
            }

            try {
                $message = $template->render(array_merge($variables, [
                    'name' => $user->name ?? 'کاربر',
                    'first_name' => $user->first_name ?? 'کاربر',
                ]));

                $bot = TelegraphBot::first();
                $response = $bot->message($user->telegram_id)->html($message);
                
                if ($template->inline_keyboard) {
                    $response->keyboard($template->inline_keyboard);
                }

                $result = $response->send();
                
                $results[] = [
                    'user_id' => $user->id,
                    'success' => true,
                    'message_id' => $result->telegraphMessageId()
                ];
                
            } catch (\Exception $e) {
                $results[] = [
                    'user_id' => $user->id,
                    'success' => false,
                    'error' => $e->getMessage()
                ];
            }
        }

        return $results;
    }

    /**
     * Get campaign statistics
     */
    public function getCampaignStats(int $campaignId): array
    {
        return Cache::remember("campaign_stats_{$campaignId}", 300, function () use ($campaignId) {
            $campaign = BroadcastCampaign::find($campaignId);
            
            if (!$campaign) {
                return [];
            }

            $stats = $campaign->recipients()
                ->selectRaw('
                    status,
                    COUNT(*) as count,
                    DATE(created_at) as date
                ')
                ->groupBy(['status', 'date'])
                ->get()
                ->groupBy('date');

            return [
                'campaign' => $campaign,
                'daily_stats' => $stats,
                'total_recipients' => $campaign->total_recipients,
                'sent_count' => $campaign->sent_count,
                'failed_count' => $campaign->failed_count,
                'progress' => $campaign->progress,
                'success_rate' => $campaign->success_rate,
            ];
        });
    }

    /**
     * Pause campaign
     */
    public function pauseCampaign(int $campaignId): bool
    {
        $campaign = BroadcastCampaign::find($campaignId);
        return $campaign ? $campaign->pause() : false;
    }

    /**
     * Resume campaign
     */
    public function resumeCampaign(int $campaignId): bool
    {
        $campaign = BroadcastCampaign::find($campaignId);
        
        if (!$campaign || !$campaign->start()) {
            return false;
        }

        // Dispatch job to continue sending
        dispatch(new \App\Jobs\Notification\SendBroadcastJob($campaign->id));
        
        return true;
    }

    /**
     * Cancel campaign
     */
    public function cancelCampaign(int $campaignId): bool
    {
        $campaign = BroadcastCampaign::find($campaignId);
        return $campaign ? $campaign->cancel() : false;
    }

    /**
     * Get notification settings for user
     */
    public function getUserSettings(User $user): UserNotificationSetting
    {
        return UserNotificationSetting::forUser($user);
    }

    /**
     * Update user notification preferences
     */
    public function updateUserSettings(User $user, array $settings): UserNotificationSetting
    {
        $notificationSetting = $this->getUserSettings($user);
        $notificationSetting->update($settings);
        
        return $notificationSetting;
    }
}