<?php

namespace Mautic\CoreBundle\Model;

use Mautic\CoreBundle\Entity\TranslationEntityInterface;
use Mautic\LeadBundle\Entity\Lead;
use Symfony\Component\HttpFoundation\Request;

/**
 * Provides helper methods for determine the requested language from contact's profile and/or request.
 */
trait TranslationModelTrait
{
    /**
     * Get the entity based on requested translation.
     *
     * @param Lead|array{preferred_locale?: string} $lead
     *
     * @return array{TranslationEntityInterface, TranslationEntityInterface}
     */
    public function getTranslatedEntity(TranslationEntityInterface $entity, Lead|array|null $lead = null, ?Request $request = null): array
    {
        [$translationParent, $translationChildren] = $entity->getTranslations();

        $leadPreference = $chosenLanguage = null;

        if (count($translationChildren)) {
            if ($translationParent) {
                $translationChildren = $translationParent->getTranslationChildren();
            } else {
                $translationParent = $entity;
            }

            // Generate a list of translations
            $translations = [$translationParent->getId() => $translationParent->getLanguage()];
            foreach ($translationChildren as $c) {
                $translations[$c->getId()] = $c->getLanguage();
            }

            // Generate a list of translations for this entity
            $translationList = [];
            foreach ($translations as $id => $language) {
                $core = $this->getTranslationLocaleCore($language);
                if (!isset($translationList[$core])) {
                    $translationList[$core] = [];
                }
                $translationList[$core][$language] = $id;
            }

            // Get the contact's preferred language if defined
            $languageList   = [];
            $leadPreference = null;
            if ($lead) {
                if ($lead instanceof Lead) {
                    $leadPreference                = $lead->getPreferredLocale();
                    $languageList[$leadPreference] = $leadPreference;
                } elseif (is_array($lead) && isset($lead['preferred_locale'])) {
                    $leadPreference                = $lead['preferred_locale'];
                    $languageList[$leadPreference] = $leadPreference;
                }
            }

            // Check request for language
            if (null !== $request) {
                $browserLanguages = $request->server->get('HTTP_ACCEPT_LANGUAGE');
                if (!empty($browserLanguages)) {
                    $browserLanguages = explode(',', $browserLanguages);
                    foreach ($browserLanguages as $language) {
                        if (($pos = strpos($language, ';q=')) !== false) {
                            // remove weights
                            $language = substr($language, 0, $pos + 1);
                        }
                        // change - to _
                        $language = str_replace('-', '_', $language);

                        if (!isset($languageList[$language])) {
                            $languageList[$language] = $language;
                        }
                    }
                }
            }

            $matchFound    = false;
            $preferredCore = false;
            foreach ($languageList as $language) {
                $core = $this->getTranslationLocaleCore($language);
                if (isset($translationList[$core])) {
                    // Does the dialect exist?
                    if (isset($translationList[$core][$language])) {
                        // There's a match
                        $matchFound     = $translationList[$core][$language];
                        $chosenLanguage = $language;
                        break;
                    } elseif (!$preferredCore) {
                        // This will be the fallback if no matches are found
                        $preferredCore = $core;
                    }
                }
            }

            if ($matchFound) {
                // A translation was found based on language preference
                $entity = ($matchFound == $translationParent->getId()) ? $translationParent : $translationChildren[$matchFound];
            } elseif ($preferredCore) {
                // Return the best matching language
                $bestMatch      = array_values($translationList[$preferredCore])[0];
                $entity         = ($bestMatch == $translationParent->getId()) ? $translationParent : $translationChildren[$bestMatch];
                $chosenLanguage = $preferredCore;
            }
        }

        // Save the preferred language to the lead's profile
        if (!$leadPreference && !empty($chosenLanguage) && $lead instanceof Lead) {
            $lead->addUpdatedField('preferred_locale', $chosenLanguage);
        }

        // Return the translation parent and translated entity
        return [$translationParent, $entity];
    }

    /**
     * Run post saving a translation aware entity.
     */
    public function postTranslationEntitySave(TranslationEntityInterface $entity): void
    {
        // If parent, add this entity as a child of the parent so that it populates the list in the tab (due to Doctrine hanging on to entities in memory)
        if ($translationParent = $entity->getTranslationParent()) {
            $translationParent->addTranslationChild($entity);
        }
    }

    protected function getTranslationLocaleCore(string $locale): string
    {
        if (str_contains($locale, '_')) {
            $locale = substr($locale, 0, 2);
        }

        return $locale;
    }
}
