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

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

    /**
     * Create a new stat instance.
     *
     * @param array|object $attributes
     */
    public function __construct($attributes = [])
    {
        $attributes = array_merge([
            'label' => 'Amount redeemed',
            "type" => "metric",
            'sort' => 3,
        ], $attributes);

        parent::__construct($attributes);
    }

    /**
     * The HTML element ID.
     *
     * @return string
     */
    public function getElement()
    {
        return 'transaction-amount-redeemed-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('core.user.transaction.browse')->apply(
                DB::table('core_transaction')
                    ->select(DB::raw('COUNT(id) as total'))
                    ->selectSub($this->getSubQuery($this->getDateRange()), 'current')
                    ->selectSub($this->getSubQuery($this->getPreviousDateRange()), 'previous')
            )->first();

            $this->setPercentageChange($result->current, $result->previous);

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

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

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

    /**
     * Get the sub-query with date constraints.
     *
     * @param array $range
     *
     * @return \Illuminate\Database\Query\Builder
     */
    protected function getSubQuery(array $range)
    {
        return QueryPermissionFilters::for('core.user.transaction.browse')->apply(
            DB::table('core_transaction')
                ->selectRaw('count(id)')
                ->whereNull('deleted_at')
                ->whereIn('type', ['MANUAL-REDEEM', 'REDEEMED'])
                ->whereRaw('DATE(created_at) BETWEEN ? AND ?', $range)
        );
    }

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

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

    /**
     * Scope the query.
     *
     * @param Builder $query
     *
     * @return Builder
     */
    protected function scope(Builder $query)
    {
        $query->whereIn('type', ['MANUAL-REDEEM', 'REDEEMED']);

        return $query;
    }

    /**
     * The query scoped to the given range.
     *
     * @param array $range
     *
     * @return Builder
     */
    protected function buildRangedQuery($range)
    {
        return QueryPermissionFilters::for('core.user.transaction.browse')->apply(
            DB::table('core_transaction')
                ->selectRaw('DATE(created_at) as `date`, COUNT(id) as `total`')
                ->whereNull('deleted_at')
                ->groupBy('date')
                ->havingBetween('date', $range)
                ->orderBy('date', 'asc')
        );
    }

    /**
     * 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] = round($value, PHP_ROUND_HALF_DOWN);
        }

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