<?php

namespace Ignite\Core\Models\Dashboard\Charts;

use Carbon\Carbon;
use Carbon\CarbonPeriod;
use Google_Service_AnalyticsReporting_Dimension;
use Google_Service_AnalyticsReporting_GetReportsRequest;
use Google_Service_AnalyticsReporting_Metric;
use Google_Service_AnalyticsReporting_Report;
use Google_Service_AnalyticsReporting_ReportData;
use GuzzleHttp\Exception\ConnectException;
use Ignite\Core\Contracts\Dashboard\WithInlineSeries;
use Ignite\Core\Contracts\Dashboard\WithPercentageChange;
use Ignite\Core\Models\Dashboard\Stat;
use Ignite\Core\Services\Google\Analytics\Client;

abstract class BaseGoogleAnalyticsMetric extends Stat
{
    /**
     * @var Client
     */
    private $analytics;

    /**
     * Create a new stat instance.
     *
     * @param array|object $attributes
     * @param Client $analytics
     */
    public function __construct(Client $analytics, $attributes = [])
    {
        $attributes = array_merge([
            'color' => 'white',
            'icon' => 'ion ion-ios-person-outline',
            'link' => '',
        ], $attributes);

        parent::__construct($attributes);

        $this->analytics = $analytics;
    }

    /**
     * The chart size in the layout.
     *
     * @return string
     */
    public function getSize()
    {
        return $this->get('size', 'col-xs-6 col-sm-3');
    }

    /**
     * Prepare the data for display.
     *
     * @param string $metric
     *
     * @return mixed
     * @throws \Google_Exception
     */
    protected function prepareData($metric)
    {
        if (! $this->get('data', false)) {
            if (! cache()->has($this->cacheKey())) {
                list($start, $end) = $this->getDateRange();
                list($previousStart, $previousEnd) = $this->getPreviousDateRange();
                $this->attributes['data'] = $this->fetchData($metric, $start, $end, $previousStart, $previousEnd);

                cache()->put($this->cacheKey(), $this->toArray(), 60);
            } else {
                $this->attributes = cache()->get($this->cacheKey());
            }
        }

        return $this->get('data');
    }

    /**
     * The number of unique visitors for the given period
     *
     * @param string $metric
     * @param Carbon $start
     * @param Carbon $end
     * @param Carbon $previousStart
     * @param Carbon $previousEnd
     *
     * @return int
     * @throws \Google_Exception
     */
    protected function fetchData($metric, Carbon $start, Carbon $end, Carbon $previousStart, Carbon $previousEnd)
    {
        $period = $this->period($start, $end);
        $previous = $this->period($previousStart, $previousEnd);

        $metric = $this->metric($metric);
        $request = $this->report([$period, $previous], $metric);

        $body = new Google_Service_AnalyticsReporting_GetReportsRequest();
        $body->setReportRequests([$request]);
        try {
            $response = $this->analytics->instance()->reports->batchGet($body);
        } catch (ConnectException $connectException) {
            logger()->error($connectException->getMessage(), $connectException->getHandlerContext());
            return null;
        }

        $reports = $response->getReports();
        if (! empty($reports)) {
            /** @var \Google_Service_AnalyticsReporting_Report $report */
            /** @var \Google_Service_AnalyticsReporting_ReportData $reportData */
            $reportData = $reports[0]->getData();

            if ($this instanceof WithInlineSeries) {
                $series = [];
                /** @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);
                    $series[$key] = $dimensionValues[$key];
                }

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

                ksort($series);

                $this->setSeries($series);
            }

            if ($this instanceof WithPercentageChange) {
                $totals = $reportData->getTotals();
                $current = $totals[0]->getValues()[0];
                $previous = $totals[1]->getValues()[0];
                $this->setPercentageChange($current, $previous);
            }
        }

        return $this->decorate($this->value($response));
    }

    /**
     * Get the period.
     *
     * @param Carbon $start
     * @param Carbon $end
     *
     * @return \Google_Service_AnalyticsReporting_DateRange
     */
    protected function period(Carbon $start, Carbon $end)
    {
        $period = $this->analytics->period();
        $period->setStartDate($start->format('Y-m-d'));
        $period->setEndDate($end->format('Y-m-d'));

        return $period;
    }

    /**
     * Get the sessions metric.
     *
     * @param string $type
     *
     * @return Google_Service_AnalyticsReporting_Metric
     */
    protected function metric($type)
    {
        $sessions = $this->analytics->metrics();
        $sessions->setExpression("ga:$type");
        $sessions->setAlias($type);

        return $sessions;
    }

    /**
     * The report class.
     *
     * @param $period
     * @param Google_Service_AnalyticsReporting_Metric $sessions
     *
     * @return \Google_Service_AnalyticsReporting_ReportRequest
     */
    protected function report($period, Google_Service_AnalyticsReporting_Metric $sessions)
    {
        $day = new Google_Service_AnalyticsReporting_Dimension();
        $day->setName("ga:day");

        $request = $this->analytics->report();
        $request->setViewId(config('core.google.analytics.view'));
        $request->setDateRanges($period);
        $request->setMetrics(array($sessions));
        $request->setDimensions(array($day));

        return $request;
    }

    /**
     * Get the metric value.
     *
     * @param \Google_Service_AnalyticsReporting_GetReportsResponse $reports
     *
     * @return int
     */
    protected function value(\Google_Service_AnalyticsReporting_GetReportsResponse $reports)
    {
        if (! isset($reports[0])) {
            return 0;
        }

        /** @var Google_Service_AnalyticsReporting_Report $report */
        $report = $reports[0];
        //dd($report);

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

        //dd($data->getTotals());

        /** @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;
    }

    /**
     * Decorate the value.
     *
     * @param string $value
     *
     * @return mixed
     */
    abstract protected function decorate($value);
}
