<?php
namespace App\Services;

use RuntimeException;
use Throwable;

final class UltraMsgService
{
    private array $rootConfig;
    private array $providerConfig;

    public function __construct(?array $config = null)
    {
        $this->rootConfig = $config ?? (array)($GLOBALS['config']['whatsapp'] ?? []);
        $nested = $this->rootConfig['ultramsg'] ?? [];
        $this->providerConfig = is_array($nested) ? $nested : [];
    }

    public function driver(): string
    {
        return strtolower(trim((string)($this->rootConfig['driver'] ?? $this->rootConfig['provider'] ?? '')));
    }

    public function enabled(): bool
    {
        return !empty($this->rootConfig['enabled']) && $this->driver() === 'ultramsg';
    }

    public function limits(): array
    {
        return [
            'max_per_batch' => $this->boundedInt('max_per_batch', 50, 1, 200),
            'max_per_day' => $this->boundedInt('max_per_day', 100, 1, 5000),
            'messages_per_run' => $this->boundedInt('messages_per_run', 5, 1, 50),
            'max_attempts' => $this->boundedInt('max_attempts', 3, 1, 10),
            'duplicate_window_hours' => $this->boundedInt('duplicate_window_hours', 24, 1, 720),
            'default_country_code' => preg_replace('/\D+/', '', (string)($this->providerConfig['default_country_code'] ?? '961')) ?: '961',
        ];
    }

    public function configurationStatus(bool $checkInstance = false): array
    {
        $instanceId = $this->instanceId();
        $token = $this->token();
        $issues = [];

        if (!$this->enabled()) {
            $issues[] = 'UltraMsg is disabled in config/app.php.';
        }
        if ($instanceId === '') {
            $issues[] = 'UltraMsg instance ID is missing.';
        }
        if ($token === '') {
            $issues[] = 'UltraMsg token is missing or the token file is not readable.';
        }
        if (!function_exists('curl_init')) {
            $issues[] = 'PHP cURL is not enabled.';
        }

        $configured = !$issues;
        $status = [
            'provider' => 'ultramsg',
            'enabled' => $this->enabled(),
            'configured' => $configured,
            'instance_id' => $instanceId,
            'token_source' => $this->tokenSource(),
            'authenticated' => false,
            'instance_status' => $configured ? 'not_checked' : 'not_configured',
            'message' => $configured ? 'UltraMsg is configured. Test the connection to verify the linked WhatsApp session.' : implode(' ', $issues),
            'limits' => $this->limits(),
        ];

        if ($configured && $checkInstance) {
            $live = $this->instanceStatus();
            $status = array_merge($status, $live);
        }

        return $status;
    }

    public function instanceStatus(): array
    {
        $base = $this->configurationStatus(false);
        if (!$base['configured']) {
            return $base;
        }

        try {
            $response = $this->request('GET', '/instance/status', ['token' => $this->token()]);
            $instanceStatus = $this->extractInstanceStatus($response['json'], $response['body']);
            $authenticated = $instanceStatus === 'authenticated';

            return [
                'provider' => 'ultramsg',
                'enabled' => true,
                'configured' => true,
                'instance_id' => $this->instanceId(),
                'token_source' => $this->tokenSource(),
                'authenticated' => $authenticated,
                'instance_status' => $instanceStatus,
                'message' => $authenticated
                    ? 'UltraMsg is connected and authenticated.'
                    : 'UltraMsg responded, but the instance status is ' . ($instanceStatus ?: 'unknown') . '. Open UltraMsg and scan/reconnect the QR session.',
                'limits' => $this->limits(),
            ];
        } catch (Throwable $e) {
            return [
                'provider' => 'ultramsg',
                'enabled' => true,
                'configured' => true,
                'instance_id' => $this->instanceId(),
                'token_source' => $this->tokenSource(),
                'authenticated' => false,
                'instance_status' => 'connection_failed',
                'message' => 'UltraMsg connection test failed: ' . $e->getMessage(),
                'limits' => $this->limits(),
            ];
        }
    }

    public function sendText(string $mobile, string $message, ?string $referenceId = null): array
    {
        $status = $this->configurationStatus(false);
        if (!$status['configured']) {
            throw new RuntimeException($status['message']);
        }

        $normalized = $this->normalizeMobile($mobile);
        if ($normalized === null) {
            throw new RuntimeException('Invalid WhatsApp number. Use a valid international number.');
        }

        $message = trim($message);
        if ($message === '') {
            throw new RuntimeException('WhatsApp message is empty.');
        }
        $messageLength = function_exists('mb_strlen') ? mb_strlen($message, 'UTF-8') : strlen($message);
        if ($messageLength > 4096) {
            throw new RuntimeException('WhatsApp message exceeds UltraMsg\'s 4,096-character text limit.');
        }

        $payload = [
            'token' => $this->token(),
            'to' => $normalized,
            'body' => $message,
            'priority' => 10,
            'referenceId' => $referenceId ? (function_exists('mb_substr') ? mb_substr($referenceId, 0, 100, 'UTF-8') : substr($referenceId, 0, 100)) : '',
        ];

        $response = $this->request('POST', '/messages/chat', $payload);
        $json = $response['json'];
        $sentValue = is_array($json) ? ($json['sent'] ?? false) : false;
        $sent = $sentValue === true || $sentValue === 1 || strtolower((string)$sentValue) === 'true';
        $messageResult = is_array($json) ? strtolower(trim((string)($json['message'] ?? ''))) : '';

        if (!$sent && $messageResult !== 'ok') {
            $providerError = is_array($json)
                ? (string)($json['error'] ?? $json['message'] ?? 'UltraMsg rejected the message.')
                : 'UltraMsg returned an unexpected response.';
            throw new RuntimeException($providerError);
        }

        return [
            'status' => 'sent',
            'provider' => 'ultramsg',
            'provider_message_id' => is_array($json) && isset($json['id']) ? (string)$json['id'] : null,
            'mobile' => $normalized,
            'response' => $json,
        ];
    }

