<?php

namespace Ignite\Core\Models\Dashboard\Charts;

use Carbon\Carbon;
use Carbon\CarbonPeriod;
use Ignite\Core\Contracts\Dashboard\WithInlineSeries;
use Ignite\Core\Contracts\Dashboard\WithPercentageChange;
use Ignite\Core\Entities\Filters\QueryPermissionFilters;
use Ignite\Core\Models\Dashboard\Concerns\DeterminesPercentageChange;
use Ignite\Core\Models\Dashboard\Stat;
use Illuminate\Support\Facades\DB;

class ActivitiesSubmittedStat extends Stat implements WithPercentageChange, WithInlineSeries
{
    use DeterminesPercentageChange;

    /**
     * Create a new stat instance.
     *
     * @param array|object $attributes
     */
    public function __construct($attributes = [])
    {
        $attributes = array_merge([
            'label' => 'Activities Submitted',
            'color' => 'blue',
            "type" => "metric",
            'link' => url('/admin/participant/participants'),
            'sort' => 3,
        ], $attributes);

        parent::__construct($attributes);
    }

    /**
     * The HTML element ID.
     *
     * @return string
     */
    public function getElement()
    {
        return 'activities-submitted-correlated-stat';
    }

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

    /**
     * Get the correct data value from storage.
     *
     * @return mixed
     */
    public function getData()
    {
        if (! $this->get('data', false)) {
            $result = QueryPermissionFilters::for('activity.submission.all.browse')->apply(
                DB::table('activity_submission')
                    ->select(DB::raw('COUNT(activity_id) as total'))
                    ->selectSub($this->getSubQuery($this->getDateRange()), 'current')
                    ->selectSub($this->getSubQuery($this->getPreviousDateRange()), 'previous')
            )->first();

            if ($this instanceof WithPercentageChange) {
                $this->setPercentageChange($result->current, $result->previous);
            }

            $this->offsetSet('data', $result->current);
        }

        if ($this instanceof WithInlineSeries) {
            $this->setSeries($this->getSeriesData());
        }

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

    /**
     * Query the database for series data.
     *
     * @return array
     */
    protected function getSeriesData()
    {
        $results = $this->buildRangedQuery($this->getDateRange())->get();

        return $results->keyBy('date')->map->total->toArray();
    }

    /**
     * The query scoped to the given range.
     * @param array $range
     *
     * @return \Illuminate\Database\Query\Builder
     */
    protected function buildRangedQuery($range)
    {
        return QueryPermissionFilters::for('activity.submission.all.browse')->apply(
            DB::table('activity_submission')
                ->selectRaw('DATE(activity_submission.created_at) as `date`')
                ->selectRaw('COUNT(activity_submission.user_id) as `total`')
                ->groupBy('date')
                ->havingBetween('date', $range)
                ->orderBy('date', 'asc')
        );
    }

    /**
     * Get the sub-query with date contraints.
     *
     * @param array $range
     *
     * @return \Illuminate\Database\Query\Builder
     */
    protected function getSubQuery(array $range)
    {
        /**
         * @var Carbon $end
         * @var Carbon $end
         */
        list($start, $end) = $range;

        return QueryPermissionFilters::for('activity.submission.all.browse')->apply(
            DB::table('activity_submission')
                ->selectRaw('COUNT(user_id)')
                ->whereRaw(
                    'DATE(created_at) BETWEEN ? AND ?',
                    [$start->format('Y-m-d'), $end->format('Y-m-d')]
                )
        );
    }

    /**
     * Set the percentage change values using current versus previous ranges.
     *
     * @param int|float $current
     * @param int|float $previous
     *
     * @return self
     */
    public function setPercentageChange($current, $previous)
    {
        $this->offsetSet('current', number_format($current, 0));
        $this->offsetSet('previous', number_format($previous, 0));
        $this->offsetSet('direction', $this->determineChangeType($current, $previous));
        $this->offsetSet('percentage', $this->determinePercentageChange($current, $previous));

        return $this;
    }

    public function setSeries(array $data)
    {
        $values = [];
        $type = 'range';

        foreach ($data as $date => $value) {
            if ($type === 'month') {
                $key = Carbon::parse($date)->format('d');
            } else {
                $key = $date;
            }
            $values[$key] = intval($value);
        }

        /**
         * @var Carbon $end
         * @var Carbon $end
         */
        list($start, $end) = $this->getDateRange();
        $intervals = CarbonPeriod::create($start, $end);

        foreach ($intervals as $interval) {
            if ($type === 'month') {
                $key = $interval->format('d');
            } else {
                $key = $interval->format('Y-m-d');
            }

            if (! array_key_exists($key, $values)) {
                $values[$key] = 0;
            }
        }

        ksort($values);

        $this->offsetSet('series', $values);
    }
}
