<?php

namespace Ignite\Core\Console;

use Illuminate\Console\Command;
use Ignite\Core\Demo\Generator;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

class ThemeScaffold extends Command
{
    /**
     * The console command name.
     *
     * @var string
     */
    protected $name = 'theme:scaffold';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Scaffold a new theme.';

    /**
     * The base themes folder.
     *
     * @var string
     */
    protected $folder = 'themes';

    /**
     * Create a new command instance.
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $name = $this->input->getArgument('name');
        $base = $this->input->getOption('base');
        $parent = $this->input->getOption('parent');
        $installNpm = $this->input->getOption('install-packages');
        $runGulp = $this->input->getOption('run-gulp');
        $installEnv = $this->input->getOption('install-env');

        $this->createDirectories($name);
        $this->createThemeJson($name, $parent);
        $this->copyDefaultFiles($name, $base);
        $this->customizeTheme($name, $base);

        if ($installNpm || $this->confirm('Would you like to run <fg=yellow>npm install</>')) {
            $this->installNpm($name);
        }

        if ($runGulp || $this->confirm('Would you like to run <fg=yellow>gulp</>')) {
            $this->runGulp($name);
        }

        if ($installEnv || $this->confirm('Would you like to set the theme in your .env file')) {
            $this->updateEnvFile($name);
        }

        $this->output->writeln('<fg=green>✓</> Done');

        return 0;
    }

    /**
     * Get the console command arguments.
     *
     * @return array
     */
    protected function getArguments()
    {
        return [
            ['name', InputArgument::REQUIRED, "'name' The name of the theme."],
        ];
    }

    /**
     * Get the console command options.
     *
     * @return array
     */
    protected function getOptions()
    {
        return [
            ['base', null, InputOption::VALUE_OPTIONAL, 'The theme on this theme should be based.', 'default'],
            ['parent', null, InputOption::VALUE_OPTIONAL, 'The parent theme.', 'default'],
            ['install-packages', 'p', InputOption::VALUE_NONE, 'Install npm packages.'],
            ['run-gulp', 'g', InputOption::VALUE_NONE, 'Run gulp.'],
            ['install-env', 'e', InputOption::VALUE_NONE, 'Install the theme in the env file.'],
        ];
    }

    /**
     * Create the directories we need.
     *
     * @param  string $name
     * @return Process
     */
    private function createDirectories($name)
    {
        $this->output->writeln('Creating the base directories...');
        $directories = [
            $this->getPath($name, 'assets'),
            $this->getPath($name, 'src'),
            $this->getPath($name, 'src/js'),
            $this->getPath($name, 'src/scss'),
            $this->getPath($name, 'views'),
        ];

        $command = sprintf('mkdir -p %s', implode(' ', $directories));
        $process = new Process($command, base_path());
        $process->run();
        if (! $process->isSuccessful()) {
            throw new ProcessFailedException($process);
        }

        return $process;
    }

    /**
     * Create the theme JSON file.
     *
     * @param  string $name
     * @param  string $parent
     * @return ThemeScaffold
     */
    private function createThemeJson($name, $parent = 'default')
    {
        $this->output->writeln('Creating the theme.json file...');
        $destination = $this->getPath($name, 'theme.json');
        $data = [
            "name" => $name,
            "parent" => $parent,
            "description" => "The $name theme is a child theme of $parent."
        ];
        file_put_contents($destination, json_encode($data, JSON_PRETTY_PRINT));

        return $this;
    }

