<?php

namespace App\Service\Domain;

use App\Entity\Domain;
use App\Exception\HostNotFoundException;
use App\Service\DomainService;
use App\Tools\Domain as DomainHelper;
use Doctrine\ORM\EntityNotFoundException;
use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\TransferStats;
use Pdp\Cache;
use Pdp\CurlHttpClient;
use Pdp\Manager;
use Throwable;

class Builder
{
    /**
     * @var Client
     */
    private $httpClient;
    /**
     * @var string
     */
    private $url;
    /**
     * @var DomainService
     */
    private $domainService;
    /**
     * @var string
     */
    private $type;
    /**
     * @var string
     */
    private $ip;
    /**
     * @var string
     */
    private $protocol;
    /**
     * @var Domain
     */
    private $domain;

    public function __construct(DomainService $domainService, Client $httpClient)
    {
        $this->httpClient = $httpClient;
        $this->domainService = $domainService;
        $this->reset();
    }

    public function clean(): void
    {
        $this->url = preg_replace('#^(https?://)*#', '', rtrim($this->url, '/'));
    }

    public function isDuplicate()
    {
        try {
            $this->domainService->getByUrl($this->url);

            throw new Exception('Is duplicate');
        } catch (EntityNotFoundException $e) {
            return $this;
        }
    }

    public function getProtocol()
    {
        foreach (['http://', 'https://'] as $protocol) {
            try {
                $redir = '';
                $response = $this->httpClient->get($protocol.$this->url, ['allow_redirects' => true, 'headers' => ['User-Agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36', 'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
                    'Accept-Encoding' => 'gzip, deflate, br', ], 'on_stats' => function (TransferStats $stats) use (&$redir) {
                        $redir = (string) $stats->getEffectiveUri();
                    }]);
                if (200 === $response->getStatusCode()) {
                    $this->protocol = DomainHelper::getProtocol($redir);

                    break;
                }
            } catch (Throwable $e) {
                continue;
            }
        }

        $parsed = parse_url($this->protocol.$this->url);
        if (empty($parsed['host'])) {
            throw new HostNotFoundException('Host not found');
        }

        $this->url = $parsed['host'];
    }

    public function getType()
    {
        $manager = new Manager(new Cache(), new CurlHttpClient());
        $rules = $manager->getRules();

        $resolvedDomain = $rules->resolve($this->url);
        if (!$resolvedDomain->isResolvable()) {
            throw new Exception('Could not set type of a domain');
        } else {
            if (!$resolvedDomain->getSubDomain()) {
                $this->type = 'ROOTDOMAIN';
            } else {
                $this->type = 'SUBDOMAIN';
            }
        }
    }

    public function getIp(): void
    {
        $this->ip = DomainHelper::getIp($this->protocol.$this->url);
    }

    public function build()
    {
        try {
            $this->reset();
            $this->clean();
            $this->isDuplicate();
            $this->getProtocol();
            $this->getType();
            $this->getIp();
        } catch (Throwable $exception) {
            throw $exception;
        }

        $this->domain->setUrl($this->url);
        $this->domain->setIp($this->ip);
        $this->domain->setProtocol($this->protocol);
        $this->domain->setUrlType($this->type);

        $this->domainService->saveDomain($this->domain);

        return $this->domain;
    }

    private function reset()
    {
        $this->domain = new Domain();
    }

    /**
     * @param string $url
     */
    public function setUrl(string $url): void
    {
        $this->url = $url;
    }
}
