<?php

namespace Ignite\Core\Models\Dashboard;

use Exception;
use Illuminate\Support\Collection;
use Ignite\Core\Contracts\Dashboard\Stat;
use Ignite\Core\Contracts\Dashboard\Chart;
use Ignite\Core\Contracts\Dashboard\Donut;
use Ignite\Core\Contracts\Dashboard\Table;
use Nwidart\Modules\Facades\Module;

class Manager extends Collection
{
    /**
     * Add a Stat class to the collection.
     *
     * @param  Chart $chart
     * @return $this
     */
    public function addChart(Chart $chart)
    {
        $this->offsetSet($chart->getElement(), $chart);

        return $this;
    }

    /**
     * Add many Stat classes to the collection.
     *
     * @param array|\ArrayIterator|\Illuminate\Support\Collection $charts
     *
     * @return $this
     * @throws Exception
     */
    public function addManyCharts($charts)
    {
        foreach ($charts as $row => $chart) {
            $this->addChart($chart);
        }

        return $this;
    }

    /**
     * @inheritDoc
     * @throws Exception
     */
    public function add($item)
    {
        throw new Exception('You must use addChart()');
    }

    /**
     * @inheritDoc
     * @throws Exception
     */
    public function put($key, $value)
    {
        throw new Exception('You must use addChart()');
    }

    /**
     * Locate charts in other modules.
     *
     * @return Collection
     */
    public function locate()
    {
        return collect(Module::all())
            ->filter(function (\Nwidart\Modules\Module $module) {
                return $module->isEnabled();
            })
            ->sortBy(function (\Nwidart\Modules\Module $module) {
                return $module->get('order', 0);
            })->map(function (\Nwidart\Modules\Module $module) {
                try {
                    return $this->parseJsonSchema($module);
                } catch (Exception $e) {
                    return null;
                }
            })
            ->filter();
    }

    /**
     * Parse the dashboard json schema.
     *
     * @param  \Nwidart\Modules\Module $module
     * @return array
     * @throws Exception
     */
    private function parseJsonSchema(\Nwidart\Modules\Module $module)
    {
        $json = $module->json('dashboard.json')->getContents();

        $data = json_decode($json, true);
        if (json_last_error() !== JSON_ERROR_NONE) {
            throw new Exception(sprintf(
                'Unable to parse json for module %s: %s.',
                $module->getName(),
                json_last_error_msg()
            ));
        }

        if (! isset($data['dashboard'])) {
            throw new Exception(sprintf(
                'Missing expected json key "dashboard" for module %s.',
                $module->getName()
            ));
        }

        return $data['dashboard'];
    }

    /**
     * Calculate all chart data.
     *
     * @return void
     */
    public function calculate()
    {
        $this->map(function (Chart $chart) {
            return $chart->process();
        });
    }

    /**
     * Determine if the chart is a stat chart.
     *
     * @param  Chart $chart
     * @return bool
     */
    public function isStat($chart)
    {
        return $chart instanceof Stat;
    }

    /**
     * Filter down to only include stat charts.
     *
     * @return static
     */
    public function stats()
    {
        return $this->filter(function ($chart) {
            return $this->isStat($chart);
        })->sortBy('sort', SORT_NUMERIC);
    }

    /**
     * Determine if the chart is a donut chart.
     *
     * @param  Chart $chart
     * @return bool
     */
    public function isDonut($chart)
    {
        return $chart instanceof Donut;
    }

    /**
     * Filter down to only include donut charts.
     *
     * @return static
     */
    public function donuts()
    {
        return $this->filter(function ($chart) {
            return $this->isDonut($chart);
        })->sortBy('sort', SORT_NUMERIC);
    }

    /**
     * Determine if the chart is a line chart.
     *
     * @param  Chart $chart
     * @return bool
     */
    public function isLine($chart)
    {
        return $chart instanceof Line;
    }

    /**
     * Filter down to only include line charts.
     *
     * @return static
     */
    public function lines()
    {
        return $this->filter(function ($chart) {
            return $this->isLine($chart);
        })->sortBy('sort', SORT_NUMERIC);
    }

    /**
     * Determine if the chart is a table chart.
     *
     * @param  Chart $chart
     * @return bool
     */
    public function isTable($chart)
    {
        return $chart instanceof Table;
    }

    /**
     * Filter down to only include line charts.
     *
     * @return static
     */
    public function tables()
    {
        return $this->filter(function ($chart) {
            return $this->isTable($chart);
        })->sortBy('sort', SORT_NUMERIC);
    }
}