    /**
     * Copy the files from the default theme.
     *
     * @param  string $name
     * @param  string $base
     * @return Process
     */
    private function copyDefaultFiles($name, $base = 'default')
    {
        $this->output->writeln("Copying files from the $base theme...");
        $folder = ($base === 'default') ? 'vendor/ignite/platform/themes' : null;
        $commands = [
            sprintf('cp -r %s %s', $this->getPath($base, 'src/js/*', $folder), $this->getPath($name, 'src/js')),
            sprintf('cp -r %s %s', $this->getPath($base, 'src/scss/style.scss', $folder), $this->getPath($name, 'src/scss')),
            sprintf('cp -r %s %s', $this->getPath($base, 'src/scss/_variables.scss', $folder), $this->getPath($name, 'src/scss')),
            sprintf('cp -r %s %s', $this->getPath($base, 'assets/fonts', $folder), $this->getPath($name, 'assets/fonts')),
            sprintf('cp -r %s %s', $this->getPath($base, 'assets/images', $folder), $this->getPath($name, 'assets/images')),
            sprintf('cp -r %s %s', $this->getPath($base, 'gulpfile.js', $folder), $this->getPath($name, 'gulpfile.js')),
            sprintf('cp -r %s %s', $this->getPath($base, 'package.json', $folder), $this->getPath($name, 'package.json')),
            sprintf('cp -r %s %s', $this->getPath($base, 'readme.md', $folder), $this->getPath($name, 'readme.md')),
            sprintf('cp -r %s %s', $this->getPath($base, 'views/cms', $folder), $this->getPath($name, 'views/cms')),
        ];

        $process = new Process(implode(' ; ', $commands), base_path());
        $process->run();
        if (! $process->isSuccessful()) {
            throw new ProcessFailedException($process);
        }

        return $process;
    }

    /**
     * Customize the theme files.
     *
     * @param  string $name
     * @param  string $base
     * @return Process
     */
    private function customizeTheme($name, $base = 'default')
    {
        $this->output->writeln("Customizing your theme...");
        $base = ucfirst(strtolower($base));
        $title = ucfirst(strtolower($name));
        $commands = [
            sprintf('sed -i "1s/%s/%s/" %s', $base, $title, $this->getPath($name, 'readme.md')),
            sprintf('sed -i "38s/\/\///" %s', $this->getPath($name, 'gulpfile.js'))
        ];

        $process = new Process(implode(' ; ', $commands), base_path());
        $process->run();
        if (! $process->isSuccessful()) {
            throw new ProcessFailedException($process);
        }

        return $process;
    }

    /**
     * Install npm packages.
     *
     * @param  string $name
     * @return Process
     */
    private function installNpm($name)
    {
        $this->output->writeln("Installing packages.json...");
        $process = new Process('npm install', $this->getPath($name));
        $process->setTimeout(3600 * 10);
        $process->run();
        if (! $process->isSuccessful()) {
            throw new ProcessFailedException($process);
        }

        return $process;
    }

    /**
     * Run gulp to compile assets.
     *
     * @param  string $name
     * @return Process
     */
    private function runGulp($name)
    {
        $this->output->writeln("Running gulp...");
        $process = new Process('gulp', $this->getPath($name));
        $process->setTimeout(60 * 60 * 5);
        $process->run();
        if (! $process->isSuccessful()) {
            throw new ProcessFailedException($process);
        }

        return $process;
    }

    /**
     * Update the .env file to set the theme.
     *
     * @param  string $name
     * @return Process
     */
    private function updateEnvFile($name)
    {
        $this->output->writeln("Updating env file...");
        $command = sprintf('sed -i "s/APP_THEME_ACTIVE=[a-zA-Z0-9_]*/APP_THEME_ACTIVE=%s/" .env', $name);
        $process = new Process($command, base_path());
        $process->run();
        if (! $process->isSuccessful()) {
            throw new ProcessFailedException($process);
        }

        return $process;
    }

    /**
     * Get the path to a directory in context of the current theme.
     *
     * @param  string $name
     * @param  string $path
     * @param  mixed  $folder
     * @return string
     */
    private function getPath($name, $path = null, $folder = null)
    {
        $folder = is_null($folder) ? $this->folder : $folder;
        return base_path(
            $folder . DIRECTORY_SEPARATOR .
            $name .
            (is_null($path) ? '' : DIRECTORY_SEPARATOR . $this->normalizePath($path))
        );
    }

    /**
     * Normalize the path for use across different OSs.
     *
     * @param  string $path
     * @return string
     */
    private function normalizePath($path)
    {
        return str_replace('/', DIRECTORY_SEPARATOR, $path);
    }
}
