<?php

namespace Mnv\Core\YaMetrika;

use DateTime;
use DateInterval;

/**
 * Class YaMetrika
 * @package Mnv\Core\YaMetrika
 */
class YaMetrika
{
    private Client $client;

    public function __construct(Client $client)
    {
        $this->client = $client;
    }


    /** Данные по посещаемости */
    /** Будут получены данные: визитов, просмотров, уникальных посетителей по дням. */

    /**
     * За последние N дней
     * @param int $days
     * @return Response
     * @throws Exception\ClientException
     */
    public function getVisitors(int $days = 30): Response
    {
        list($startDate, $endDate) = $this->differenceDate($days);

        return $this->getVisitorsForPeriod($startDate, $endDate);
    }


    /**
     * За указанный период
     * @param DateTime $startDate
     * @param DateTime $endDate
     * @return Response
     * @throws Exception\ClientException
     */
    public function getVisitorsForPeriod(DateTime $startDate, DateTime $endDate): Response
    {
        $params = [
            'date1'      => $startDate->format('Y-m-d'),
            'date2'      => $endDate->format('Y-m-d'),
            'metrics'    => 'ym:s:visits,ym:s:pageviews,ym:s:users',
            'dimensions' => 'ym:s:date',
            'sort'       => 'ym:s:date',
        ];

        return new Response($this->client->request($params));
    }

    /** Самые просматриваемые страницы */

    /**
     * За последние N дней
     * @param int $days
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getMostViewedPages(int $days = 30, int $limit = 10): Response
    {
        list($startDate, $endDate) = $this->differenceDate($days);

        return $this->getMostViewedPagesForPeriod($startDate, $endDate, $limit);
    }

    /**
     * За указанный период
     * @param DateTime $startDate
     * @param DateTime $endDate
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getMostViewedPagesForPeriod(DateTime $startDate, DateTime $endDate, int $limit = 10): Response
    {
        $params = [
            'date1'      => $startDate->format('Y-m-d'),
            'date2'      => $endDate->format('Y-m-d'),
            'metrics'    => 'ym:pv:pageviews',
            'dimensions' => 'ym:pv:URLPathFull,ym:pv:title',
            'sort'       => '-ym:pv:pageviews',
            'limit'      => $limit,
        ];

        return new Response($this->client->request($params));
    }

    /** Браузеры пользователей */

    /**
     * За последние N дней
     * @param int $days
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getBrowsers(int $days = 30, int $limit = 10): Response
    {
        list($startDate, $endDate) = $this->differenceDate($days);

        return $this->getBrowsersForPeriod($startDate, $endDate, $limit);
    }

    /**
     * За указанный период
     * @param DateTime $startDate
     * @param DateTime $endDate
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getBrowsersForPeriod(DateTime $startDate, DateTime $endDate, int $limit = 10): Response
    {
        $params = [
            'date1'      => $startDate->format('Y-m-d'),
            'date2'      => $endDate->format('Y-m-d'),
            'preset'     => 'tech_platforms',
            'dimensions' => 'ym:s:browser',
            'limit'      => $limit,
        ];

        return new Response($this->client->request($params));
    }

    /** Пользователи из поисковых систем */

    /**
     * За последние N дней
     * @param int $days
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getUsersSearchEngine(int $days = 30, int $limit = 10): Response
    {
        list($startDate, $endDate) = $this->differenceDate($days);

        return $this->getUsersSearchEngineForPeriod($startDate, $endDate, $limit);
    }

    /**
     * За указанный период
     * @param DateTime $startDate
     * @param DateTime $endDate
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getUsersSearchEngineForPeriod(DateTime $startDate, DateTime $endDate, $limit = 10): Response
    {
        $params = [
            'date1'      => $startDate->format('Y-m-d'),
            'date2'      => $endDate->format('Y-m-d'),
            'metrics'    => 'ym:s:visits,ym:s:users',
            'dimensions' => 'ym:s:searchEngine',
            'filters'    => "ym:s:trafficSource=='organic'",
            'limit'      => $limit,
        ];

        return new Response($this->client->request($params));
    }

    /** Пользователи по странам и регионам */

    /**
     * За последние N дней
     * @param int $days
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getGeo(int $days = 7, int $limit = 20): Response
    {
        list($startDate, $endDate) = $this->differenceDate($days);

        return $this->getGeoForPeriod($startDate, $endDate, $limit);
    }

    /**
     * За указанный период
     * @param DateTime $startDate
     * @param DateTime $endDate
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getGeoForPeriod(DateTime $startDate, DateTime $endDate, int $limit = 20): Response
    {
        $params = [
            'date1'      => $startDate->format('Y-m-d'),
            'date2'      => $endDate->format('Y-m-d'),
            'dimensions' => 'ym:s:regionCountry,ym:s:regionArea',
            'metrics'    => 'ym:s:visits',
            'sort'       => '-ym:s:visits',
            'limit'      => $limit,
        ];

        return new Response($this->client->request($params));
    }

    /** Пол и возраст пользователей */

