<?php

namespace Dipper\Console;

use Exception;
use Illuminate\Filesystem\Filesystem;

class PackageManifest
{
    use Finder;

    /**
     * The filesystem instance.
     *
     * @var \Illuminate\Filesystem\Filesystem
     */
    public $files;

    /**
     * The manifest path.
     *
     * @var string|null
     */
    public $manifestPath;

    /**
     * The loaded manifest array.
     *
     * @var array
     */
    public $manifest;

    /**
     * Create a new package manifest instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->files = new Filesystem;
        $this->manifestPath = $this->getCachedPackagesPath();
    }

    /**
     * Get all of the service provider class names for all packages.
     *
     * @return array
     */
    public function providers()
    {
        return array_values($this->getManifest());
    }

    /**
     * Get the current package manifest.
     *
     * @return array
     */
    public function getManifest()
    {
        if (! is_null($this->manifest)) {
            return $this->manifest;
        }

        if (! file_exists($this->manifestPath)) {
            $this->build();
        }

        $this->files->get($this->manifestPath, true);

        return $this->manifest = file_exists($this->manifestPath) ?
            $this->files->getRequire($this->manifestPath) : [];
    }

    /**
     * Build the manifest and write it to disk.
     *
     * @return void
     */
    public function build()
    {
        $domains = $this->listDomains();

        $providers = [];

        $domains->map(function ($domain) use (&$providers) {
            if ($this->files->exists($path = $domain->realPath.'/composer.json')) {
                $package = json_decode($this->files->get($path), true);
                if (!$package['extra']['dipper']['dont-discover']) {
                    if (isset($package['extra']['dipper']['providers']) && is_array($package['extra']['dipper']['providers'])) {
                        $providers = array_merge($providers, $package['extra']['dipper']['providers']);
                    } else {
                        $providers[] = $this->getDomainServiceProvider($domain);
                    }
                }
            }
        });

        $this->write($providers);
    }

    /**
     * Write the given manifest array to disk.
     *
     * @param  array  $manifest
     * @return void
     * @throws \Exception
     */
    protected function write(array $manifest)
    {
        if (! is_writable(dirname($this->manifestPath))) {
            throw new Exception('The '.dirname($this->manifestPath).' directory must be present and writable.');
        }

        $this->files->put(
            $this->manifestPath,
            '<?php return '.var_export($manifest, true).';',
            true
        );
    }
}
