<?php

declare(strict_types=1);

namespace KupShop\I18nBundle\Resources\script;

use KupShop\AdminBundle\Util\Script\Script;
use KupShop\I18nBundle\Translations\BlocksTranslation;
use KupShop\I18nBundle\Translations\ITranslation;
use KupShop\I18nBundle\Util\TranslationEngine;
use KupShop\I18nBundle\Util\TranslationLocator;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\Contexts;
use Query\Operator;
use Query\QueryBuilder;
use Query\Translation;

class AutoTranslateScript extends Script
{
    protected static $name = 'Auto-translate translations using translator';
    protected static $defaultParameters = [
        // realRun: true = translate and save translations into DB
        'realRun' => false,
        'run_auto_translates' => false,
        // translate from default language to specific languages
        'languages' => ['sk'],
        'po_files' => false,
    ];

    private static array $translateDefinition = [];

    public static function getDefaultParameters(): array
    {
        $translationLocator = ServiceContainer::getService(TranslationLocator::class);
        $translations = $translationLocator->getTranslations();

        $defaultParameters = self::$defaultParameters;
        foreach ($translations as $translationClass) {
            /** @var ITranslation $translation */
            $translation = ServiceContainer::getService($translationClass);
            $defaultParameters['autoTranslateClasses'][$translationClass] = array_keys($translation->getColumns());
        }
        self::$translateDefinition = $defaultParameters['autoTranslateClasses'] ?? [];

        return $defaultParameters;
    }

    protected function run(array $arguments)
    {
        $languages = $arguments['languages'];
        if (empty($languages)) {
            $this->log('Argument "languages" cannot be empty!');

            return;
        }

        $missingTranslations = [];
        if (!empty($arguments['run_auto_translates'])) {
            foreach ($arguments['autoTranslateClasses'] ?? [] as $className => $class) {
                $missingTranslations[$className] = $this->getMissingTranslations($className, $languages);
            }
        }

        $missingPoTranslations = [];
        if (!empty($arguments['po_files'])) {
            foreach ($languages as $language) {
                $missingPoTranslations[$language] = $this->getMissingPoTranslations($language);
            }
        }

        if (!($arguments['realRun'] ?? false)) {
            foreach ($missingTranslations as $class => $translations) {
                foreach ($translations as $lang => $items) {
                    $this->log("[{$lang}] {$class}: ".count($items));
                }
            }

            $this->log('Statické texty: '.count($missingPoTranslations));

            return;
        }
        ini_set('max_execution_time', 60 * 60 * 2); // 2 hours

        $translator = ServiceContainer::getService(TranslationEngine::class);
        $languageContext = Contexts::get(LanguageContext::class);

        foreach ($missingTranslations as $class => $translations) {
            $this->log("Translating {$class}...");

            /** @var ITranslation $translation */
            $translation = ServiceContainer::getService($class);

            foreach ($translations as $lang => $items) {
                $totalItems = count($items);
                $processedItems = 0;
                foreach ($items as $id => $item) {
                    $translatedValues = [];
                    foreach ($item as $column => $text) {
                        $translated = null;
                        if ($column == 'json_content' && $translation instanceof BlocksTranslation) {
                            $blocks = json_decode($text, true);
                            $text_blocks = [];
                            BlocksTranslation::getTextBlocks($blocks, $text_blocks);
                            foreach ($text_blocks as $key => $blockText) {
                                if (empty($blockText['settings']['html'])) {
                                    continue;
                                }
                                try {
                                    $translated = $translator->getTranslation(
                                        $blockText['settings']['html'],
                                        $languageContext->getDefaultId(),
                                        $lang,
                                        (bool) ($translation->getColumns()[$column]['richtext'] ?? false)
                                    )['translatedText'] ?? null;
                                    $blockText['settings']['html'] = $translated;
                                    $text_blocks[$key] = $blockText;
                                } catch (\Throwable $e) {
                                }
                            }
                            BlocksTranslation::replaceRecursive($blocks, $text_blocks);
                            $translated = $blocks;
                        } else {
                            if (isLocalDevelopment()) {
                                $this->log('Překlad není povolen pro localDevelopment');
                                continue;
                            }
                            try {
                                $translated = $translator->getTranslation(
                                    $text,
                                    $languageContext->getDefaultId(),
                                    $lang,
                                    (bool) ($translation->getColumns()[$column]['richtext'] ?? false)
                                );
                                if (!empty($translated['error'])) {
                                    $this->log($translated['error']);
                                    continue;
                                }
                                $translated = $translated['translatedText'] ?? null;
                            } catch (\Throwable $e) {
                            }
                        }

                        // pokud mam preklad
                        if (!empty($translated)) {
                            $translatedValues[$column] = $translated;
                        }

                        if (!empty($translatedValues)) {
                            if ($column == 'json_content' && $translation instanceof BlocksTranslation) {
                                $translation->saveSingleObjectForce($lang, $id, $translatedValues, false);
                            } else {
                                $translation->saveSingleObject($lang, $id, $translatedValues);
                            }
                        }
                    }

                    $processedItems++;
                    if ($processedItems % 10 === 0) {
                        $this->log("{$processedItems}/{$totalItems}...");
                    }
                }
            }
        }

        foreach ($missingPoTranslations as $language => $translations) {
            $totalItems = count($translations);
            $processedItems = 0;
            foreach ($translations as $translation) {
                try {
                    $translated = $translator->getTranslation(
                        $translation['original'],
                        $languageContext->getDefaultId(),
                        $language,
                        false
                    );
                    if (!empty($translated['error'])) {
                        $this->log($translated['error']);
                        continue;
                    }
                    $translated = $translated['translatedText'] ?? null;
                } catch (\Throwable $e) {
                    $this->log($e->getMessage());
                }

                if (!empty($translated)) {
                    sqlQueryBuilder()->update('translations_frontend')->directValues([
                        'translation' => $translated,
                    ])->where(Operator::equals(['id' => $translation['id']]))->execute();
                }
                $processedItems++;
                if ($processedItems % 10 === 0) {
                    $this->log("{$processedItems}/{$totalItems}...");
                }
            }
        }

        $this->log('Translations done');
    }

