<?php
declare(strict_types=1);

namespace App\Modules\Sms;

use App\Core\Exceptions\IntegrationConfigException;
use App\Modules\Notifications\NotificationRepository;
use App\Modules\Settings\SmsplanetApiClient;
use App\Modules\Settings\SmsplanetIntegrationRepository;

final class SmsConversationService
{
    private const MAX_SMS_LENGTH = 918;

    public function __construct(
        private readonly SmsMessageRepository $messages,
        private readonly SmsplanetIntegrationRepository $settings,
        private readonly SmsplanetApiClient $apiClient,
        private readonly NotificationRepository $notifications
    ) {
    }

    /**
     * @return array{ok: bool, message: string, message_id: string}
     */
    public function sendFromOrder(int $orderId, string $toPhone, string $body, ?int $userId): array
    {
        $phone = $this->normalizePhone($toPhone);
        $message = trim($body);
        if ($orderId <= 0 || $phone === '' || $message === '') {
            throw new IntegrationConfigException('Podaj numer telefonu i tresc SMS.');
        }
        $credentials = $this->settings->getCredentials();
        if ($credentials === null) {
            throw new IntegrationConfigException('Konfiguracja SMSPLANET jest niekompletna albo nieaktywna.');
        }

        $finalMessage = $this->buildFinalOutboundBody($message, (string) ($credentials['default_footer'] ?? ''));
        if ($this->messageLength($finalMessage) > self::MAX_SMS_LENGTH) {
            throw new IntegrationConfigException('Tresc SMS ze stopka nie moze przekraczac 918 znakow.');
        }

        $from = trim((string) ($credentials['sender'] ?? ''));
        $result = $this->apiClient->sendSms($credentials, $phone, $finalMessage);
        $this->messages->insert([
            'direction' => 'outbound',
            'provider' => 'smsplanet',
            'order_id' => $orderId,
            'from_phone' => $from,
            'from_phone_normalized' => $this->normalizePhone($from),
            'to_phone' => $phone,
            'to_phone_normalized' => $this->normalizePhone($phone),
            'body' => $finalMessage,
            'message_id' => (string) $result['message_id'],
            'status' => $result['ok'] ? 'sent' : 'failed',
            'raw_payload' => $result,
            'created_by' => $userId,
        ]);

        return [
            'ok' => (bool) $result['ok'],
            'message' => (string) $result['message'],
            'message_id' => (string) $result['message_id'],
        ];
    }

    /**
     * @param array<string, mixed> $payload
     * @return array{message_id: int, order_id: ?int}
     */
    public function receiveSmsplanetWebhook(array $payload): array
    {
        $parsed = $this->parseSmsplanetPayload($payload);
        $fromNormalized = $this->normalizePhone($parsed['from_phone']);
        $toNormalized = $this->normalizePhone($parsed['to_phone']);
        $orderId = $this->messages->findLatestOrderIdByPhones($this->phoneLookupVariants($fromNormalized));

        $messageId = $this->messages->insert([
            'direction' => 'inbound',
            'provider' => 'smsplanet',
            'order_id' => $orderId,
            'from_phone' => $parsed['from_phone'],
            'from_phone_normalized' => $fromNormalized,
            'to_phone' => $parsed['to_phone'],
            'to_phone_normalized' => $toNormalized,
            'body' => $parsed['body'],
            'message_id' => $parsed['message_id'],
            'status' => 'received',
            'raw_payload' => $payload,
        ]);

        $this->notifications->create([
            'type' => 'sms_inbound',
            'title' => 'Nowy SMS od klienta',
            'body' => $this->notificationBody($parsed['from_phone'], $parsed['body']),
            'target_url' => $orderId !== null ? '/orders/' . $orderId . '?tab=sms' : '/notifications',
            'related_order_id' => $orderId,
            'related_sms_message_id' => $messageId,
        ]);

        return ['message_id' => $messageId, 'order_id' => $orderId];
    }

    public function normalizePhone(string $phone): string
    {
        return preg_replace('/[^0-9]+/', '', trim($phone)) ?? '';
    }

    public function hasDefaultFooter(): bool
    {
        $settings = $this->settings->getSettings();

        return trim((string) ($settings['default_footer'] ?? '')) !== '';
    }

    private function buildFinalOutboundBody(string $message, string $defaultFooter): string
    {
        $footer = trim(str_replace(["\r\n", "\r"], "\n", $defaultFooter));
        if ($footer === '') {
            return $message;
        }

        return rtrim($message) . "\n\n" . $footer;
    }

    /**
     * @return array{from_phone: string, to_phone: string, body: string, message_id: string}
     */
    private function parseSmsplanetPayload(array $payload): array
    {
        $from = $this->firstPayloadValue($payload, ['from', 'sender', 'msisdn', 'phone', 'originator']);
        $to = $this->firstPayloadValue($payload, ['to', 'recipient', 'receiver', 'number']);
        $body = $this->firstPayloadValue($payload, ['msg', 'message', 'body', 'text', 'content']);
        if ($from === '' || $body === '') {
            throw new IntegrationConfigException('Webhook SMSPLANET nie zawiera numeru nadawcy albo tresci.');
        }

        return [
            'from_phone' => $from,
            'to_phone' => $to,
            'body' => $body,
            'message_id' => $this->firstPayloadValue($payload, ['messageId', 'message_id', 'id', 'smsId']),
        ];
    }

    /**
     * @param array<string, mixed> $payload
     * @param array<int, string> $keys
     */
    private function firstPayloadValue(array $payload, array $keys): string
    {
        foreach ($keys as $key) {
            if (array_key_exists($key, $payload)) {
                return trim((string) $payload[$key]);
            }
        }

        return '';
    }

    /**
     * @return array<int, string>
     */
    private function phoneLookupVariants(string $normalized): array
    {
        if ($normalized === '') {
            return [];
        }

        $variants = [$normalized];
        if (str_starts_with($normalized, '48') && strlen($normalized) === 11) {
            $variants[] = substr($normalized, 2);
        } elseif (strlen($normalized) === 9) {
            $variants[] = '48' . $normalized;
        }

        return $variants;
    }

    private function notificationBody(string $fromPhone, string $body): string
    {
        $shortBody = trim(preg_replace('/\s+/', ' ', $body) ?? $body);
        if (strlen($shortBody) > 140) {
            $shortBody = substr($shortBody, 0, 137) . '...';
        }

        return trim($fromPhone . ': ' . $shortBody);
    }

    private function messageLength(string $value): int
    {
        if (function_exists('mb_strlen')) {
            return mb_strlen($value, 'UTF-8');
        }

        return strlen($value);
    }
}
