<?php

namespace Ignite\Core\Models\Dashboard\Charts;

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;
use Illuminate\Support\Carbon;
use Throwable;

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

    /**
     * Create a new stat instance.
     *
     * @param Client $analytics
     * @param array|object $attributes
     */
    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
     */
    protected function prepareData(string $metric)
    {
        if (! $this->get('data', false)) {
            if (! cache()->has($this->cacheKey())) {
                list($start, $end) = $this->getDateRange();
                list($previousStart, $previousEnd) = $this->getPreviousDateRange();

                try {
                    $this->attributes['data'] = $this->fetchData($metric, $start, $end, $previousStart, $previousEnd);
                    cache()->put($this->cacheKey(), $this->toArray(), 60);
                } catch (Throwable $exception) {
                    report($exception);
                    $this->applyUnavailableState();
                    cache()->put($this->cacheKey(), $this->toArray(), 5);
                }
            } else {
                $this->attributes = cache()->get($this->cacheKey());
            }
        }

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

    /**
     * The fetched metrics for the given period
     *
     * @param string $metric
     * @param Carbon $start
     * @param Carbon $end
     * @param Carbon $previousStart
     * @param Carbon $previousEnd
     * @return mixed
     */
    protected function fetchData(string $metric, Carbon $start, Carbon $end, Carbon $previousStart, Carbon $previousEnd)
    {
        $response = $this->analytics->fetchResponse($metric, $start, $end, $previousStart, $previousEnd);

        if ($this instanceof WithInlineSeries) {
            $series = $this->analytics->fetchSeries($response, $start, $end);
            $this->setSeries($series);
        }

        if ($this instanceof WithPercentageChange) {
            [$currentPercentage, $previousPercentage] = $this->analytics->fetchChangingTotals($response);
            $this->setPercentageChange($currentPercentage, $previousPercentage);
        }

        $metricValue = $this->analytics->metricValue($response);
        return $this->decorate($metricValue);
    }

    /**
     * Surface Google Analytics failures to Flare while keeping the admin
     * dashboard usable when analytics is missing or misconfigured.
     *
     * @return void
     */
    protected function applyUnavailableState(): void
    {
        $this->attributes['data'] = 'Unavailable';
        $this->attributes['help'] = 'Google Analytics data is unavailable. Please verify the analytics environment configuration and service account access.';

        if ($this instanceof WithInlineSeries) {
            $this->offsetSet('series', []);
        }

        if ($this instanceof WithPercentageChange) {
            $this->offsetUnset('current');
            $this->offsetUnset('previous');
            $this->offsetUnset('direction');
            $this->offsetUnset('percentage');
        }
    }

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