<?php

namespace App\Services;

use App\Enums\SmsSendStatus;
use App\Jobs\SendSmsToCouponJob;
use App\Models\Coupon;
use App\Models\SmsCampaign;
use App\Models\SmsSendLog;
use App\Models\User;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\Bus;
use Illuminate\Support\Facades\DB;

class SmsBroadcastService
{
    /**
     * @param  array<int, int|string>  $selectedIds
     * @param  array<int, int|string>  $excludedIds
     * @return Collection<int, Coupon>
     */
    public function resolveRecipients(bool $sendToAll, array $selectedIds = [], array $excludedIds = []): Collection
    {
        $excludedIds = array_values(array_unique(array_map('intval', $excludedIds)));

        if ($sendToAll) {
            $query = Coupon::query();
        } else {
            $selectedIds = array_values(array_unique(array_map('intval', $selectedIds)));
            $query = Coupon::query()->whereIn('id', $selectedIds);
        }

        if ($excludedIds !== []) {
            $query->whereNotIn('id', $excludedIds);
        }

        return $query->orderBy('id')->get();
    }

    public function startCampaign(
        User $admin,
        string $message,
        bool $sendToAll,
        array $selectedIds = [],
        array $excludedIds = [],
    ): SmsCampaign {
        $recipients = $this->resolveRecipients($sendToAll, $selectedIds, $excludedIds);
        $mode = $sendToAll ? 'all' : 'selected';

        return DB::transaction(function () use ($admin, $message, $mode, $recipients): SmsCampaign {
            $campaign = SmsCampaign::query()->create([
                'message' => $message,
                'mode' => $mode,
                'recipient_count' => $recipients->count(),
                'created_by' => $admin->id,
            ]);

            $jobs = [];

            foreach ($recipients as $coupon) {
                if (! is_valid_coupon_phone($coupon->phone)) {
                    $this->createLog($campaign, $coupon, SmsSendStatus::SkippedInvalid);
                    $campaign->increment('skipped_count');

                    continue;
                }

                $this->createLog($campaign, $coupon, SmsSendStatus::Pending);
                $jobs[] = new SendSmsToCouponJob($campaign, $coupon);
            }

            if ($jobs === []) {
                $campaign->update(['completed_at' => now()]);

                return $campaign->fresh();
            }

            Bus::chain($jobs)
                ->onQueue('sms')
                ->dispatch();

            return $campaign->fresh();
        });
    }

    protected function createLog(SmsCampaign $campaign, Coupon $coupon, SmsSendStatus $status): SmsSendLog
    {
        return SmsSendLog::query()->create([
            'sms_campaign_id' => $campaign->id,
            'coupon_id' => $coupon->id,
            'phone' => $coupon->phone,
            'message' => $campaign->message,
            'status' => $status,
            'attempted_at' => $status === SmsSendStatus::Pending ? null : now(),
        ]);
    }
}