    /**
     * За последние N дней
     * @param int $days
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getAge($days = 30, int $limit = 20): Response
    {
        list($startDate, $endDate) = $this->differenceDate($days);

        return $this->getAgeForPeriod($startDate, $endDate, $limit);
    }

    /**
     * За указанный период
     * @param DateTime $startDate
     * @param DateTime $endDate
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getAgeForPeriod(DateTime $startDate, DateTime $endDate, int $limit = 20): Response
    {
        $params = [
            'date1'      => $startDate->format('Y-m-d'),
            'date2'      => $endDate->format('Y-m-d'),
            'preset'     => 'age',
            'limit'      => $limit,
        ];

        return new Response($this->client->request($params));
    }

    /**
     * За последние N дней
     * @param int $days
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getGender($days = 30, int $limit = 20): Response
    {
        list($startDate, $endDate) = $this->differenceDate($days);

        return $this->getGenderForPeriod($startDate, $endDate, $limit);
    }

    /**
     * За указанный период
     * @param DateTime $startDate
     * @param DateTime $endDate
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getGenderForPeriod(DateTime $startDate, DateTime $endDate, int $limit = 20): Response
    {
        $params = [
            'date1'      => $startDate->format('Y-m-d'),
            'date2'      => $endDate->format('Y-m-d'),
            'preset'     => 'gender',
            'limit'      => $limit,
        ];

        return new Response($this->client->request($params));
    }

    /**
     * За последние N дней
     * @param int $days
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getAgeGender($days = 30, int $limit = 20): Response
    {
        list($startDate, $endDate) = $this->differenceDate($days);

        return $this->getAgeGenderForPeriod($startDate, $endDate, $limit);
    }

    /**
     * За указанный период
     * @param DateTime $startDate
     * @param DateTime $endDate
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getAgeGenderForPeriod(DateTime $startDate, DateTime $endDate, int $limit = 20): Response
    {
        $params = [
            'date1'      => $startDate->format('Y-m-d'),
            'date2'      => $endDate->format('Y-m-d'),
            'preset'     => 'age_gender',
            'limit'      => $limit,
        ];

        return new Response($this->client->request($params));
    }

    /** Поисковые фразы */

    /**
     * За последние N дней
     * @param int $days
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getSearchPhrases($days = 30, int $limit = 20): Response
    {
        list($startDate, $endDate) = $this->differenceDate($days);

        return $this->getSearchPhrasesForPeriod($startDate, $endDate, $limit);
    }

    /**
     * За указанный период
     * @param DateTime $startDate
     * @param DateTime $endDate
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getSearchPhrasesForPeriod(DateTime $startDate, DateTime $endDate, int $limit = 20): Response
    {
        $params = [
            'date1'      => $startDate->format('Y-m-d'),
            'date2'      => $endDate->format('Y-m-d'),
            'preset'     => 'sources_search_phrases',
            'limit'      => $limit,
        ];

        return new Response($this->client->request($params));
    }

    /** Данные по шаблону */
    /**
     * Шаблоны (preset) автоматически задают метрики и группировки,
     * которые необходимы для того или иного отчета.
     * Список всех шаблонов доступен по ссылке - tech.yandex.ru/metrika/../presets-docpage.
     */

    /**
     * За последние N дней
     * @param string $preset
     * @param int $days
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getPreset(string $preset, int $days = 30, int $limit = 10): Response
    {
        list($startDate, $endDate) = $this->differenceDate($days);

        return $this->getPresetForPeriod($preset, $startDate, $endDate, $limit);
    }

    /**
     * За указанный период
     * @param string $preset
     * @param DateTime $startDate
     * @param DateTime $endDate
     * @param int $limit
     * @return Response
     * @throws Exception\ClientException
     */
    public function getPresetForPeriod(string $preset, DateTime $startDate, DateTime $endDate, int $limit = 10): Response
    {
        $params = [
            'preset' => $preset,
            'date1' => $startDate->format('Y-m-d'),
            'date2' => $endDate->format('Y-m-d'),
            'limit' => $limit
        ];

        return new Response($this->client->request($params));
    }


    /**
     * Произвольный запрос
     * @param array $params
     * @return Response
     * @throws Exception\ClientException
     */
    public function customQuery(array $params): Response
    {
        return new Response($this->client->request($params));
    }

    public function getClient(): Client
    {
        return $this->client;
    }

    public function setClient(Client $client): void
    {
        $this->client = $client;
    }

    private function differenceDate($amountOfDays = 30): array
    {
        $endDate = new DateTime();
        $startDate = (new DateTime())->sub(new DateInterval("P{$amountOfDays}D"));

        return [$startDate, $endDate];
    }
}