    public function normalizeMobile(string $mobile): ?string
    {
        $raw = trim($mobile);
        if ($raw === '') return null;

        $hadPlus = str_starts_with($raw, '+');
        $digits = preg_replace('/\D+/', '', $raw);
        if ($digits === '') return null;

        if (str_starts_with($digits, '00')) {
            $digits = substr($digits, 2);
            $hadPlus = true;
        }

        $countryCode = $this->limits()['default_country_code'];
        if (!$hadPlus) {
            if (str_starts_with($digits, '0')) {
                $digits = $countryCode . ltrim($digits, '0');
            } elseif (strlen($digits) <= 8 && !str_starts_with($digits, $countryCode)) {
                $digits = $countryCode . $digits;
            }
        }

        $digits = ltrim($digits, '0');
        if (!preg_match('/^[1-9]\d{7,14}$/', $digits)) {
            return null;
        }

        return '+' . $digits;
    }

    private function request(string $method, string $path, array $params): array
    {
        $instanceId = rawurlencode($this->instanceId());
        $url = 'https://api.ultramsg.com/' . $instanceId . $path;
        $method = strtoupper($method);

        if ($method === 'GET') {
            $url .= '?' . http_build_query($params, '', '&', PHP_QUERY_RFC3986);
        }

        $ch = curl_init($url);
        if ($ch === false) {
            throw new RuntimeException('Could not initialize cURL.');
        }

        $options = [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_CONNECTTIMEOUT => $this->boundedInt('connect_timeout_seconds', 10, 3, 30),
            CURLOPT_TIMEOUT => $this->boundedInt('timeout_seconds', 30, 5, 90),
            CURLOPT_FOLLOWLOCATION => false,
            CURLOPT_SSL_VERIFYPEER => true,
            CURLOPT_SSL_VERIFYHOST => 2,
            CURLOPT_HTTPHEADER => ['Accept: application/json'],
        ];

        if ($method === 'POST') {
            $options[CURLOPT_POST] = true;
            $options[CURLOPT_POSTFIELDS] = http_build_query($params, '', '&', PHP_QUERY_RFC3986);
            $options[CURLOPT_HTTPHEADER] = [
                'Accept: application/json',
                'Content-Type: application/x-www-form-urlencoded',
            ];
        }

        curl_setopt_array($ch, $options);
        $body = curl_exec($ch);
        $error = curl_error($ch);
        $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
        curl_close($ch);

        if ($body === false || $error !== '') {
            throw new RuntimeException('Network error: ' . ($error ?: 'empty response'));
        }
        if ($code < 200 || $code >= 300) {
            throw new RuntimeException('UltraMsg API returned HTTP ' . $code . '.');
        }

        $json = json_decode((string)$body, true);
        return [
            'code' => $code,
            'body' => (string)$body,
            'json' => is_array($json) ? $json : null,
        ];
    }

    private function instanceId(): string
    {
        $value = trim((string)($this->providerConfig['instance_id'] ?? $this->rootConfig['instance_id'] ?? ''));
        return preg_match('/^[A-Za-z0-9_-]+$/', $value) ? $value : '';
    }

    private function token(): string
    {
        $file = trim((string)($this->providerConfig['token_file'] ?? ''));
        if ($file !== '' && is_file($file) && is_readable($file)) {
            $token = trim((string)file_get_contents($file));
            if ($token !== '') return $token;
        }
        return trim((string)($this->providerConfig['token'] ?? $this->rootConfig['token'] ?? ''));
    }

    private function tokenSource(): string
    {
        $file = trim((string)($this->providerConfig['token_file'] ?? ''));
        if ($file !== '') return 'private_file';
        return trim((string)($this->providerConfig['token'] ?? $this->rootConfig['token'] ?? '')) !== '' ? 'config' : 'missing';
    }

    private function boundedInt(string $key, int $default, int $min, int $max): int
    {
        $value = (int)($this->providerConfig[$key] ?? $default);
        return max($min, min($max, $value ?: $default));
    }

    private function extractInstanceStatus(?array $json, string $body): string
    {
        $known = ['initialize','qr','retrying','loading','authenticated','disconnected','standby'];
        $candidates = [];

        $walk = static function (mixed $value) use (&$walk, &$candidates): void {
            if (is_array($value)) {
                foreach ($value as $key => $child) {
                    if (is_scalar($child) && in_array(strtolower((string)$key), ['status','accountstatus','state'], true)) {
                        $candidates[] = strtolower(trim((string)$child));
                    }
                    $walk($child);
                }
            } elseif (is_scalar($value)) {
                $candidates[] = strtolower(trim((string)$value));
            }
        };

        if ($json !== null) $walk($json);
        $walk(trim($body));

        foreach ($candidates as $candidate) {
            foreach ($known as $status) {
                if ($candidate === $status || str_contains($candidate, $status)) return $status;
            }
        }

        return 'unknown';
    }
}
