<?php

namespace Openguru\OpenCartFramework\TeleCartPulse;

use Carbon\Carbon;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
use Openguru\OpenCartFramework\Cache\CacheInterface;
use Openguru\OpenCartFramework\Support\Arr;
use Openguru\OpenCartFramework\Support\Utils;
use Openguru\OpenCartFramework\Telegram\TelegramInitDataDecoder;
use Openguru\OpenCartFramework\Telegram\TelegramService;
use Psr\Log\LoggerInterface;
use Throwable;

class TeleCartPulseService
{
    private TelegramInitDataDecoder $initDataDecoder;
    private TelegramService $telegramService;
    private CacheInterface $cache;
    private LoggerInterface $logger;
    private TeleCartEvent $eventModel;
    private ?string $apiKey;
    private ?PayloadSigner $heartbeatPayloadSigner;

    public function __construct(
        TelegramInitDataDecoder $initDataDecoder,
        TelegramService $telegramService,
        CacheInterface $cache,
        LoggerInterface $logger,
        TeleCartEvent $eventModel,
        ?string $apiKey = null,
        ?PayloadSigner $heartbeatPayloadSigner = null
    ) {
        $this->initDataDecoder = $initDataDecoder;
        $this->telegramService = $telegramService;
        $this->cache = $cache;
        $this->logger = $logger;
        $this->eventModel = $eventModel;
        $this->apiKey = $apiKey;
        $this->heartbeatPayloadSigner = $heartbeatPayloadSigner;
    }

    /**
     * @throws PulseIngestException
     */
    public function handleIngest(array $data): void
    {
        if (! $this->apiKey) {
            return;
        }

        $initData = Arr::get($data, 'payload.webapp.initData');
        if (! $initData) {
            return;
        }

        $event = Arr::get($data, 'event');

        if (! $event) {
            return;
        }

        try {
            $decoded = $this->initDataDecoder->parseInitDataStringToArray($initData);
            $startParam = Arr::get($decoded, 'start_param', '');
            $deserialized = StartParamSerializer::deserialize($startParam);

            // Сохраняем событие в БД только если есть campaign_id и tracking_id
            if (isset($deserialized['campaign_id'], $deserialized['tracking_id'])) {
                $webapp = Arr::get($data, 'payload.webapp', []);
                $eventData = Arr::get($data, 'payload.eventData', []);
                $payload = $this->buildEventPayload($event, $deserialized, $webapp, $eventData);

                $idempotencyKey = Arr::get($data, 'idempotency_key');
                $eventId = $this->eventModel->create($event, $payload, $idempotencyKey);
                $this->logger->debug("Event {$event} fired", [
                    'event_id' => $eventId,
                    'event' => $event,
                    'campaign_id' => $deserialized['campaign_id'],
                    'tracking_id' => $deserialized['tracking_id'],
                ]);
            }
        } catch (Throwable $exception) {
            $this->logger->error('Failed to handle ingest: ' . $exception->getMessage(), [
                'exception' => $exception,
            ]);
            throw new PulseIngestException('Could not handle ingest: ' . $exception->getMessage(), 0, $exception);
        }
    }

    /**
     * Формирует полный payload для отправки в SaaS (готовый к отправке)
     *
     * @param string $event Название события
     * @param array $deserialized Десериализованные start_param данные
     * @param array $webapp Данные webapp
     * @param array $eventData Данные события
     * @return array Готовый payload для отправки в SaaS
     */
    private function buildEventPayload(string $event, array $deserialized, array $webapp, array $eventData): array
    {
        // Общие поля для всех событий
        $payload = [
            'event' => $event,
            'campaign_id' => $deserialized['campaign_id'],
            'tracking_id' => $deserialized['tracking_id'],
            'meta' => [
                'domain' => Utils::getCurrentDomain(),
            ],
            'timestamp' => Carbon::now('UTC')->toJSON(),
        ];

        // Добавляем version и platform в meta (для всех событий)
        if (isset($webapp['version'])) {
            $payload['meta']['version'] = $webapp['version'];
        }
        if (isset($webapp['platform'])) {
            $payload['meta']['platform'] = $webapp['platform'];
        }

        // Добавляем все данные из eventData в meta
        foreach ($eventData as $key => $value) {
            $payload['meta'][$key] = $value;
        }

        return $payload;
    }

    /**
     * @throws GuzzleException
     */
    public function handleHeartbeat(): void
    {
        if ($this->cache->get('telecart_pulse_heartbeat')) {
            return;
        }

        try {
            $this->cache->set('telecart_pulse_heartbeat', time(), 3600);
            $me = $this->telegramService->getMe();
        } catch (Throwable $e) {
            $this->logger->warning(
                'TeleCart Pulse heartbeat prerequisites failed: ' . $e->getMessage(),
                ['exception' => $e]
            );

            return;
        }

        $botName = $me['username'] ?? 'unknown';

        $moduleVersion = module_version();

        $payload = [
            'event' => 'HEARTBEAT',
            'meta' => [
                'domain' => Utils::getCurrentDomain(),
                'bot_name' => $botName,
                'php_version' => PHP_VERSION,
                'module_version' => $moduleVersion,
                'opencart_version' => defined('VERSION') ? VERSION : 'unknown',
                'opencart_version_core' => defined('VERSION_CORE') ? VERSION_CORE : 'unknown',
            ],
            'timestamp' => Carbon::now('UTC')->toJSON(),
        ];

        if (! $this->heartbeatPayloadSigner) {
            return;
        }

        try {
            $signature = $this->heartbeatPayloadSigner->sign($payload);
        } catch (PayloadSignException $exception) {
            $this->logger->warning(
                'TeleCart Pulse heartbeat signing failed: ' . $exception->getMessage(),
                ['exception' => $exception]
            );

            return;
        }

        $dataToSend = [
            'payload' => $payload,
            'signature' => $signature,
        ];

        $this->pushHeartbeat($dataToSend);
    }

    /**
     * @throws GuzzleException
     */
    private function pushHeartbeat(array $json): void
    {
        $baseUri = rtrim(env('PULSE_API_HOST', 'http://localhost'), '/') . '/';

        $client = new Client([
            'base_uri' => $baseUri,
            'timeout' => env('PULSE_TIMEOUT', 2.0),
            'headers' => [
                'X-TELECART-VERSION' => module_version(),
            ],
        ]);

        $client->post('heartbeat', compact('json'));
    }
}
