<?php

namespace Ignite\Core\Listeners;

use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Exception\ProcessFailedException;
use Symfony\Component\Process\Process;

class ScaffoldTheme
{
    /**
     * The folder under which themes are located.
     *
     * @var string
     */
    protected $folder = 'themes';

    /**
     * The code to execute when a theme has been scaffolded.
     *
     * @param InputInterface $input
     * @param OutputInterface $output
     * @param string $path
     */
    public function handle(InputInterface $input, OutputInterface $output, string $path)
    {
        $base = 'default';
        $name = $input->getArgument('name');

        $output->writeln("Copying files from the $base theme...");
        $this->copyDefaultFiles($name, $base);

        $output->writeln("Customizing your theme...");
        $this->customizeTheme($name, $base);
        $this->updatePublicPath($name, $path);

        $output->writeln("Updating .env files...");
        $this->updateEnvFile($name, '.env');
        $this->updateEnvFile($name, '.env.example');
    }

    /**
     * Copy the files from the default theme.
     *
     * @param  string $name
     * @param  string $base
     */
    protected function copyDefaultFiles(string $name, string $base = 'default')
    {
        $folder = ($base === 'default') ? 'vendor/ignite/themes' : null;
        $cwd = base_path();
        $commands = [
            Process::fromShellCommandline(sprintf('mkdir -p %s', $this->getPath($name, 'src/js/')), $cwd),
            Process::fromShellCommandline(sprintf('cp -r %s %s', $this->getPath($base, 'src/js/*', $folder), $this->getPath($name, 'src/js/')), $cwd),
            Process::fromShellCommandline(sprintf('mkdir -p %s', $this->getPath($name, 'src/scss/')), $cwd),
            Process::fromShellCommandline(sprintf('cp %s %s', $this->getPath($base, 'src/scss/style.scss', $folder), $this->getPath($name, 'src/scss/')), $cwd),
            Process::fromShellCommandline(sprintf('cp %s %s', $this->getPath($base, 'src/scss/_variables.scss', $folder), $this->getPath($name, 'src/scss/')), $cwd),
            Process::fromShellCommandline(sprintf('cp -r %s %s', $this->getPath($base, 'dist/fonts', $folder), $this->getPath($name, 'dist/fonts')), $cwd),
            Process::fromShellCommandline(sprintf('cp -r %s %s', $this->getPath($base, 'dist/images', $folder), $this->getPath($name, 'dist/images')), $cwd),
            Process::fromShellCommandline(sprintf('mkdir -p %s', $this->getPath($name, 'dist/stylesheets')), $cwd),
            Process::fromShellCommandline(sprintf('mkdir -p %s', $this->getPath($name, 'dist/javascripts')), $cwd),
            Process::fromShellCommandline(sprintf('cp -r %s %s', $this->getPath($base, '.babelrc', $folder), $this->getPath($name, '.babelrc')), $cwd),
            Process::fromShellCommandline(sprintf('cp -r %s %s', $this->getPath($base, '.gitignore', $folder), $this->getPath($name, '.gitignore')), $cwd),
            Process::fromShellCommandline(sprintf('cp -r %s %s', $this->getPath($base, 'webpack.mix.js', $folder), $this->getPath($name, 'webpack.mix.js')), $cwd),
            Process::fromShellCommandline(sprintf('cp -r %s %s', $this->getPath($base, 'gulpfile.js', $folder), $this->getPath($name, 'gulpfile.js')), $cwd),
            Process::fromShellCommandline(sprintf('cp -r %s %s', $this->getPath($base, 'package.json', $folder), $this->getPath($name, 'package.json')), $cwd),
            Process::fromShellCommandline(sprintf('cp -r %s %s', $this->getPath($base, 'yarn.lock', $folder), $this->getPath($name, 'yarn.lock')), $cwd),
            Process::fromShellCommandline(sprintf('cp -r %s %s', $this->getPath($base, 'readme.md', $folder), $this->getPath($name, 'readme.md')), $cwd),
            Process::fromShellCommandline(sprintf('cp -r %s %s', $this->getPath($base, 'views/cms', $folder), $this->getPath($name, 'views/cms')), $cwd),
            Process::fromShellCommandline(sprintf('cp -r %s %s', $this->getPath($base, 'forms', $folder), $this->getPath($name, 'forms')), $cwd),
            Process::fromShellCommandline(sprintf('cp -r %s %s', $this->getPath($base, 'dist/javascripts/conditionals.js', $folder), $this->getPath($name, 'dist/javascripts/conditionals.js')), $cwd),
            Process::fromShellCommandline(sprintf('cp -r %s %s', $this->getPath($base, 'dashboards', $folder), $this->getPath($name, 'dashboards')), $cwd),
        ];

        foreach ($commands as $command) {
            $command->run();

            if (! $command->isSuccessful()) {
                throw new ProcessFailedException($command);
            }
        }
    }

    /**
     * Customize the theme files.
     *
     * @param  string $name
     * @param  string $base
     */
    protected function customizeTheme(string $name, string $base = 'default')
    {
        $base = ucfirst(strtolower($base));
        $title = ucfirst(strtolower($name));
        $cwd = base_path();

        $commands = [
            Process::fromShellCommandline(sprintf('sed -i "1s/%s/%s/" %s', $base, $title, $this->getPath($name, 'readme.md')), $cwd),
            Process::fromShellCommandline(sprintf('sed -i "39s/\/\///" %s', $this->getPath($name, 'gulpfile.js')), $cwd),
        ];

        foreach ($commands as $command) {
            $command->run();
            if (! $command->isSuccessful()) {
                throw new ProcessFailedException($command);
            }
        }
    }

    /**
     * Update the .env file to set the theme.
     *
     * @param  string $name
     */
    protected function updateEnvFile(string $name, string $file = '.env')
    {
        $process = Process::fromShellCommandline(
            sprintf('sed -i "s/APP_THEME_ACTIVE=[a-zA-Z0-9_]*/APP_THEME_ACTIVE=%s/" %s', $name, $file),
            base_path()
        );

        $process->run();

        if (! $process->isSuccessful()) {
            throw new ProcessFailedException($process);
        }
    }

    /**
     * Updates the public path in the webpack.mix.js file.
     *
     * @param  string $path
     * @return self
     */
    protected function updatePublicPath(string $name, string $path)
    {
        $webpackMixPath = $path . DIRECTORY_SEPARATOR . 'webpack.mix.js';
        $webpackMixContents = file_get_contents($webpackMixPath);

        $webpackMixContents = str_replace(
            'const realPublicPath = \'\';',
            'const realPublicPath = \'../../public/themes/' . $name . '/\';',
            $webpackMixContents
        );
        file_put_contents($webpackMixPath, $webpackMixContents);

        // also make the public theme directory
        $themeFolder = public_path('themes/' . $name);
        if (!is_dir($themeFolder)) {
            mkdir($themeFolder, 0755, true);
        }

        return $this;
    }

    /**
     * Get the path to a directory in context of the current theme.
     *
     * @param  string $name
     * @param  string $path
     * @param  mixed  $folder
     * @return string
     */
    protected function getPath(string $name, ?string $path = null, ?string $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
     */
    protected function normalizePath(string $path)
    {
        return str_replace('/', DIRECTORY_SEPARATOR, $path);
    }
}
