<?php

namespace Dipper\Foundation\Repository;

use Illuminate\Support\Arr;
use Illuminate\Database\Eloquent\Builder as EloquentBuilder;

/**
 * Class AggregateRepository.
 */
trait AggregateRepository
{
    /**
     * Retrieve the "count" result of the query.
     *
     * @param  string  $columns
     * @return int
     */
    public function count($columns = '*')
    {
        return (int) $this->aggregate(__FUNCTION__, Arr::wrap($columns));
    }

    /**
     * Retrieve the minimum value of a given column.
     *
     * @param  string  $column
     * @return mixed
     */
    public function min($column)
    {
        return $this->aggregate(__FUNCTION__, [$column]);
    }

    /**
     * Retrieve the maximum value of a given column.
     *
     * @param  string  $column
     * @return mixed
     */
    public function max($column)
    {
        return $this->aggregate(__FUNCTION__, [$column]);
    }

    /**
     * Retrieve the sum of the values of a given column.
     *
     * @param  string  $column
     * @return mixed
     */
    public function sum($column)
    {
        $result = $this->aggregate(__FUNCTION__, [$column]);

        return $result ?: 0;
    }

    /**
     * Retrieve the average of the values of a given column.
     *
     * @param  string  $column
     * @return mixed
     */
    public function avg($column)
    {
        return $this->aggregate(__FUNCTION__, [$column]);
    }

    /**
     * Alias for the "avg" method.
     *
     * @param  string  $column
     * @return mixed
     */
    public function average($column)
    {
        return $this->avg($column);
    }

    /**
     * Execute an aggregate function on the database.
     *
     * @param  string  $function
     * @param  array   $columns
     * @return mixed
     */
    public function aggregate($function, $columns = ['*'])
    {
        if ($this->model instanceof EloquentBuilder) {
            $query = $this->model->toBase();
        } else {
            $query = $this->model;
        }

        $clone = clone $this;

        $query = $query->cloneWithout(['columns'])
            ->cloneWithoutBindings(['select']);

        $query = $this->setAggregate($query, $function, $columns);

        $results = $this->rememberCache($query, function ($model) {
            return $model->get();
        });

        $this->resetModel();
        
        if (! $results->isEmpty()) {
            return array_change_key_case((array) $results[0])['aggregate'];
        }
    }

    /**
     * Set the aggregate property without running the query.
     *
     * @param  string  $function
     * @param  array  $columns
     * @return $this
     */
    protected function setAggregate($query, $function, $columns)
    {
        $query->aggregate = compact('function', 'columns');

        if (empty($query->groups)) {
            $query->orders = null;

            $query->bindings['order'] = [];
        }

        return $query;
    }
}
