<?php

namespace Ignite\Core\Models\Dashboard\Charts;

use Carbon\Carbon;
use Carbon\CarbonPeriod;
use Ignite\Core\Entities\Filters\QueryPermissionFilters;
use Ignite\Core\Models\Dashboard\Line;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

class TransactionsTypeLine extends Line
{
    /**
     * ParticipantEnrollmentLine.
     *
     * @param array $attributes
     */
    public function __construct($attributes = [])
    {
        parent::__construct(array_merge([
            'label' => 'Transactions by type',
            'element' => 'transactions-type-line',
            'help' => 'Transactions by type: Earned vs Redeemed',
            'labels' => [],
            "colors" => ["maroon", "purple", "#aaaaaa", "#dadada"],
            "strokeWidth" => [3, 3, 3, 3],
            "strokeCurve" => "smooth",
            "strokeDash" => [0, 0, 3, 3],
        ], $attributes));
    }

    /**
     * The HTML element ID.
     *
     * @return string
     */
    public function getElement()
    {
        return 'transactions-type-line';
    }

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

    /**
     * Get the correct data value from storage.
     *
     * @return mixed
     */
    public function getData()
    {
        if (! $this->get('data', false)) {
            $this->attributes['data'] = $this->query();
        }

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

    /**
     * Transform the data.
     *
     * @param Collection $results
     * @param array $initial
     *
     * @return array
     */
    protected function transformData(Collection $results, array $initial = [])
    {
        foreach ($initial as $key => $series) {
            foreach ($results->toArray() as $date => $items) {
                $initial[$key]['data'][$date] = [
                    'x' => Carbon::parse($date)->format('jS'),
                    'y' => 0,
                ];
                if (! empty($items)) {
                    foreach ($items as $item) {
                        if ($key === $item->name) {
                            $initial[$key]['data'][$date] = [
                                'x' => Carbon::parse($date)->format('jS'),
                                'y' => $item->total,
                            ];
                        }
                    }
                }
                $initial[$key]['data'] = array_values($initial[$key]['data']);
            }
        }

        return array_values($initial);
    }

    /**
     * Query the database.
     *
     * @return array
     */
    protected function query()
    {
        DB::statement("SET SESSION sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''))");

        $dataCurrent = [
            'earned_current' => [
                'name' => 'Earned (current)',
                'type' => 'line',
                'data' => [],
            ],
            'redeemed_current' => [
                'name' => 'Redeemed (current)',
                'type' => 'line',
                'data' => [],
            ],
        ];

        $rangeCurrent = $this->queryForRange(
            $this->getDateRange(),
            'earned_current',
            'redeemed_current'
        );

        $current = $this->transformData($rangeCurrent, $dataCurrent);

        $dataPrevious = [
            'earned_previous' => [
                'name' => 'Earned (previous)',
                'type' => 'line',
                'data' => [],
            ],
            'redeemed_previous' => [
                'name' => 'Redeemed (previous)',
                'type' => 'line',
                'data' => [],
            ],
        ];

        $rangePrevious = $this->queryForRange(
            $this->getPreviousDateRange(),
            'earned_previous',
            'redeemed_previous'
        );

        $previous = $this->transformData($rangePrevious, $dataPrevious);

        return array_merge($current, $previous);
    }

    protected function queryForRange($range, $earnedAlias, $redeemedAlias)
    {
        list($start, $end) = $range;

        $period = CarbonPeriod::create($start, $end);

        $query = $this->getUnionQuery($earnedAlias, ['MANUAL-EARNED', 'EARNED'], $range);
        $redeemed = $this->getUnionQuery($redeemedAlias, ['MANUAL-REDEEM', 'REDEEMED'], $range);
        $query->unionAll($redeemed)->orderBy('date', 'asc');

        $result = $query->get()->groupBy('date');

        foreach ($period as $date) {
            $key = $date->format('Y-m-d');
            if (! $result->has($key)) {
                $result->put($key, []);
            }
        }

        return $result->sortKeys();
    }

    /**
     * @param $alias
     * @param $types
     * @param $range
     *
     * @return \Illuminate\Database\Query\Builder
     */
    protected function getUnionQuery($alias, $types, $range)
    {
        $derivedTable = QueryPermissionFilters::for('core.user.transaction.browse')->apply(
            DB::table('core_transaction')->select(['id', 'transaction_date'])
                ->whereNull('deleted_at')
                ->whereIn('type', $types)
                ->whereRaw('DATE(transaction_date) BETWEEN ? AND ?', $range)
        );

        return QueryPermissionFilters::for('core.user.transaction.browse')->apply(
            DB::table('core_transaction')
                ->selectRaw("'{$alias}' as name, DATE_FORMAT(transaction_date, '%Y-%m-%d') as date, COUNT(id) as total")
                ->from($derivedTable, $alias)
                ->groupBy('date')
                ->orderBy('date')
        );
    }
}
