<?php
// Copyright (c) Pickware GmbH. All rights reserved.
// This file is part of software that is released under a proprietary license.
// You must not copy, modify, distribute, make publicly available, or execute
// its contents or parts thereof without express permission by the copyright
// holder, unless otherwise permitted by law.

namespace Shopware\Plugins\ViisonCommon\Commands;

use Shopware\Commands\ShopwareCommand;
use Shopware\Models\Config\Element;
use Shopware\Models\Config\ElementTranslation;
use Shopware\Models\Config\Form;
use Shopware\Models\Plugin\Plugin;
use Shopware\Models\Shop\Locale;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;

class PluginConfigTranslationsExportIniCommand extends ShopwareCommand
{
    /**
     * {@inheritdoc}
     */
    protected function configure()
    {
        $this->setName(
            'viison:plugin:config:translations:export:ini'
        )->setDescription(
            'Export the translations of a plugin\'s config form to the form.ini file.'
        )->addArgument(
            'plugin',
            InputArgument::REQUIRED,
            'Technical name of the plugin to export the form.ini for.'
        )->addOption(
            'destinationPath',
            'd',
            InputOption::VALUE_OPTIONAL,
            'Destination for the form.ini, relative to the plugin dir.',
            'form.ini'
        )->addOption(
            'baseLocale',
            'b',
            InputOption::VALUE_OPTIONAL,
            'The locale of the strings in the form.',
            'de_DE'
        )->addOption(
            'localesToGenerate',
            'l',
            InputOption::VALUE_OPTIONAL | InputOption::VALUE_IS_ARRAY,
            'The locales to generate the snippets for.',
            [
                'en_GB',
                'de_DE',
            ]
        );
    }

    /**
     * {@inheritdoc}
     */
    protected function execute(InputInterface $input, OutputInterface $output)
    {
        $plugin = $this->getPlugin($input->getArgument('plugin'));
        /** @var Form $form */
        $form = $plugin->getConfigForms()->first();
        $baseLocale = $this->getLocale($input->getOption('baseLocale'));

        $iniFileContents = '';

        foreach ($input->getOption('localesToGenerate') as $localeToGenerate) {
            $locale = $this->getLocale(trim($localeToGenerate));
            $iniFileContents .= '[' . $locale->getLocale() . "]\n";

            $sortedFormElements = $form->getElements()->toArray();
            usort($sortedFormElements, function (Element $e1, Element $e2) {
                return strcasecmp($e1->getName(), $e2->getName());
            });

            /** @var Element $element */
            foreach ($sortedFormElements as $element) {
                /** @var ElementTranslation $elementTranslationOfLocale */
                $elementTranslationOfLocale = array_shift(array_filter($element->getTranslations()->toArray(), function (ElementTranslation $translation) use ($locale) {
                    return $translation->getLocale()->getLocale() === $locale->getLocale();
                }));

                if ($elementTranslationOfLocale) {
                    $label = $elementTranslationOfLocale->getLabel();
                    $description = $elementTranslationOfLocale->getDescription();
                } else {
                    $label = $element->getLabel();
                    $description = $element->getDescription();
                    if ($locale->getLocale() !== $baseLocale->getLocale()) {
                        $label = '<untranslated>' . $label;
                        $description = '<untranslated>' . $description;
                    }
                }

                $iniFileContents .= sprintf(
                    "%s/label = \"%s\"\n",
                    $element->getName(),
                    $this->escapeIniValue($label)
                );
                $iniFileContents .= sprintf(
                    "%s/description = \"%s\"\n",
                    $element->getName(),
                    $this->escapeIniValue($description)
                );

                $options = $element->getOptions();
                if (!is_array($options['store'])) {
                    continue;
                }

                foreach ($options['store'] as $storeOptions) {
                    if (is_array($storeOptions[1])) {
                        $storeOptionTranslation = array_shift(array_filter($storeOptions[1], function ($optionLocale) use ($locale) {
                            return $locale->getLocale() === $optionLocale;
                        }, ARRAY_FILTER_USE_KEY));
                    } else {
                        $storeOptionTranslation = null;
                    }

                    if (!$storeOptionTranslation) {
                        $storeOptionTranslation = $storeOptions[1];
                        if ($locale->getLocale() !== $baseLocale->getLocale()) {
                            $storeOptionTranslation = '<untranslated>' . $storeOptionTranslation;
                        }
                    }
                    $iniFileContents .= sprintf(
                        "%s/store/%s = \"%s\"\n",
                        $element->getName(),
                        $storeOptions[0],
                        $this->escapeIniValue($storeOptionTranslation)
                    );
                }
            }

            $iniFileContents .= "\n";
        }

        $path = $input->getOption('destinationPath');

        if ($path[0] !== '/') {
            $path = (
                rtrim(Shopware()->Container()->getParameter('kernel.root_dir'), '/')
                . '/engine/Shopware/Plugins/' . $plugin->getSource() . '/' . $plugin->getNamespace() . '/'
                . $plugin->getName() . '/' . $path
            );
        }

        file_put_contents($path, $iniFileContents);

        $output->writeln(sprintf('File "%s" has been generated.', $path));
    }

    /**
     * Loads the model entity of the plugin, which is specified by its $pluginName and checks whether the plugin is
     * installed and active.
     *
     * @param string $pluginName
     * @return Plugin
     * @throws \Exception if the plugin does not exist or is not active
     */
    private function getPlugin($pluginName)
    {
        $entityManager = $this->container->get('models');

        /** @var Plugin $plugin */
        $plugin = $entityManager->getRepository('Shopware\\Models\\Plugin\\Plugin')->findOneBy([
            'name' => $pluginName,
        ]);

        if (!$plugin) {
            throw new \Exception('The specified plugin does not exist');
        } elseif (!$plugin->getInstalled()) {
            throw new \Exception('The specified plugin is not installed');
        }

        return $plugin;
    }

    /**
     * @param $localeName
     * @return Locale
     * @throws \Exception
     */
    private function getLocale($localeName)
    {
        $entityManager = $this->container->get('models');

        /** @var Locale $locale */
        $locale = $entityManager->getRepository('Shopware\\Models\\Shop\\Locale')->findOneBy([
            'locale' => $localeName,
        ]);

        if (!$locale) {
            throw new \Exception(sprintf('Locale "%s" not found.', $localeName));
        }

        return $locale;
    }

    /**
     * @param string $value
     * @return string $value, with double quotes and backslashes escaped by '\'
     */
    private function escapeIniValue($value)
    {
        return mb_ereg_replace('(["\\\\])', '\\\\1', $value);
    }
}
