<?php

namespace Ignite\Core\Models\Dashboard;

use Carbon\Carbon;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;

class Dashboard
{
    /**
     * @var Manager
     */
    private $manager;

    /**
     * @var string
     */
    private $name;

    /**
     * @var bool
     */
    private $autoLocate;

    /**
     * @var Carbon
     */
    private $start;

    /**
     * @var Carbon
     */
    private $end;

    /**
     * Dashboard constructor.
     *
     * @param Manager $manager
     */
    public function __construct(Manager $manager)
    {
        $this->manager = $manager;
    }

    /**
     * The configured dashboard to show by default.
     *
     * @param string $name
     *
     * @return $this
     */
    public function name($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Determine whether or not we should autolocated modules and their charts.
     *
     * @param bool $flag
     *
     * @return $this
     */
    public function autoLocate($flag)
    {
        $this->autoLocate = $flag;

        return $this;
    }

    /**
     * The start date constraint for chart queries.
     *
     * @param string $date
     *
     * @return Dashboard
     */
    public function startDate($date)
    {
        $this->start = Carbon::createFromFormat('Y-m-d', $date);

        return $this;
    }

    /**
     * The end date constraint for chart queries.
     *
     * @param string $date
     *
     * @return Dashboard
     */
    public function endDate($date)
    {
        $this->end = Carbon::createFromFormat('Y-m-d', $date);

        return $this;
    }

    /**
     * Render the dashboard view.
     *
     * @param string $view
     *
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     */
    public function render(string $view)
    {
        $dashboardModules = $this->chartsForAllModules($this->name);

        $options = $dashboardModules->keys();

        if (! $dashboardModules->has($this->name)) {
            $this->name = $options->last();
        }

        return view($view, [
            'charts' => $this->calculateCharts($dashboardModules)->values(),
            'dashboards' => $options->count() > 1 ? $options : [],
            'dashboard' => $this->name,
            'start' => $this->start,
            'end' => $this->end,
        ]);
    }

    /**
     * If we auto-locate, we go through each module and look for a dashboard.json
     * file, if we find it we parse the file and load it into memory so that
     * we can create Chart classes. If we don't autoload, we'll just load
     * the configured default dashboard.
     *
     * @param string $dashboard
     *
     * @return Collection
     */
    protected function chartsForAllModules(string $dashboard)
    {
        if ($this->autoLocate) {
            $types = $this->manager->locate();
        } else {
            $types = collect([$dashboard => $this->manager->load($dashboard)]);
        }

        return $types->map(function ($board) {
            return collect($board)->map(function ($charts) {
                return collect($charts);
            });
        });
    }

    /**
     * The charts for the selected dashboard.
     *
     * @param Collection $type
     *
     * @return Collection
     */
    protected function chartsForSelectedModule(Collection $type)
    {
        return $type->flatMap(function ($chart) {
            return $chart;
        })->map(function ($chart) {
            return app(Arr::pull($chart, 'class'), ['attributes' => $chart]);
        });
    }

    /**
     * Calculate the values for the charts provided with the given module.
     *
     * @param Collection $dashboardModules
     *
     * @return Manager
     */
    protected function calculateCharts(Collection $dashboardModules)
    {
        $selectedDashboardModule = $dashboardModules->get($this->name);

        $charts = $this->chartsForSelectedModule($selectedDashboardModule);

        try {
            $this->manager->addManyCharts($charts);
        } catch (\Exception $exception) {
            logger()->critical('Unable to add charts.', compact('exception'));
            return $this->manager;
        }

        return $this->manager->calculate();
    }
}
