<?php

namespace Ignite\Core\Services\Google\Analytics\Adapter;

use Carbon\CarbonPeriod;
use Google\Client as GoogleClient;
use Google\Collection as GoogleCollection;
use Google\Service\AnalyticsReporting;
use Google\Service\AnalyticsReporting\DateRange;
use Google\Service\AnalyticsReporting\Dimension;
use Google\Service\AnalyticsReporting\GetReportsRequest;
use Google\Service\AnalyticsReporting\GetReportsResponse;
use Google\Service\AnalyticsReporting\Metric;
use Google\Service\AnalyticsReporting\ReportRequest;
use GuzzleHttp\Exception\ConnectException;
use Ignite\Core\Services\Google\Analytics\Adapter\AdapterInterface;
use Illuminate\Support\Carbon;

class AdapterUA implements AdapterInterface
{
    /**
     * @var GoogleClient
     */
    protected $client;

    /**
     * @param GoogleClient $client
     */
    public function __construct(GoogleClient $client)
    {
        $this->client = $client;
    }

    /**
     * {@inheritdoc}
     */
    public function fetchChangingTotals(GoogleCollection $response): array
    {
        $reportData = $response->getReports()[0]->getData();
        $totals = $reportData->getTotals();
        $current = $totals[0]->getValues()[0];
        $previous = $totals[1]->getValues()[0];

        return [$current, $previous];
    }

    /**
     * {@inheritdoc}
     */
    public function fetchResponse(
        string $metricName,
        Carbon $start,
        Carbon $end,
        Carbon $previousStart = null,
        Carbon $previousEnd = null
    ): GetReportsResponse {
        $currentPeriod = new DateRange();
        $currentPeriod->setStartDate($start->format('Y-m-d'));
        $currentPeriod->setEndDate($end->format('Y-m-d'));

        $previousPeriod = new DateRange();
        $previousPeriod->setStartDate($previousStart->format('Y-m-d'));
        $previousPeriod->setEndDate($previousEnd->format('Y-m-d'));

        $metric = new Metric();
        $metric->setExpression("ga:{$metricName}");
        $metric->setAlias($metricName);

        $request = $this->setupRequest([$currentPeriod, $previousPeriod], $metric);
        $body = new GetReportsRequest();
        $body->setReportRequests([$request]);

        try {
            $response = $this->service()->reports->batchGet($body);
        } catch (ConnectException $connectException) {
            logger()->error($connectException->getMessage(), $connectException->getHandlerContext());
            return null;
        }

        return $response;
    }

    /**
     * {@inheritdoc}
     */
    public function fetchSeries(GoogleCollection $response, Carbon $start, Carbon $end): array
    {
        $series = [];

        $intervals = CarbonPeriod::create($start, $end);
        foreach ($intervals as $interval) {
            $key = $interval->format('j');
            $series[$key] = 0;
        }

        $reports = $response->getReports();

        if (! empty($reports)) {
            /** @var \Google\Service\AnalyticsReporting\Report $report */
            /** @var \Google\Service\AnalyticsReporting\ReportData $reportData */
            $reportData = $reports[0]->getData();

            /** @var \Google\Service\AnalyticsReporting\ReportRow $row */
            foreach ($reportData->getRows() as $row) {
                $keys = array_map(function ($value) {
                    return (int) $value;
                }, $row->getDimensions());

                $values = $row->getMetrics()[0]->getValues();
                $dimensionValues = array_combine($keys, $values);
                $key = key($dimensionValues);

                if (isset($series[$key])) {
                    $series[$key] = $dimensionValues[$key];
                }
            }
        }

        return $series;
    }

    /**
     * {@inheritdoc}
     */
    public function metricValue(GoogleCollection $response): float
    {
        if (! isset($response[0])) {
            return 0;
        }

        /** @var Google_Service_AnalyticsReporting_Report $report */
        $report = $response[0];

        /** @var Google_Service_AnalyticsReporting_ReportData $data */
        $data = $report->getData();

        /** @var \Google_Service_AnalyticsReporting_ReportRow[] $rows */
        $totals = $data->getTotals();

        if (! $totals || ! isset($totals[0])) {
            return 0;
        }

        /** @var \Google_Service_AnalyticsReporting_DateRangeValues $total */
        $total = $totals[0];

        /** @var \Google_Service_AnalyticsReporting_DateRangeValues[] $metrics */
        $values = $total->getValues();

        if (! $values || ! isset($values[0])) {
            return 0;
        }

        $value = $values[0];

        if (! $value) {
            return 0;
        }

        return $value;
    }

    /**
     * Instantiate the Google Service for the API.
     *
     * @param  Google_Client|null $client
     * @return AnalyticsReporting
     */
    protected function service(Google_Client $client = null): AnalyticsReporting
    {
        $scopes = [
            'https://www.googleapis.com/auth/analytics.readonly'
        ];

        $client = $client ?? $this->client;
        $client->setApplicationName("Ignite 3");
        $client->setAuthConfig(config('core.google.analytics.key_file'));
        $client->setScopes($scopes);

        return new AnalyticsReporting($client);
    }

    /**
     * Given the date ranges and needed metric, create a request object.
     *
     * @param array $periods
     * @param Metric $metric
     * @return ReportRequest
     */
    protected function setupRequest(array $periods, Metric $metric): ReportRequest
    {
        $day = new Dimension();
        $day->setName("ga:day");

        $viewId = config('core.google.analytics.view');
        if (!$viewId) {
            throw new \Google\Exception('Google Analytics View ID is not set.');
        }

        $request = new ReportRequest();
        $request->setViewId($viewId);
        $request->setDateRanges($periods);
        $request->setMetrics([$metric]);
        $request->setDimensions([$day]);

        return $request;
    }
}