    private function getMissingTranslations(string $translationClass, array $languages): array
    {
        /** @var ITranslation $translation */
        $translation = ServiceContainer::getService($translationClass);

        if (!($columns = (static::$translateDefinition[$translationClass] ?? false))) {
            $this->log('Žádné sloupce pro tabulku: '.$translationClass);

            return [];
        }
        $missingTranslations = [];
        $SQL = sqlQuery("SHOW TABLES LIKE '".$translation->getTableName()."'");
        if (sqlNumRows($SQL) > 0) {
            $qb = sqlQueryBuilder()
                ->select("{$translation->getTableAlias()}.id")
                ->from($translation->getTableName(), $translation->getTableAlias());

            foreach ($columns as $column) {
                $qb->addSelect("{$translation->getTableAlias()}.{$column}");
            }

            $qb->andWhere(
                Translation::joinTranslations(
                    $languages,
                    $translation,
                    function (QueryBuilder $qb, $columnName, $translatedColumn, $langID) {
                        $qb->addSelect("{$translatedColumn} as translated_{$columnName}_{$langID}");

                        return false;
                    },
                    $columns
                )
            );

            foreach ($qb->execute() as $item) {
                foreach ($columns as $column) {
                    if (empty($item[$column] ?? '')) {
                        continue;
                    }
                    $originalText = trim($item[$column]);
                    // preskocim polozky, ktere nemaji text v default jazyce
                    if (empty($originalText)) {
                        continue;
                    }

                    foreach ($languages as $language) {
                        $translatedColumn = "translated_{$column}_{$language}";

                        if (empty($item[$translatedColumn])) {
                            $missingTranslations[$language][$item['id']][$column] = $originalText;
                        }
                    }
                }
            }
        }

        return $missingTranslations;
    }

    protected function getMissingPoTranslations($language)
    {
        return sqlQueryBuilder()->select('id, original, id_language')->from('translations_frontend')
            ->where(Operator::equals(['id_language' => $language]))
            ->andWhere(Operator::orX(
                Operator::isNull('translation'),
                Operator::equals(['translation' => ''])
            ))
            ->execute()->fetchAllAssociative();
    }
}

return AutoTranslateScript::class;
