<?php

namespace Ignite\Core\Models\Dashboard\Charts;

use Ignite\Core\Entities\Filters\QueryPermissionFilters;
use Ignite\Core\Entities\Participant;
use Ignite\Core\Entities\User;
use Ignite\Core\Models\Dashboard\Gauge;
use Illuminate\Support\Facades\DB;

class ActivityEngagementGauge extends Gauge
{
    /**
     * Create a new stat instance.
     *
     * @param array|object $attributes
     */
    public function __construct($attributes = [])
    {
        parent::__construct(array_merge([
            'label' => 'Activity Engagement',
        ], $attributes ?? []));
    }

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

    /**
     * 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)) {
            /**
             * @var \Carbon\Carbon $start
             * @var \Carbon\Carbon $end
             */
            list($start, $end) = $this->getDateRange();

            $this->attributes['data'] = $this->percentageQuery()->get();
        }

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

    /**
     * @return array
     */
    protected function getParticipantTypes()
    {
        return config(
            'core.report.participant-types',
            array_keys(
                config(
                    'core.participant.types',
                    Participant::select('type')->distinct()->pluck('type', 'type')->toArray()
                )
            )
        );
    }

    /**
     */
    protected function percentageQuery()
    {
        /* Activity Engagement query
         * -------------------------------------------------- //
         * This is probably the most elegant way of writing
         * this query without using subqueries to do the
         * following:
         * - Part 1: Find the count of distinct participants
         * - Part 2: Find the count of distinct participants
         *           with submissions within 90 days
         * - Divide the results of Part 2 by Part 1 to get
         *   the activity engagement percentage, or percent
         *   of participants that have submitted activities
         *   within the the last 90 days
         *
         * SQL:
         * SELECT FORMAT((COUNT(DISTINCT activity_submission.user_id) / COUNT(DISTINCT core_user.user_id) * 100), 0) AS percentage
         * FROM core_user
         *          JOIN core_participant
         *              ON core_user.user_id = core_participant.user_id
         *          LEFT JOIN activity_submission
         *              ON core_user.user_id = activity_submission.user_id
         *              AND activity_submission.created_at BETWEEN DATE_ADD(CURDATE(), INTERVAL -90 DAY) AND CURDATE()
         * WHERE core_participant.internal = 0
         *   AND core_participant.status = 1
         *   AND core_participant.type = 'Participant';
         * -------------------------------------------------- */

        // In this case, we want to filter this query to all the participants, regardless of if they have a submission
        // or not because we want the percent of users that have made a submission in the last 90 days.
        return QueryPermissionFilters::for('core.user.participant.browse')->apply(
            DB::query()
                ->selectRaw(
                    'FORMAT((count(distinct activity_submission.user_id) / count(distinct core_user.user_id)) * 100, 0) as percentage'
                )
                ->from('core_user')
                ->join('core_participant', 'core_user.user_id', 'core_participant.user_id')
                ->leftJoin('activity_submission', function ($join) {
                    /* Constraint for only the last 90 days
                     * -------------------------------------------------- */
                    $join->on('core_user.user_id', '=', 'activity_submission.user_id')
                        ->whereBetween(
                            'activity_submission.created_at',
                            [
                                DB::raw('DATE_ADD(CURDATE(), INTERVAL -90 DAY)'),
                                DB::raw('CURDATE()'),
                            ]
                        );
                })
                ->where('core_participant.internal', User::TYPE_PARTICIPANT)
                ->where('core_participant.status', '=', User::STAT_ACTIVE)
                ->whereIn('core_participant.type', $this->getParticipantTypes())
        );
    }
}
