<?php

namespace Openguru\OpenCartFramework\TeleCartPulse;

use Carbon\Carbon;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\GuzzleException;
use Openguru\OpenCartFramework\Support\Arr;
use Openguru\OpenCartFramework\Support\Utils;
use Openguru\OpenCartFramework\Telegram\TelegramInitDataDecoder;
use Throwable;

class TeleCartPulseService
{
    private TelegramInitDataDecoder $initDataDecoder;
    private PayloadSigner $payloadSigner;
    private ?string $apiKey;

    public function __construct(
        TelegramInitDataDecoder $initDataDecoder,
        PayloadSigner $payloadSigner,
        ?string $apiKey = null
    ) {
        $this->initDataDecoder = $initDataDecoder;
        $this->payloadSigner = $payloadSigner;
        $this->apiKey = $apiKey;
    }

    /**
     * @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);

            if ($event === PulseEvents::WEBAPP_OPEN) {
                $this->handleWebAppInit($data, $deserialized);
            }

            if ($event === PulseEvents::ORDER_CREATED) {
                $this->handleOrderCreated($data, $deserialized);
            }
        } catch (ClientException $exception) {
            $contents = (string)$exception->getResponse()->getBody();
            $decoded = json_decode($contents, true);
            throw new PulseIngestException('TeleCart Pulse API error: ' . $decoded['error'] ?? '', 0, $exception);
        } catch (Throwable $exception) {
            throw new PulseIngestException('Could not handle ingest: ' . $exception->getMessage(), 0, $exception);
        }
    }

    /**
     * @throws PayloadSignException
     * @throws GuzzleException
     */
    private function handleWebAppInit(array $data, array $deserialized): void
    {
        // Campaign Event
        if (isset($deserialized['campaign_id'], $deserialized['tracking_id'])) {
            $payload = [
                'event' => PulseEvents::WEBAPP_OPEN,
                'campaign_id' => $deserialized['campaign_id'],
                'tracking_id' => $deserialized['tracking_id'],
                'meta' => [
                    'domain' => Utils::getCurrentDomain(),
                    'version' => Arr::get($data, 'webapp.version'),
                    'platform' => Arr::get($data, 'webapp.platform'),
                ],
                'timestamp' => Carbon::now('UTC')->toJSON(),
            ];

            $dataToSend = [
                'payload' => $payload,
                'signature' => $this->payloadSigner->sign($payload),
            ];

            $this->pushEvent($dataToSend);
        }
    }

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

        $client = new Client([
            'base_uri' => $baseUri,
            'timeout' => env('PULSE_TIMEOUT', 5.0),
            'headers' => [
                'Authorization' => 'Bearer ' . $this->apiKey,
                'X-TELECART-VERSION' => '2.0.0',
            ],
        ]);

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

    private function handleOrderCreated(array $data, array $deserialized): void
    {
        if (isset($deserialized['campaign_id'], $deserialized['tracking_id'])) {
            $payload = [
                'event' => PulseEvents::ORDER_CREATED,
                'campaign_id' => $deserialized['campaign_id'],
                'tracking_id' => $deserialized['tracking_id'],
                'meta' => [
                    'domain' => Utils::getCurrentDomain(),
                    'version' => Arr::get($data, 'webapp.version'),
                    'platform' => Arr::get($data, 'webapp.platform'),
                    'order_id' => Arr::get($data, 'eventData.order_id'),
                    'currency' => Arr::get($data, 'eventData.currency'),
                ],
                'timestamp' => Carbon::now('UTC')->toJSON(),
            ];

            $dataToSend = [
                'payload' => $payload,
                'signature' => $this->payloadSigner->sign($payload),
            ];

            $this->pushEvent($dataToSend);
        }
    }
}
