<?php

namespace Query;

use KupShop\I18nBundle\Translations\ITranslation;
use KupShop\KupShopBundle\Context\LanguageContext;
use KupShop\KupShopBundle\Util\ArrayUtil;
use KupShop\KupShopBundle\Util\Compat\ServiceContainer;
use KupShop\KupShopBundle\Util\Contexts;

class Translation
{
    public const COALESCE_STYLE_INHERITANCE = 'inheritance';
    public const COALESCE_STYLE_NONE = 'none';

    /**
     * @return \Closure
     */
    public static function adminAlwaysVisible($spec)
    {
        if (!getAdminUser()) {
            return $spec;
        }

        return function (QueryBuilder $qb) use ($spec) {
            $dbcfg = \Settings::getDefault();

            $tmp = $dbcfg->hide_not_translated_objects ?? null;
            $dbcfg->hide_not_translated_objects = 'N';

            $qb->andWhere($spec);

            $dbcfg->hide_not_translated_objects = $tmp;
        };
    }

    /**
     * @param array|callable|null $columns list of column names ['name', 'title', ...]
     */
    public static function withTranslatedFields(
        array $languages,
        ITranslation $translation,
        $columns = null,
        bool $frontend = false,
        string $coalesceStyle = self::COALESCE_STYLE_NONE,
    ): callable {
        // TODO: Hack
        $first = false;

        return self::joinTranslations($languages, $translation, function (QueryBuilder $qb, $columnName, $translatedColumn, $langID) use (&$first, $translation, $frontend, $coalesceStyle) {
            $translationAlias = $translation->getTableAlias().'_'.$langID;
            if (!$frontend && $coalesceStyle == self::COALESCE_STYLE_NONE) {
                $qb->addSelect("{$translationAlias}.{$columnName} AS {$langID}_{$columnName}");

                // TODO: Rozdelit na specku pouze pro admin
                if ($first == false) {
                    $qb->leftJoin($translationAlias, 'admins', "{$translationAlias}_a", "{$translationAlias}_a.id = {$translationAlias}.id_admin");
                    $qb->addSelect("{$translationAlias}.created AS {$langID}_created")
                        ->addSelect("{$translationAlias}.updated AS {$langID}_updated")
                        ->addSelect("{$translationAlias}_a.id AS {$langID}_admin_id")
                        ->addSelect("{$translationAlias}_a.email AS {$langID}_admin_email");
                    $first = true;
                }

                return false;
            } elseif ($coalesceStyle == self::COALESCE_STYLE_INHERITANCE) {
                $qb->addSelect("{$translatedColumn} AS {$langID}_{$columnName}");

                return false;
            }
        }, $columns);
    }

    /**
     * @param array|callable|null $columns list of column names ['name', 'title', ...]
     */
    public static function joinTranslations(
        array $languages,
        ITranslation $translation,
        callable $fieldsCallback,
        $columns = null,
    ): callable {
        return function (QueryBuilder $qb) use ($fieldsCallback, $languages, $translation, $columns) {
            if (!findModule(\Modules::TRANSLATIONS)) {
                return '1';
            }

            if (!$languages) {
                return '1';
            }

            $tableAlias = $translation->getTableAlias();
            $foreignKeyColumn = $translation->getForeignKeyColumn();
            $keyColumn = $translation->getKeyColumn();

            if (is_callable($columns)) {
                $columnsToSelect = array_keys($translation->getColumns());
                $columns = $columns(array_combine($columnsToSelect, $columnsToSelect));
            }

            if (!is_array($columns)) {
                $columns = array_keys($translation->getColumns());
            }

            if (!ArrayUtil::isDictionary($columns)) {
                // convert to assoc
                $columns = array_combine($columns, $columns);
            }

            $inheritedLanguages = static::getInheritedLanguages($languages);

            // Pokud je getJoinType join a máme více jazyků, tak použijeme leftJoin a následně použijeme inNotNul v proměnné $whereOr.
            // Upraveno kvůli skrývání nepřeložených objektů
            $useLeftJoin = count($inheritedLanguages) > 1 && $translation->getJoinType() == 'join';
            $join = $useLeftJoin ? 'leftJoin' : $translation->getJoinType();
            $whereOr = [];

            // join translation tables
            foreach ($inheritedLanguages as $langID) {
                $translationAlias = $translation->getTableAlias().'_'.$langID;
                $qb->setParameter("translation_language_{$langID}", $langID);
                $qb->{$join}(
                    $tableAlias,
                    $translation->getTableName().'_translations',
                    $translationAlias,
                    "{$translationAlias}.{$foreignKeyColumn} = {$tableAlias}.{$keyColumn} AND {$translationAlias}.id_language = :translation_language_{$langID}"
                );
                $translation->customizeJoinQueryBuilder($qb, $langID);
                if ($useLeftJoin) {
                    $whereOr[] = Operator::isNotNull("{$translationAlias}.{$foreignKeyColumn}");
                }
            }
            if (!empty($whereOr)) {
                $qb->andWhere(Operator::orX($whereOr));
            }
            // add columns
            foreach ($languages as $langID) {
                foreach ($columns as $columnName => $alias) {
                    $coalesce = [];
                    // add translation fields to $coalesce
                    foreach (static::getInheritedLanguages($langID) as $inheritedLanguageId) {
                        $translationAlias = $translation->getTableAlias().'_'.$inheritedLanguageId;
                        $coalesce[] = "{$translationAlias}.{$columnName}";
                    }

                    $coalesceField = Operator::coalesce(...$coalesce);
                    // add select with coalesce to default field
                    if ($fieldsCallback($qb, $columnName, $coalesceField, $langID) !== false) {
                        $qb->addSelect("COALESCE({$coalesceField}, {$tableAlias}.{$columnName}) as `{$alias}`");
                    }
                }
            }

            return '1';
        };
    }

