<?php

namespace App\Modules\Sms\Twilio;

use App\Core\Sms\AbstractSms;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

class Twilio extends AbstractSms
{
    public function sendSms(array $params): array
    {
        $to = $params['to'] ?? null;
        $content = $params['content'] ?? null;

        if (! $to || ! $content) {
            return ['success' => false, 'error' => 'Required fields missing (to, content)'];
        }

        if ($this->isTestMode) {
            Log::info('Twilio TEST SMS', ['to' => $to, 'content' => $content]);

            return ['success' => true, 'message_id' => 'test_tw_' . uniqid()];
        }

        try {
            $accountSid = $this->getConfig('api_key');
            $authToken = $this->getConfig('api_secret');
            $from = $this->getConfig('from');

            if (empty($accountSid) || empty($authToken) || empty($from)) {
                return ['success' => false, 'error' => 'Twilio configuration is incomplete'];
            }

            $response = Http::asForm()
                ->withBasicAuth($accountSid, $authToken)
                ->post("https://api.twilio.com/2010-04-01/Accounts/{$accountSid}/Messages.json", [
                    'From' => $from,
                    'To' => $this->formatPhoneNumber($to),
                    'Body' => $content,
                ]);

            $data = $response->json();

            // HTTP transport hatası (4xx/5xx). Twilio 'message' + 'code' döner;
            // ham gövde loglanmaz (güvenlik) — yalnız yapılandırılmış alanlar.
            if (! $response->successful()) {
                $apiError = is_array($data) ? ($data['message'] ?? 'HTTP ' . $response->status()) : 'HTTP ' . $response->status();
                Log::error('Twilio SMS failed', ['status' => $response->status(), 'code' => is_array($data) ? ($data['code'] ?? null) : null]);

                return ['success' => false, 'error' => 'Twilio API error: ' . $apiError];
            }

            // HTTP 200/201 gövde-statü kapısı: Twilio 201'de bile mesaj 'failed'/
            // 'undelivered' olabilir → false-success YASAK. 'sid' yoksa UYDURMA.
            $messageId = is_array($data) ? ($data['sid'] ?? null) : null;
            $msgStatus = is_array($data) ? ($data['status'] ?? null) : null;

            if (empty($messageId) || in_array($msgStatus, ['failed', 'undelivered'], true)) {
                Log::error('Twilio SMS rejected', ['status' => $msgStatus, 'error_code' => is_array($data) ? ($data['error_code'] ?? null) : null]);

                return ['success' => false, 'error' => 'Twilio rejected message (status: ' . ($msgStatus ?? 'unknown') . ')'];
            }

            Log::info('SMS sent successfully via Twilio', ['id' => $messageId, 'status' => $msgStatus]);

            return ['success' => true, 'message_id' => $messageId];
        } catch (\Throwable $e) {
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }

    public function testConnection(array $params): array
    {
        try {
            $accountSid = $this->getConfig('api_key');
            $authToken = $this->getConfig('api_secret');

            if (! $accountSid || ! $authToken) {
                throw new \Exception('Twilio Account SID and Auth Token are required');
            }

            if ($this->isTestMode) {
                return ['success' => true, 'message' => 'Test mode: credentials provided, verified on first send'];
            }

            // Gerçek probe: hesap kaynağını çek. Geçerli kimlikte 200 + status döner.
            $response = Http::withBasicAuth($accountSid, $authToken)
                ->get("https://api.twilio.com/2010-04-01/Accounts/{$accountSid}.json");

            if ($response->successful()) {
                return ['success' => true, 'message' => 'Twilio credentials verified'];
            }

            $data = $response->json();
            $msg = is_array($data) ? ($data['message'] ?? 'HTTP ' . $response->status()) : 'HTTP ' . $response->status();

            return ['success' => false, 'error' => 'Twilio verification failed: ' . $msg];
        } catch (\Throwable $e) {
            return ['success' => false, 'error' => $e->getMessage()];
        }
    }

    /**
     * Twilio status callback: MessageSid + MessageStatus (+ErrorCode).
     * Yalnız final durumlar normalize edilir; ara durumlar (queued/sent) yok sayılır.
     *
     * GÜVENLİK: İmza doğrulaması CALLER'ın işidir (verifyWebhookSignature +
     * SmsWebhookController). Bu metod yalnız zaten-doğrulanmış payload'u parse eder
     * (WhatsApp/Plivo ile aynı sözleşme).
     */
    public function handleDeliveryReport(array $payload): ?array
    {
        if (empty($payload['MessageSid'])) {
            return null;
        }

        $status = $payload['MessageStatus'] ?? '';
        if (! in_array($status, ['delivered', 'failed', 'undelivered'], true)) {
            return null;
        }

        return [
            'provider_message_id' => $payload['MessageSid'],
            'status' => $status === 'delivered' ? 'delivered' : 'failed',
            'detail' => $payload['ErrorCode'] ?? null,
        ];
    }

    /**
     * X-Twilio-Signature doğrulaması — ham Request'ten (başlık + tam URL + POST gövdesi).
     * Şema: fullUrl + (POST params key ASC sıralı, key.value concat) → HMAC-SHA1(auth_token) → base64,
     * hash_equals ile sabit-zamanlı. İmza başlığı/token eksikse false (fail-closed).
     */
    public function verifyWebhookSignature(\Illuminate\Http\Request $request): ?bool
    {
        $authToken = $this->getConfig('api_secret');
        $signature = $request->header('X-Twilio-Signature');

        if (empty($authToken) || empty($signature)) {
            return false;
        }

        $params = $request->post();
        ksort($params);

        $data = $request->fullUrl();
        foreach ($params as $key => $value) {
            $data .= $key . (is_scalar($value) ? (string) $value : '');
        }

        $expected = base64_encode(hash_hmac('sha1', $data, $authToken, true));

        return hash_equals($expected, (string) $signature);
    }

    /**
     * DLR gerçekten implemente ediliyor (handleDeliveryReport + imza doğrulama) →
     * delivery_reports = true. Diğerleri AbstractSms default'undan miras.
     */
    public function getFeatures(): array
    {
        return array_merge(parent::getFeatures(), [
            'delivery_reports' => true,
        ]);
    }

    public function getConfigFields(): array
    {
        return [
            'api_key' => ['type' => 'text', 'label' => 'Account SID', 'required' => true, 'encrypted' => true],
            'api_secret' => ['type' => 'password', 'label' => 'Auth Token', 'required' => true, 'encrypted' => true],
            'from' => ['type' => 'text', 'label' => 'From Number/Sender ID', 'required' => true],
            'default_country_code' => ['type' => 'text', 'label' => 'Default Country Code (e.g. 90, 1)', 'required' => false],
            'test_mode' => ['type' => 'boolean', 'label' => 'Test Mode', 'default' => true],
        ];
    }

    protected function getModuleLabel(): string
    {
        return 'Twilio';
    }

    protected function getModuleDescription(): string
    {
        return 'Twilio SMS provider';
    }

    /**
     * Twilio KATI E.164 ('+' önekli) ister. Ülke kodu HARDCODE edilmez —
     * modül-config'ten default_country_code okunur (yoksa null → helper uydurmaz).
     * normalizeMsisdn '+' HARİÇ rakam döner; Twilio için başına '+' eklenir.
     */
    private function formatPhoneNumber(string $phone): string
    {
        $default = $this->getConfig('default_country_code');
        $default = ($default !== null && $default !== '') ? (string) $default : null;

        $digits = $this->normalizeMsisdn($phone, $default);

        if ($digits === '') {
            return '';
        }

        return '+' . $digits;
    }
}
