<?php

namespace Ignite\Core\Models\Dashboard;

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 add(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
	 */
	public function addMany($charts)
	{
		foreach ($charts as $row => $chart) {
			$this->add($chart);
		}

		return $this;
	}

    /**
     * Disable the put method.
     *
     * @param  string $key
     * @param  mixed  $value
     * @throws \Exception
     */
    public function put($key, $value)
    {
        throw new \Exception('You must use add()');
    }

    /**
     * Locate charts in other modules.
     *
     * @return $this
     */
    public function locate()
    {
        return collect(Module::all())
            ->filter(function (\Nwidart\Modules\Module $module) {
                return $module->enabled();
            })
            ->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);
    }
}