    /**
     * @param array|callable|null $columns list of column names ['name', 'title', ...]
     */
    public static function coalesceTranslatedFields(
        string $translationClass,
        $columns = null,
        ?string $languageID = null,
    ) {
        if (!findModule(\Modules::TRANSLATIONS)) {
            if ($columns) {
                // Emulate selected columns
                $class = new $translationClass();

                if (is_callable($columns)) {
                    $columnsToSelect = array_keys($class->getColumns());
                    $columns = $columns(array_combine($columnsToSelect, $columnsToSelect));
                }

                if (!ArrayUtil::isDictionary($columns)) {
                    // convert to assoc
                    $columns = array_combine($columns, $columns);
                }

                return function (QueryBuilder $qb) use ($columns, $class) {
                    foreach ($columns as $columnName => $translatedField) {
                        $qb->addSelect("{$class->getTableAlias()}.{$columnName} AS `{$translatedField}`");
                    }

                    return '1';
                };
            }

            return '1';
        }

        /** @var $translation ITranslation */
        $translation = ServiceContainer::getService($translationClass);
        if (!isset($languageID)) {
            $language = ServiceContainer::getService(LanguageContext::class)->getActive();
            $languageID = $language->getId();
        }

        return self::withTranslatedFields([$languageID], $translation, $columns, true);
    }

    public static function joinTranslatedFields(
        string $translationClass,
        callable $fieldsCallback,
        array $columns,
    ) {
        if (!findModule(\Modules::TRANSLATIONS)) {
            if (!ArrayUtil::isDictionary($columns)) {
                // convert to assoc
                $columns = array_combine($columns, $columns);
            }

            return function (QueryBuilder $qb) use ($columns, $fieldsCallback, $translationClass) {
                $class = new $translationClass();
                foreach ($columns as $columnName => $translatedField) {
                    if ($fieldsCallback($qb, $columnName, null, null) !== false) {
                        $qb->addSelect("{$class->getTableAlias()}.{$columnName} AS `{$translatedField}`");
                    }
                }
            };
        }

        $translation = ServiceContainer::getService($translationClass);
        $language = ServiceContainer::getService(LanguageContext::class)->getActive();

        return self::joinTranslations([$language->getId()], $translation, $fieldsCallback, $columns);
    }

    public static function getInheritedLanguages(array|string $languages): array
    {
        $languageContext = Contexts::get(LanguageContext::class);

        if (is_string($languages)) {
            $languages = [$languages];
        }

        $inheritedLanguages = [];
        foreach ($languages as $language) {
            $inheritedLanguages = array_unique(array_merge($inheritedLanguages, $languageContext->getInheritance($language)));
        }

        return $inheritedLanguages;
    }
}
