<?php

namespace Openguru\OpenCartFramework\Telegram;

use App\Services\SettingsService;
use Openguru\OpenCartFramework\Http\Request;
use Openguru\OpenCartFramework\Telegram\Enums\TelegramHeader;
use Openguru\OpenCartFramework\Telegram\Exceptions\DecodeTelegramInitDataException;
use Openguru\OpenCartFramework\Telegram\Exceptions\TelegramInvalidSignatureException;
use Psr\Log\LoggerInterface;

class SignatureValidator
{
    private ?string $botToken;
    private SettingsService $settings;
    private LoggerInterface $logger;
    private TelegramInitDataDecoder $initDataDecoder;

    public function __construct(
        SettingsService $settings,
        LoggerInterface $logger,
        TelegramInitDataDecoder $initDataDecoder,
        ?string $botToken = null
    ) {
        $this->botToken = $botToken;
        $this->settings = $settings;
        $this->logger = $logger;
        $this->initDataDecoder = $initDataDecoder;
    }

    /**
     * @throws TelegramInvalidSignatureException
     */
    public function validate(Request $request): void
    {
        if ($this->settings->config()->getApp()->isAppDebug()) {
            return;
        }

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

        if (! $request->header(TelegramHeader::INIT_DATA)) {
            throw new TelegramInvalidSignatureException('Telegram Signature not exists.');
        }

        try {
            $data = $this->initDataDecoder->decode($request->header(TelegramHeader::INIT_DATA));
        } catch (DecodeTelegramInitDataException $e) {
            throw new TelegramInvalidSignatureException('Invalid Telegram Signature.', 500, $e);
        }

        if (! isset($data['hash'])) {
            throw new TelegramInvalidSignatureException('Missing hash in init data');
        }

        $checkString = $this->getCheckString($data);

        $algorithm = 'sha256';
        $secretKey = hash_hmac($algorithm, $this->botToken, 'WebAppData', true);
        $calculatedHash = bin2hex(hash_hmac($algorithm, $checkString, $secretKey, true));

        if (! hash_equals($calculatedHash, $data['hash'])) {
            $this->logger->error('Request with invalid Telegram Signature. Check Telegram Bot Token is correct.');
            throw new TelegramInvalidSignatureException('Invalid Telegram signature!');
        }
    }

    private function getCheckString(array $data): string
    {
        unset($data['hash']);
        ksort($data);

        $array = [];
        foreach ($data as $key => $value) {
            if (is_array($value)) {
                $array[] = $key . '=' . json_encode($value, JSON_UNESCAPED_UNICODE);
            } else {
                $array[] = $key . '=' . $value;
            }
        }

        return implode(PHP_EOL, $array);
    }
}
