<?php

namespace App\Sms\Provider;

use Psr\Log\LoggerInterface;
use Symfony\Contracts\HttpClient\Exception\TransportExceptionInterface;
use Symfony\Contracts\HttpClient\HttpClientInterface;

class KavehNegarProvider implements SmsProviderInterface
{
    private const BASE = 'https://api.kavenegar.com/v1';

    public function __construct(
        private readonly HttpClientInterface  $httpClient,
        private readonly LoggerInterface      $logger,
        // API key comes only from env `%env(default::KAVENEGAR_API_KEY)%`; null when unset.
        private readonly ?string $apiKey = null,
        private readonly ?string $sender = null,
    ) {}

    private function key(): string   { return $this->apiKey ?? ''; }
    private function sender(): string { return $this->sender ?? ''; }

    public function getName(): string { return 'kavenegar'; }

    // Kavenegar فقط GET با query را می‌پذیرد (مطابق مستند رسمی). هیچ درخواست POST/body نباید ساخته شود.
    // GET بدنه ندارد پس curl هدر `Expect: 100-continue` نمی‌فرستد (رفع idle timeout) و حجم درخواست کوچک می‌ماند.
    private const MAX_ATTEMPTS = 3;

    /** یک GET به کاوه‌نگار با query params و retry برای خطاهای گذرا؛ آرایهٔ پاسخ را برمی‌گرداند. */
    private function get(string $path, array $query): array
    {
        $url = self::BASE . '/' . $this->key() . $path;
        for ($attempt = 1; ; $attempt++) {
            try {
                return $this->httpClient->request('GET', $url, [
                    'query'        => $query,
                    'timeout'      => 15,
                    'max_duration' => 30,
                    'proxy'        => null,
                ])->toArray();
            } catch (TransportExceptionInterface $e) {
                if ($attempt >= self::MAX_ATTEMPTS) {
                    throw $e;
                }
                usleep(500_000 * $attempt);
            }
        }
    }

    public function send(string $mobile, string $message): bool
    {
        try {
            $data = $this->get('/sms/send.json', [
                'receptor' => $mobile,
                'message'  => $message,
                'sender'   => $this->sender(),
            ]);
            return ($data['return']['status'] ?? 0) === 200;
        } catch (\Throwable $e) {
            $this->logger->error(sprintf('SMS send failed (kavenegar): %s @ %s:%d', $e->getMessage(), $e->getFile(), $e->getLine()), ['exception' => $e, 'mobile' => $mobile]);
            return false;
        }
    }

    // token/token2/token3 مقدارِ دارای فاصله را رد می‌کنند؛ فاصله را با نیم‌فاصله (ZWNJ) جایگزین می‌کنیم.
    private const NO_SPACE_SLOTS = ['token', 'token2', 'token3'];

    private function forSlot(string $slot, string $value): string
    {
        if (!in_array($slot, self::NO_SPACE_SLOTS, true)) {
            return $value;
        }
        return preg_replace('/\s+/u', "\u{200C}", $value) ?? $value;
    }

    public function sendTemplate(string $mobile, string $templateCode, array $vars): bool
    {
        try {
            $params = ['receptor' => $mobile, 'template' => $templateCode];
            // Kavenegar token slots: token/token2/token3 reject spaces; token10/token20 allow them.
            // If every key is an explicit slot name, honor it (e.g. site with spaces → token10);
            // otherwise fall back to positional mapping (token, token2, token3, ...).
            $slots = ['token', 'token2', 'token3', 'token10', 'token20'];
            if ($vars !== [] && array_keys($vars) !== range(0, count($vars) - 1)
                && array_diff(array_keys($vars), $slots) === []) {
                foreach ($vars as $slot => $v) {
                    $params[$slot] = $this->forSlot($slot, (string) $v);
                }
            } else {
                foreach (array_values($vars) as $i => $v) {
                    $slot = 'token' . ($i > 0 ? $i + 1 : '');
                    $params[$slot] = $this->forSlot($slot, (string) $v);
                }
            }
            $data = $this->get('/verify/lookup.json', $params);
            return ($data['return']['status'] ?? 0) === 200;
        } catch (\Throwable $e) {
            $this->logger->error(sprintf('SMS sendTemplate failed (kavenegar): %s @ %s:%d', $e->getMessage(), $e->getFile(), $e->getLine()), ['exception' => $e, 'mobile' => $mobile, 'template' => $templateCode]);
            return false;
        }
    }
}
