<?php

namespace App\Domain\Notification\Services;

use App\Domain\Notification\Models\PushAnnouncement;
use App\Domain\Participant\Models\Participant;
use App\Domain\Shared\Models\DeviceToken;
use App\Domain\Training\Models\TrainingGroup;
use App\Models\User;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class AnnouncementBroadcastService
{
    public function __construct(private PushNotificationService $pushService) {}

    public function create(array $data, User $actor): PushAnnouncement
    {
        return DB::transaction(function () use ($data, $actor) {
            return PushAnnouncement::create([
                'academy_id' => $actor->academy_id,
                'title' => $data['title'],
                'body' => $data['body'],
                'target_type' => $data['target_type'],
                'target_ids' => $data['target_ids'] ?? [],
                'push_data' => $data['push_data'] ?? [],
                'status' => isset($data['scheduled_at']) ? 'scheduled' : 'pending',
                'scheduled_at' => $data['scheduled_at'] ?? null,
                'created_by' => $actor->id,
            ]);
        });
    }

    public function send(PushAnnouncement $announcement): void
    {
        $announcement->update(['status' => 'sending']);

        try {
            $tokens = $this->resolveTargetTokens($announcement);

            if ($tokens->isEmpty()) {
                $announcement->update([
                    'status' => 'sent',
                    'sent_count' => 0,
                    'sent_at' => now(),
                ]);
                return;
            }

            $chunks = $tokens->chunk(500);
            $totalSent = 0;
            $totalFailed = 0;

            foreach ($chunks as $chunk) {
                $result = $this->pushService->sendBroadcast(
                    $chunk->toArray(),
                    $announcement->title,
                    $announcement->body,
                    array_merge($announcement->push_data, [
                        'type' => 'announcement',
                        'announcement_uuid' => $announcement->uuid,
                    ]),
                    $announcement->academy_id,
                    'announcement.broadcast'
                );

                $totalSent += $result['success'];
                $totalFailed += $result['failed'];
            }

            $announcement->update([
                'status' => 'sent',
                'sent_count' => $totalSent,
                'failed_count' => $totalFailed,
                'sent_at' => now(),
            ]);

            Log::info('[Broadcast] Sent announcement', [
                'uuid' => $announcement->uuid,
                'sent' => $totalSent,
                'failed' => $totalFailed,
            ]);
        } catch (\Throwable $e) {
            $announcement->update(['status' => 'failed']);
            Log::error('[Broadcast] Failed', ['uuid' => $announcement->uuid, 'error' => $e->getMessage()]);
            throw $e;
        }
    }

    public function sendImmediate(array $data, User $actor): PushAnnouncement
    {
        $announcement = $this->create($data, $actor);
        $this->send($announcement);
        return $announcement->refresh();
    }

    private function resolveTargetTokens(PushAnnouncement $announcement): Collection
    {
        $userIds = $this->resolveTargetUserIds($announcement);

        if ($userIds->isEmpty()) {
            return collect();
        }

        return DeviceToken::withoutGlobalScope('academy')
            ->where('academy_id', $announcement->academy_id)
            ->whereIn('user_id', $userIds)
            ->where('is_active', true)
            ->pluck('device_token');
    }

    private function resolveTargetUserIds(PushAnnouncement $announcement): Collection
    {
        return match ($announcement->target_type) {
            'all' => $this->getAllParentUserIds($announcement->academy_id),
            'group' => $this->getGroupParentUserIds($announcement->target_ids, $announcement->academy_id),
            'branch' => $this->getBranchParentUserIds($announcement->target_ids, $announcement->academy_id),
            'program' => $this->getProgramParentUserIds($announcement->target_ids, $announcement->academy_id),
            default => collect(),
        };
    }

    private function getAllParentUserIds(int $academyId): Collection
    {
        return DB::table('guardians')
            ->where('academy_id', $academyId)
            ->whereNotNull('user_id')
            ->pluck('user_id')
            ->unique();
    }

    private function getGroupParentUserIds(array $groupIds, int $academyId): Collection
    {
        $participantIds = DB::table('enrollments')
            ->where('academy_id', $academyId)
            ->whereIn('training_group_id', $groupIds)
            ->where('status', 'active')
            ->pluck('participant_id');

        return $this->getUserIdsFromParticipants($participantIds, $academyId);
    }

    private function getBranchParentUserIds(array $branchIds, int $academyId): Collection
    {
        $participantIds = DB::table('participants')
            ->where('academy_id', $academyId)
            ->whereIn('branch_id', $branchIds)
            ->where('status', 'active')
            ->pluck('id');

        return $this->getUserIdsFromParticipants($participantIds, $academyId);
    }

    private function getProgramParentUserIds(array $programIds, int $academyId): Collection
    {
        $groupIds = DB::table('training_groups')
            ->where('academy_id', $academyId)
            ->whereIn('training_program_id', $programIds)
            ->pluck('id');

        return $this->getGroupParentUserIds($groupIds->toArray(), $academyId);
    }

    private function getUserIdsFromParticipants(Collection $participantIds, int $academyId): Collection
    {
        if ($participantIds->isEmpty()) {
            return collect();
        }

        return DB::table('guardian_participant')
            ->join('guardians', 'guardians.id', '=', 'guardian_participant.guardian_id')
            ->whereIn('guardian_participant.participant_id', $participantIds)
            ->where('guardians.academy_id', $academyId)
            ->whereNotNull('guardians.user_id')
            ->pluck('guardians.user_id')
            ->unique();
    }
}
