<?php
declare(strict_types=1);

namespace common\tasks;

use GuzzleHttp\Client as GuzzleClient;
use GuzzleHttp\ClientInterface;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Exception\ServerException;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use Psr\Http\Message\RequestInterface;
use Yii;
use yii\queue\RetryableJobInterface;

class DeliveryWebHook implements RetryableJobInterface {

    /**
     * @var string
     */
    public $type;

    /**
     * @var string
     */
    public $url;

    /**
     * @var string|null
     */
    public $secret;

    /**
     * @var array
     */
    public $payloads;

    /**
     * @return int time to reserve in seconds
     */
    public function getTtr(): int {
        return 65;
    }

    /**
     * @param int $attempt number
     * @param \Exception|\Throwable $error from last execute of the job
     *
     * @return bool
     */
    public function canRetry($attempt, $error): bool {
        if ($attempt >= 5) {
            return false;
        }

        if ($error instanceof ServerException || $error instanceof ConnectException) {
            return true;
        }

        return false;
    }

    /**
     * @param \yii\queue\Queue $queue which pushed and is handling the job
     *
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function execute($queue): void {
        $client = $this->createClient();
        try {
            $client->request('POST', $this->url, [
                'headers' => [
                    'User-Agent' => 'Account-Ely-Hookshot/' . Yii::$app->version,
                    'X-Ely-Accounts-Event' => $this->type,
                ],
                'form_params' => $this->payloads,
            ]);
        } catch (ClientException $e) {
            Yii::info("Delivery for {$this->url} has failed with {$e->getResponse()->getStatusCode()} status.");

            return;
        }
    }

    protected function createClient(): ClientInterface {
        return new GuzzleClient([
            'handler' => $this->createStack(),
            'timeout' => 60,
            'connect_timeout' => 10,
        ]);
    }

    protected function createStack(): HandlerStack {
        $stack = HandlerStack::create();
        $stack->push(Middleware::mapRequest(function(RequestInterface $request): RequestInterface {
            if (empty($this->secret)) {
                return $request;
            }

            $payload = (string)$request->getBody();
            $signature = hash_hmac('sha1', $payload, $this->secret);

            /** @noinspection ExceptionsAnnotatingAndHandlingInspection */
            return $request->withHeader('X-Hub-Signature', 'sha1=' . $signature);
        }));

        return $stack;
    }

}