<?php

namespace App\Command;

use App\Message\LinkCheckedMessage;
use App\Service\LinkService;
use DateTime;
use Doctrine\ORM\EntityNotFoundException;
use Goutte\Client;
use GuzzleHttp\Client as HttpClient;
use GuzzleHttp\TransferStats;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use Symfony\Component\Messenger\MessageBusInterface;

class CrmLinkCheckCommand extends Command
{
    protected static $defaultName = 'crm:link:check';
    /**
     * @var HttpClient
     */
    private $httpClient;
    /**
     * @var LinkService
     */
    private $linkService;
    /**
     * @var MessageBusInterface
     */
    private $messageBus;

    public function __construct(HttpClient $httpClient, LinkService $linkService, MessageBusInterface $messageBus, ?string $name = null)
    {
        parent::__construct($name);
        $this->httpClient = $httpClient;
        $this->linkService = $linkService;
        $this->messageBus = $messageBus;
    }

    protected function configure()
    {
        $this
            ->setDescription('Check Link')
            ->addArgument('linkId', InputArgument::REQUIRED, 'Id of the link to check');
    }

    /**
     * @param InputInterface  $input
     * @param OutputInterface $output
     *
     * @return int|null
     *
     * @throws \Exception
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $io = new SymfonyStyle($input, $output);
        $linkId = $input->getArgument('linkId');

        try {
            $link = $this->linkService->get($linkId);
        } catch (EntityNotFoundException $e) {
            $io->error('There is no Link for given ID');

            return;
        }
        $io->note(sprintf('Checking: %s', $link->getUrl()));

        $cssSelector = sprintf("a[href='%s']", $link->getContent()->getAnchorHref());
        $thingToScrape = ['_text', 'rel'];

        $client = new Client();
        $crawler = $client->request('GET', $link->getUrl());
        $result = $crawler->filter($cssSelector)->extract($thingToScrape);

        $output = [];
        $allResultsForHref = count($result);
        if ($allResultsForHref > 0) {
            $href = true;
            $resultsWithoutRelNoFollow = array_filter($result, function (array $link) {
                return 'nofollow' !== $link[1];
            });
            if (count($resultsWithoutRelNoFollow) > 0) {
                $anchorText = $resultsWithoutRelNoFollow[0][0];
                $relNoFollow = false;
            } else {
                $anchorText = $result[0][0];
                $relNoFollow = true;
            }
        } else {
            $href = false;
            $anchorText = null;
            $relNoFollow = null;
        }
        $output['scrape_result'] = $result;

        $statusCode = 500;
        $responseTime = 0;

        try {
            $this->httpClient->get($link->getUrl(), [
                    'on_stats' => function (TransferStats $stats) use (&$statusCode, &$responseTime, &$output) {
                        // You must check if a response was received before using the response object
                        if ($stats->hasResponse()) {
                            $responseTime = $stats->getTransferTime();
                            $statusCode = $stats->getResponse()->getStatusCode();
                            $output = array_merge($output, $stats->getHandlerStats());
                        } else {
                            $output = array_merge($output, [$stats->getHandlerErrorData()]);
                        }
                    },
                    'allow_redirects' => false,
                ]
            );
        } catch (\Exception $e) {
            $this->messageBus->dispatch(new LinkCheckedMessage($link->getId(), $statusCode, $responseTime, $href, $link->getContent()->getAnchorText(), $anchorText, $relNoFollow, 0, ['error' => $e->getMessage()], new DateTime()));
            $io->error($e->getMessage());

            return 1;
        }
        $this->messageBus->dispatch(new LinkCheckedMessage($link->getId(), $statusCode, $responseTime, $href, $link->getContent()->getAnchorText(), $anchorText, $relNoFollow, 0, $output, new DateTime()));

        $io->success('Link has been checked');

        return 0;
    }
}
