<?php
/**
 * Copyright 2020 Adobe
 * All Rights Reserved.
 *
 * NOTICE: Adobe permits you to use, modify, and distribute this file in
 * accordance with the terms of the Adobe license agreement accompanying
 * it.
 */
declare(strict_types=1);

namespace Magento\Mray\Console\Command;

use InvalidArgumentException;
use Magento\Mray\Api\GetProjectVersion;
use Magento\Mray\Index\Release\ParallelIndex;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Style\SymfonyStyle;
use function clearstatcache;
use function file_exists;
use function is_dir;
use function mkdir;
use function realpath;

class IndexMagentoReleases extends Command
{
    /**
     * @var string
     */
    protected static $defaultName = 'indexer:index-magento-releases';

    /**
     * @inheritDoc
     */
    protected function configure()
    {
        $this->addArgument(
            'dir',
            InputArgument::REQUIRED,
            'Location of magento releases'
        );
        $this->addArgument(
            'out',
            InputArgument::REQUIRED,
            'Location where to put index.'
        );
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        return (new ParallelIndex(new SymfonyStyle($input, $output)))->execute(
            $this->getProjectPaths($this->getSourcesDir($input), $output),
            $this->getIndexDir($input)
        );
    }

    /**
     * @param string $absolutePath
     * @param OutputInterface $output
     * @return string[]
     */
    private function getProjectPaths(string $absolutePath, OutputInterface $output): array
    {
        $getProjectVersion = new GetProjectVersion();
        $absolutePath = rtrim($absolutePath, '/');
        $version = false;
        try {
            $version = $getProjectVersion->execute($absolutePath);
            // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock.DetectedCatch
        } catch (\Exception $exception) {
            // not a magento project
        }

        if ($version) {
            $output->writeln(sprintf('Indexing a single project version "%s" in "%s"', $version, $absolutePath));
            return [$absolutePath];
        }

        $paths = [];
        foreach (scandir($absolutePath) as $subDirectory) {
            if (strpos($subDirectory, '.') === 0) {
                continue;
            }
            try {
                $version = $getProjectVersion->execute($absolutePath . '/' . $subDirectory);
                // phpcs:ignore Magento2.CodeAnalysis.EmptyBlock.DetectedCatch
            } catch (\Exception $exception) {
                // not a magento project
            }

            if ($version) {
                $output->writeln(
                    sprintf('Identified project version "%s" in "%s"', $version, $absolutePath . '/' . $subDirectory)
                );
                $paths[] = $absolutePath . '/' . $subDirectory;
            }
        }
        if (empty($paths)) {
            throw new InvalidArgumentException(
                sprintf(
                    'Cannot find projects to index in %s. '
                    . 'Please provide a path to a project or to a folder containing projects as the first argument.',
                    $absolutePath
                )
            );
        }
        return $paths;
    }

    /**
     * @param InputInterface $input
     * @return string
     */
    private function getSourcesDir(InputInterface $input): string
    {
        $dir = $input->getArgument('dir');
        if (!is_dir($dir)) {
            throw new InvalidArgumentException('Specified path does not exists, not a directory or not readable.');
        }
        return realpath($dir);
    }

    /**
     * @param InputInterface $input
     * @return string
     */
    private function getIndexDir(InputInterface $input): string
    {
        $targetDirectory = $input->getArgument('out');
        if (file_exists($targetDirectory) && !is_dir($targetDirectory)) {
            throw new InvalidArgumentException('Output path exists and is not a directory.');
        }

        if (!file_exists($targetDirectory)) {
            mkdir($targetDirectory, 0755, true);
            clearstatcache(true, $targetDirectory);
        } elseif (!is_dir($targetDirectory)) {
            throw new InvalidArgumentException('Output path contains invalid structure.');
        }

        return realpath($targetDirectory);
    }
}
