<?php

namespace App\Services\Sms;

use App\Models\SmsProfile;
use Illuminate\Support\Facades\Http;

class SmsApiSiClient implements SmsClient
{
    public function send(SmsProfile $profile, SmsMessage $message): SmsResult
    {
        $baseUrl = config('services.sms.providers.smsapi_si.base_url');
        $endpoint = config('services.sms.providers.smsapi_si.send_endpoint', '/poslji-sms');
        $timeout = (int) config('services.sms.providers.smsapi_si.timeout', 10);

        $url = rtrim($baseUrl, '/').$endpoint;

        $payload = [
            'un' => $profile->api_username,
            'ps' => $profile->decryptApiPassword() ?? '',
            // provider requires phone number in `from`
            'from' => isset($message->senderPhone) && $message->senderPhone !== '' ? urlencode($message->senderPhone) : '',
            'to' => $message->to,
            'm' => $message->content,
        ];

        // Log payload safely (mask password) for debugging; uses context so it will appear on supported channels
        $logPayload = $payload;
        if (isset($logPayload['ps'])) {
            $logPayload['ps'] = '***';
        }
        \Log::info('sms.send payload', ['payload' => $logPayload]);

        /*if (! empty($message->sender)) {
            $payload['sname'] = $message->sender;
        }*/
        // Default country code when not provided
        $payload['cc'] = urlencode(! empty($message->countryCode) ? $message->countryCode : '386');
        if ($message->deliveryReport) {
            $payload['dr'] = 1;
        }

        $response = Http::asForm()
            ->timeout($timeout)
            ->post($url, $payload);

        $body = trim((string) $response->body());
        $statusCode = $response->status();

        // Parse according to provider docs: ID##SMS_PRICE##FROM##TO
        // Success: 123##0.03##040123456##040654321
        // Failure: -1##ERROR_ID##FROM##TO
        $parts = array_values(array_filter(array_map(static fn ($s) => trim((string) $s), explode('##', $body)), static fn ($s) => $s !== ''));

        // Known error codes (Slovenian descriptions from docs)
        $errorMap = [
            1 => 'Napaka v validaciji.',
            2 => 'Sporočilo je predolgo ali prazno.',
            3 => 'Številka pošiljatelja ni pravilno tvorjena ali pa je prazna.',
            4 => 'Številka prejemnika ni pravilno tvorjena ali pa je prazna.',
            5 => 'Uporabnik nima dovolj kreditov.',
            6 => 'Napaka na serverju.',
            7 => 'Številka pošiljatelja ni registrirana.',
            8 => 'Referenca uporabnika ni veljavna.',
            9 => 'Koda države ni veljavna.',
            10 => 'ID pošiljatelja ni potrjen.',
            11 => 'Koda države ni podprta',
            12 => 'Številka pošiljatelja ni potrjena',
            13 => 'Številka oz. država pošiljatelja ne podpira MMS vsebin.',
            14 => 'MMS tip oblike (mime type) ni podprta',
            15 => 'MMS url je nedosegljiv ali datoteka ne obstaja.',
            16 => 'MMS vsebina je predolga',
        ];

        if ($response->successful() && ! empty($parts)) {
            // Failure shape
            if ($parts[0] === '-1') {
                $errorId = isset($parts[1]) ? (int) $parts[1] : null;
                $from = $parts[2] ?? null;
                $to = $parts[3] ?? null;

                return new SmsResult(
                    status: 'failed',
                    providerMessageId: null,
                    cost: null,
                    currency: null,
                    meta: [
                        'status_code' => $statusCode,
                        'body' => $body,
                        'parts' => $parts,
                        'error_id' => $errorId,
                        'error_message' => $errorId ? ($errorMap[$errorId] ?? 'Neznana napaka') : 'Neznana napaka',
                        'from' => $from,
                        'to' => $to,
                    ]
                );
            }

            // Success shape
            if (preg_match('/^\d+$/', $parts[0])) {
                $providerId = $parts[0];
                $price = null;
                if (isset($parts[1])) {
                    $priceStr = str_replace(',', '.', $parts[1]);
                    $price = is_numeric($priceStr) ? (float) $priceStr : null;
                }
                $from = $parts[2] ?? null;
                $to = $parts[3] ?? null;

                return new SmsResult(
                    status: 'sent',
                    providerMessageId: $providerId,
                    cost: $price,
                    currency: null,
                    meta: [
                        'status_code' => $statusCode,
                        'body' => $body,
                        'parts' => $parts,
                        'from' => $from,
                        'to' => $to,
                    ]
                );
            }
        }

        // HTTP error or unexpected body
        return new SmsResult(
            status: 'failed',
            providerMessageId: null,
            cost: null,
            currency: null,
            meta: [
                'status_code' => $statusCode,
                'body' => $body,
                'parts' => $parts,
            ]
        );
    }

    public function getCreditBalance(SmsProfile $profile): int
    {
        $baseUrl = config('services.sms.providers.smsapi_si.base_url');
        $endpoint = config('services.sms.providers.smsapi_si.credits_endpoint', '/preveri-stanje-kreditov');
        $timeout = (int) config('services.sms.providers.smsapi_si.timeout', 10);
        $url = rtrim($baseUrl, '/').$endpoint;

        $response = Http::asForm()
            ->timeout($timeout)
            ->post($url, [
                'un' => $profile->api_username,
                'ps' => $profile->decryptApiPassword() ?? '',
            ]);

        if (! $response->successful()) {
            \Log::warning('SMS credits request failed', [
                'status' => $response->status(),
                'url' => $url,
                'username' => $profile->api_username,
                'body' => (string) $response->body(),
            ]);
            throw new \RuntimeException('Credits endpoint returned HTTP '.$response->status());
        }

        $body = trim((string) $response->body());
        if ($body === '') {
            return 0;
        }

        // Per provider docs, response is '##' separated: first token contains the balance
        $parts = explode('##', $body);
        $first = trim($parts[0] ?? $body);

        // Fallback to the raw first token if no number found
        return intval($first);
    }

    public function getPriceQuotes(SmsProfile $profile): array
    {
        $baseUrl = config('services.sms.providers.smsapi_si.base_url');
        $endpoint = config('services.sms.providers.smsapi_si.price_endpoint', '/dobi-ceno');
        $timeout = (int) config('services.sms.providers.smsapi_si.timeout', 10);
        $url = rtrim($baseUrl, '/').$endpoint;

        $response = Http::asForm()
            ->timeout($timeout)
            ->post($url, [
                'un' => $profile->api_username,
                'ps' => $profile->decryptApiPassword() ?? '',
            ]);
        $body = trim((string) $response->body());
        if ($body === '') {
            return [];
        }

        // Provider returns '##' separated values; trim and drop empty tokens
        $parts = explode('##', $body);
        $parts = array_map(static fn ($s) => trim((string) $s), $parts);
        $parts = array_values(array_filter($parts, static fn ($s) => $s !== ''));

        return $parts;
    }
}
