<?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\User;
use Ignite\Core\Models\Dashboard\Concerns\DeterminesPercentageChange;
use Ignite\Core\Models\Dashboard\Stat;
use Illuminate\Support\Facades\DB;

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

    /**
     * Create a new stat instance.
     *
     * @param  array|object $attributes
     */
    public function __construct($attributes = [])
    {
        $attributes = array_merge([
            'label' => 'Participants enrolled',
            '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 'participants-enrolled-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 = DB::table('core_participant')
                        ->select(DB::raw('COUNT(user_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 DB::table('core_participant')
                 ->selectRaw('DATE(created_at) as `date`, COUNT(user_id) as `total`')
                 ->whereNull('deleted_at')
                 ->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 $sub = DB::table('core_participant')
                        ->selectRaw('COUNT(user_id)')
                        ->where('internal', User::TYPE_PARTICIPANT)
                        ->where('status', '<>', User::STAT_INACTIVE)
                        ->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 = 'month';

        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 = $date;
            }

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

        ksort($values);

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