<?php

namespace Bolt\Nut;

use Bolt\Collection\Bag;
use Bolt\Collection\MutableBag;
use Bolt\Filesystem\Adapter\Local;
use Bolt\Filesystem\Filesystem;
use Bolt\Filesystem\Handler\JsonFile;
use Bolt\Filesystem\Handler\YamlFile;
use Bolt\Storage\Migration;
use RuntimeException;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Finder\Finder;
use Webmozart\PathUtil\Path;

/**
 * Nut database importer command.
 *
 * @author Gawain Lynch <gawain.lynch@gmail.com>
 */
class DatabaseImport extends BaseCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this
            ->setName('database:import')
            ->setDescription('Import database records from a YAML or JSON file')
            ->addOption('file',      'f', InputOption::VALUE_OPTIONAL, 'A YAML or JSON file to use for import data. Must end with .yml, .yaml or .json')
            ->addOption('directory', 'd', InputOption::VALUE_OPTIONAL, 'A source directory. YAML or JSON files generated by the export command will be parsed in order')
            ->addOption('overwrite', null, InputOption::VALUE_NONE, 'Allow overwriting of existing records')
        ;
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        // Get file & directory paths
        list($fileNames, $dirPath) = $this->getResolvedPaths($input);
        $filesystem = new Filesystem(new Local($dirPath));

        $this->io->warning('This command may overwrite or replace data in the current database.');
        if (!$this->io->confirm('Are you sure you want to continue with the import')) {
            return 1;
        }

        // Get the Bolt Export migration object
        $contentTypes = Bag::from($this->app['config']->get('contenttypes'));
        $migration = new Migration\Import($this->app['storage'], $this->app['query'], $contentTypes);
        $responseBag = MutableBag::fromRecursive(['error' => [], 'warning' => [], 'success' => []]);

        $importData = [];
        $importUsers = MutableBag::of();
        foreach ($fileNames as $fileName) {
            /** @var JsonFile|YamlFile $file */
            $file = $filesystem->getFile($fileName);
            // Get file's import data
            $bag = MutableBag::fromRecursive($file->parse());

            // Fetch and remove meta information
            $meta = $bag->remove('__bolt_export_meta');
            $note = [
                'Processing:' . PHP_EOL .
                '- Export date: ' . $meta->get('date') . PHP_EOL .
                '- Bolt version: ' . $meta->get('version') . PHP_EOL .
                '- Origin platform: ' . $meta->get('platform') . PHP_EOL,
            ];
            $this->io->note($note);

            // Data format not guaranteed before 3.4.0 as this feature was experimental
            if (version_compare($meta->get('version'), '3.4.0', '<')) {
                throw new \RuntimeException('Provided data version not supported');
            }

            // Users if exists/supported
            $users = $bag->remove('__users', Bag::of());

            // Merge file data and unset
            $importData = array_merge_recursive($importData, $bag->toArray());
            $importUsers = $importUsers->merge($users);
            unset($bag);
        }

        // Perform the import
        $importData = Bag::fromRecursive($importData);
        $migration->run($importData, $responseBag, $input->getOption('overwrite'), $importUsers);

        if ($responseBag->get('error')->count() > 0) {
            $this->io->error($responseBag->get('error')->toArray());
        }
        if ($responseBag->get('warning')->count() > 0) {
            $this->io->warning($responseBag->get('warning')->toArray());
        }

        if ($responseBag->get('success')->count() > 0) {
            // Report finish
            $this->io->note('Records imported');
            $this->io->listing($responseBag->get('success')->toArray());
            $this->io->success('Complete');
        }

        return 0;
    }

    /**
     * @param InputInterface $input
     *
     * @throws RuntimeException
     *
     * @return array
     */
    private function getResolvedPaths(InputInterface $input)
    {
        $fileName = $input->getOption('file');
        $dirPath = $input->getOption('directory');

        if ($fileName === null && $dirPath === null) {
            throw new RuntimeException('Either the --file or --directory option is required.');
        }

        if ($dirPath !== null) {
            $fileNames = [];
            $finder = new Finder();
            $finder->files()
                ->in($dirPath)
                ->name('/\.(yml|yaml|json)/')
                ->depth('<2')
                ->sortByName()
            ;
            foreach ($finder as $source) {
                $fileNames[] = $source->getFilename();
            }
            if (Path::isRelative($dirPath)) {
                $dirPath = Path::makeAbsolute($dirPath, getcwd());
            }

            return [$fileNames, $dirPath];
        }

        if (Path::isRelative($fileName)) {
            $fileName = Path::makeAbsolute($fileName, getcwd());
        }
        $dirPath = $dirPath ?: dirname($fileName);
        if (Path::isRelative($dirPath)) {
            $dirPath = Path::makeAbsolute($dirPath, getcwd());
        }

        return [(array) basename($fileName), $dirPath];
    }
}
