<?php

namespace App\Sms\Service;

use App\Sms\Entity\SmsLog;
use App\Sms\Entity\SmsMessageTemplate;
use App\Sms\Message\SendSmsMessage;
use App\Sms\Provider\KavehNegarProvider;
use App\Sms\Provider\RanginehProvider;
use App\Sms\Provider\SmsProviderInterface;
use App\Sms\Repository\SmsLogRepository;
use App\Sms\Repository\SmsMessageTemplateRepository;
use Psr\Log\LoggerInterface;
use Symfony\Component\Messenger\MessageBusInterface;

class SmsService
{
    private array $providers;

    public function __construct(
        private readonly KavehNegarProvider           $kavenegar,
        private readonly RanginehProvider             $rangineh,
        private readonly SmsLogRepository             $logRepo,
        private readonly MessageBusInterface          $bus,
        private readonly SmsMessageTemplateRepository $messageTemplateRepo,
        private readonly SmsTextResolver              $textResolver,
        private readonly LoggerInterface              $logger,
    ) {
        $this->providers = [
            'kavenegar' => $kavenegar,
            'rangineh'  => $rangineh,
        ];
    }

    /**
     * ارسال یک پیامک سیستمی از طریق تمپلت VerifyLookup کاوه‌نگار.
     * فقط تگ + متغیرهای منطقی می‌گیرد؛ نام تمپلت و نگاشت token از SmsMessageTemplate (یا DEFAULTS) خوانده می‌شود.
     *
     * @param array<string,string|int> $vars متغیرهای منطقی (کلیدهای {...} بدنه)
     */
    public function dispatchTemplate(string $tag, string $mobile, array $vars = [], string $provider = 'kavenegar'): void
    {
        $tpl = $this->messageTemplateRepo->findByTag($tag);
        $kaveTemplate = $tpl?->getKavenegarTemplate()
            ?? (SmsMessageTemplate::DEFAULTS[$tag]['kavenegar_template'] ?? null);
        $tokenMap = $tpl?->getTokenMap()
            ?: (SmsMessageTemplate::DEFAULTS[$tag]['token_map'] ?? []);

        $message = $this->textResolver->resolve($tag, $vars);

        // سیاست lookup-only: بدون الگوی کاوه‌نگار نباید با sms/send.json خام ارسال شود.
        if ($kaveTemplate === null || $tokenMap === []) {
            $this->logger->error(sprintf('SMS tag "%s" has no Kavenegar template/token_map; refusing raw send', $tag), [
                'tag' => $tag, 'mobile' => $mobile,
            ]);
            $this->logRepo->save(new SmsLog($mobile, $message, $provider, false, $tag));
            return;
        }

        $slotVars = [];
        foreach ($tokenMap as $logicalKey => $slot) {
            if (array_key_exists($logicalKey, $vars)) {
                $slotVars[$slot] = (string) $vars[$logicalKey];
            }
        }

        $this->dispatchAsync($mobile, $message, $provider, templateVars: $slotVars, templateCode: $kaveTemplate, tag: $tag);
    }

    public function dispatchAsync(
        string $mobile,
        string $message,
        string $provider = 'kavenegar',
        ?string $templateUuid = null,
        array $templateVars = [],
        ?string $templateCode = null,
        string $tag = SmsLog::TAG_GLOBAL,
    ): void {
        $this->bus->dispatch(new SendSmsMessage(
            $mobile, $message, $provider, $templateUuid, $templateVars, $templateCode, $tag
        ));
    }

    public function sendNow(SendSmsMessage $msg): bool
    {
        $provider = $this->resolveProvider($msg->provider);

        // سیاست lookup-only: پیام بدون templateCode نباید با sms/send.json خام برود.
        if ($msg->templateCode === null) {
            $this->logger->error('SMS refused: no templateCode (lookup-only policy)', [
                'mobile' => $msg->mobile, 'tag' => $msg->tag,
            ]);
            $success = false;
        } else {
            $success = $provider->sendTemplate($msg->mobile, $msg->templateCode, $msg->templateVars);
        }

        $log = new SmsLog($msg->mobile, $msg->message, $provider->getName(), $success, $msg->tag);
        if ($msg->templateUuid) $log->setTemplateUuid($msg->templateUuid);
        if ($msg->templateCode) $log->setTemplateCode($msg->templateCode);
        $this->logRepo->save($log);

        return $success;
    }

    private function resolveProvider(string $name): SmsProviderInterface
    {
        return $this->providers[$name] ?? $this->kavenegar;
    }
}
