# -*- coding: utf-8 -*-
"""unit test i18n support"""

from unittest import skipIf

from django.conf import settings
from django.contrib.auth.models import User, Permission
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ImproperlyConfigured
from django.urls import reverse
from django.utils.translation import activate, get_language
from django.test.utils import override_settings

from ..models import BaseArticle, InvalidArticleError
from ..settings import has_localized_urls, is_localized, is_multilang, multilang_mode, get_article_class
from ..utils import redirect_to_language, strip_locale_path, get_url_in_language, make_locale_path
from . import BaseTestCase, BeautifulSoup


def language_fallbacks():
    """return fallback"""
    return tuple([lang_code for (lang_code, lang_name) in settings.LANGUAGES])


@skipIf(not has_localized_urls(), "Urls are not localized")
class LocalePathTest(BaseTestCase):
    """test that url is parsed correctly when using locale prefix"""

    def test_get_locale_article(self):
        """it should return lang and locale-independent-path"""
        result = strip_locale_path('/en/home/')
        self.assertEqual(len(result), 2)
        self.assertEqual(result[0], 'en')
        self.assertEqual(result[1], '/home/')

    def test_get_locale_article_no_trailing(self):
        """it should return lang and locale-independent-path"""
        result = strip_locale_path('/en/home')
        self.assertEqual(len(result), 2)
        self.assertEqual(result[0], 'en')
        self.assertEqual(result[1], '/home')

    def test_get_locale_article_no_prefix(self):
        """it should returns empty locale and path"""
        result = strip_locale_path('/home/')
        self.assertEqual(len(result), 2)
        self.assertEqual(result[0], '')
        self.assertEqual(result[1], '/home/')

    def test_get_locale_article_no_prefix_no_trailing(self):
        """it should returns empty locale and path"""
        result = strip_locale_path('/home')
        self.assertEqual(len(result), 2)
        self.assertEqual(result[0], '')
        self.assertEqual(result[1], '/home')

    @skipIf(not is_multilang(), "not multi lang")
    def test_get_url_in_language(self):
        """it should return url in another language"""

        origin_lang = settings.LANGUAGES[0][0]
        trans_lang = settings.LANGUAGES[1][0]

        activate(origin_lang)
        path = '/this-is-test/'
        origin_url = make_locale_path(path, origin_lang)

        trans_url = get_url_in_language(origin_url, trans_lang)
        self.assertEqual(trans_url, make_locale_path(path, trans_lang))


class UrlLocalizationTest(BaseTestCase):
    """localize url"""
    
    def setUp(self):
        self.user = None
        activate(settings.LANGUAGES[0][0])
    
    def tearDown(self):
        activate(settings.LANGUAGES[0][0])
    
    def _log_as_editor(self):
        """Log as editor"""
        self.user = user = User.objects.create_user('toto', 'toto@toto.fr', 'toto')
        
        content_type = ContentType.objects.get_for_model(get_article_class())
        
        perm = 'change_{0}'.format(content_type.model)
        can_edit_article = Permission.objects.get(content_type=content_type, codename=perm)
        user.user_permissions.add(can_edit_article)
        
        perm = 'add_{0}'.format(content_type.model)
        can_add_article = Permission.objects.get(content_type=content_type, codename=perm)
        user.user_permissions.add(can_add_article)
        
        user.is_active = True
        user.save()
        return self.client.login(username='toto', password='toto')
    
    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_get_locale_article(self):
        """get article with locale slug"""
        original_text = '*!-+' * 10
        translated_text = ':%@/' * 9
        
        art1 = get_article_class().objects.create(title="Home", content=original_text)
        
        origin_lang = settings.LANGUAGES[0][0]
        trans_lang = settings.LANGUAGES[1][0]
        
        setattr(art1, 'title_' + trans_lang, 'Accueil')
        setattr(art1, 'content_' + trans_lang, translated_text)
        art1.save()
        
        response = self.client.get('/{0}/home/'.format(origin_lang), follow=True)
        self.assertEqual(200, response.status_code)
        self.assertContains(response, original_text)
        
        response = self.client.get('/{0}/accueil/'.format(trans_lang), follow=True)
        self.assertEqual(200, response.status_code)
        self.assertContains(response, translated_text)

    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_change_lang(self):
        """change language"""

        original_text = '*!-+' * 10
        translated_text = ':%@/' * 9
        
        art1 = get_article_class().objects.create(title="Home", content=original_text)
        
        origin_lang = settings.LANGUAGES[0][0]
        trans_lang = settings.LANGUAGES[1][0]

        activate(origin_lang)

        setattr(art1, 'title_' + trans_lang, 'Accueil')
        setattr(art1, 'content_' + trans_lang, translated_text)
        
        art1.save()
        
        origin_url = '/{0}/home/'.format(origin_lang)
        response = self.client.get(origin_url, follow=True)
        self.assertEqual(200, response.status_code)
        self.assertContains(response, original_text)
        
        data = {'language': trans_lang}
        response = self.client.post(
            reverse('coop_cms_change_language') + '?next={0}'.format(origin_url),
            data=data,
            follow=True
        )
        self.assertEqual(200, response.status_code)
        self.assertContains(response, translated_text)
        
        response = self.client.get('/{0}/accueil/'.format(trans_lang), follow=True)
        self.assertEqual(200, response.status_code)
        self.assertContains(response, translated_text)
        
    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_change_lang_next_url_after(self):
        """change language and redirect to url"""
        
        original_text = '*!-+' * 10
        translated_text = ':%@/' * 9
        
        art1 = get_article_class().objects.create(title="Home", content=original_text)
        
        art2 = get_article_class().objects.create(title="Next", content="****NEXT****")
        
        origin_lang = settings.LANGUAGES[0][0]
        trans_lang = settings.LANGUAGES[1][0]
        
        setattr(art1, 'title_' + trans_lang, 'Accueil')
        setattr(art1, 'content_' + trans_lang, translated_text)
        
        art1.save()
        
        origin_url = '/{0}/home/'.format(origin_lang)
        response = self.client.get(origin_url, follow=True)
        self.assertEqual(200, response.status_code)
        self.assertContains(response, original_text)
        
        data = {'language': trans_lang, 'next_url_after_change_lang': art2.get_absolute_url()}
        response = self.client.post(
            reverse('coop_cms_change_language'),
            data=data,
            follow=True
        )
        self.assertEqual(200, response.status_code)
        self.assertContains(response, art2.content)
            
    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_change_lang_no_trans(self):
        """change language and no translation"""

        original_text = '*!-+' * 10
        
        get_article_class().objects.create(title="Home", content=original_text)
        
        origin_lang = settings.LANGUAGES[0][0]
        trans_lang = settings.LANGUAGES[1][0]
        
        origin_url = '/{0}/home/'.format(origin_lang)
        response = self.client.get(origin_url, follow=True)
        self.assertEqual(200, response.status_code)
        self.assertContains(response, original_text)
        
        data = {'language': trans_lang}
        response = self.client.post(
            reverse('coop_cms_change_language') + '?next={0}'.format(origin_url),
            data=data,
            follow=True
        )
        self.assertEqual(200, response.status_code)
        self.assertContains(response, original_text)
        
        response = self.client.get('/{0}/home/'.format(trans_lang), follow=True)
        self.assertEqual(200, response.status_code)
        self.assertContains(response, original_text)
            
    def test_keep_slug(self):
        """test slug are not modified when changing title"""
        article_class = get_article_class()
        art1 = article_class.objects.create(title="Home", content="aa")
        original_slug = art1.slug
        art1.title = "Title changed"
        art1.save()
        art1 = article_class.objects.get(id=art1.id)
        self.assertEqual(original_slug, art1.slug)
        
    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_keep_localized_slug(self):
        """test translation of slug are not modified when changing title"""
        
        article_class = get_article_class()
        art1 = article_class.objects.create(title="Home", content="aa")
        trans_lang = settings.LANGUAGES[1][0]
        setattr(art1, 'title_' + trans_lang, 'Accueil')
        art1.save()
        
        original_slug = art1.slug
        original_trans_slug = getattr(art1, 'slug_' + trans_lang, '**dummy**')
        
        art1.title = "Title changed"
        setattr(art1, 'title_' + trans_lang, 'Titre change')
        
        art1.save()
        art1 = article_class.objects.get(id=art1.id)
        
        self.assertEqual(original_slug, art1.slug)
        self.assertEqual(original_trans_slug, getattr(art1, 'slug_' + trans_lang))
        
    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_localized_slug_already_existing(self):
        """test localized slug already exists"""
        
        article_class = get_article_class()
        art1 = article_class.objects.create(title="Home", content="aa")
        art2 = article_class.objects.create(title="Rome", content="aa")

        trans_lang = settings.LANGUAGES[1][0]
        setattr(art1, 'title_' + trans_lang, art2.title)
        art1.save()
        
        art2.save()
        
        setattr(art2, 'title_' + trans_lang, art2.title)
        art2.save()
        
        self.assertNotEqual(getattr(art2, 'slug_' + trans_lang), getattr(art1, 'slug_' + trans_lang))
        
    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_localized_slug_already_existing2(self):
        """test localized slug already exists 2 """
        
        article_class = get_article_class()
        art1 = article_class.objects.create(title="Home", content="aa")
        art2 = article_class.objects.create(title="Rome", content="aa")

        trans_lang = settings.LANGUAGES[1][0]
        setattr(art1, 'title_' + trans_lang, art2.title)
        art1.save()
        
        setattr(art2, 'title_' + trans_lang, art2.title)
        art2.save()
        
        self.assertNotEqual(getattr(art2, 'slug_' + trans_lang), getattr(art1, 'slug_' + trans_lang))
        
    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_localized_slug_already_existing3(self):
        """test localized slug already exists 3 """
        self._log_as_editor()
        article_class = get_article_class()

        art1 = article_class.objects.create(title="Home", content="aa")
        art2 = article_class.objects.create(title="Rome", content="aa", template='test/article.html')

        trans_lang = settings.LANGUAGES[1][0]
        setattr(art1, 'title_' + trans_lang, art2.title)
        art1.save()
        
        # CHANGE LANGUAGE
        activate(trans_lang)
        
        url = art2.get_edit_url()
        
        data = {
            'title': art2.title,
            'content': 'translation', 
        }
        
        response = self.client.post(url, data=data, follow=True)
        self.assertEqual(response.status_code, 200)
        
        art2_updated = article_class.objects.get(id=art2.id)
        
        self.assertEqual(getattr(art2_updated, 'title_' + trans_lang), art2.title)
        
        self.assertNotEqual(getattr(art2_updated, 'slug_' + trans_lang), getattr(art1, 'slug_' + trans_lang))
        
    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_localize_existing_article1(self):
        """test localized existing article 1 """

        self._log_as_editor()
        article_class = get_article_class()
        art1 = article_class.objects.create(title="Home", template='test/article.html')

        origin_lang = settings.LANGUAGES[0][0]
        trans_lang = settings.LANGUAGES[1][0]
        
        # CHANGE LANGUAGE
        activate(trans_lang)
        
        url = art1.get_edit_url()
        
        data = {
            'title': "Home",
            'content': 'translation', 
        }
        
        response = self.client.post(url, data=data, follow=True)
        self.assertEqual(response.status_code, 200)
        
        art1_updated = article_class.objects.get(id=art1.id)
        
        self.assertEqual(getattr(art1_updated, 'title_' + trans_lang), art1.title)
        self.assertEqual(getattr(art1_updated, 'slug_' + trans_lang), getattr(art1, 'slug_' + origin_lang))
        
    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_localize_existing_article2(self):
        """test localized existing article 2 """
        self._log_as_editor()
        article_class = get_article_class()
        art1 = article_class.objects.create(title="Accueil", template='test/article.html')

        origin_lang = settings.LANGUAGES[0][0]
        trans_lang = settings.LANGUAGES[1][0]
        
        # CHANGE LANGUAGE
        activate(trans_lang)
        
        url = art1.get_edit_url()
        
        data = {
            'title': "Home",
            'content': 'translation', 
        }
        
        response = self.client.post(url, data=data, follow=True)
        self.assertEqual(response.status_code, 200)
        
        art1_updated = article_class.objects.get(id=art1.id)
        self.assertEqual(getattr(art1_updated, 'title_' + origin_lang), art1.title)
        self.assertEqual(getattr(art1_updated, 'title_' + trans_lang), data["title"])
        self.assertEqual(getattr(art1_updated, 'slug_' + trans_lang), "home")
        self.assertEqual(getattr(art1_updated, 'slug_' + origin_lang), "accueil")
        
    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_localized_slug_already_existing4(self):
        """test localized slug and existing article """
        self._log_as_editor()
        article_class = get_article_class()
        art1 = article_class.objects.create(title="Home", content="aa")
        art2 = article_class.objects.create(title="Rome", content="aa", template='test/article.html')

        trans_lang = settings.LANGUAGES[1][0]
        
        self.assertEqual(None, getattr(art2, 'slug_' + trans_lang))
        
        # CHANGE LANGUAGE
        activate(trans_lang)
        
        url = art2.get_edit_url()
        
        data = {
            'title': art1.title,
            'content': 'translation', 
        }
        
        response = self.client.post(url, data=data, follow=True)
        self.assertEqual(response.status_code, 200)
        
        art2_updated = article_class.objects.get(id=art2.id)
        
        self.assertEqual(getattr(art2_updated, 'title_' + trans_lang), art1.title)
        
        self.assertNotEqual(getattr(art2_updated, 'slug_' + trans_lang), art1.slug)
        
    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_localized_slug_already_existing5(self):
        """test localized slug existing and existing article """

        self._log_as_editor()
        article_class = get_article_class()
        art1 = article_class.objects.create(title="Home", content="aa")
        art2 = article_class.objects.create(title="Rome", content="aa", template='test/article.html')

        trans_lang = settings.LANGUAGES[1][0]
        
        self.assertEqual(None, getattr(art2, 'slug_' + trans_lang))
        
        setattr(art2, 'title_' + trans_lang, art1.title)
        art2.save()
        self.assertNotEqual(art1.slug, getattr(art2, 'slug_' + trans_lang))
        
        # CHANGE LANGUAGE
        activate(trans_lang)
        
        url = art2.get_edit_url()
        
        data = {
            'title': art1.title,
            'content': 'translation', 
        }
        
        response = self.client.post(url, data=data, follow=True)
        self.assertEqual(response.status_code, 200)
        
        art2_updated = article_class.objects.get(id=art2.id)
        
        self.assertEqual(getattr(art2_updated, 'title_' + trans_lang), art1.title)
        
        self.assertNotEqual(getattr(art2_updated, 'slug_' + trans_lang), art1.slug)
            
    def test_no_title(self):
        """test create article without title"""
        article_class = get_article_class()
        
        try:
            article_class.objects.create(title="", content="a!*%:" * 10, publication=BaseArticle.PUBLISHED)
        except InvalidArticleError:
            # OK
            return

        # Force to fail
        self.assertFalse(True)

    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    @override_settings(MODELTRANSLATION_FALLBACK_LANGUAGES=language_fallbacks())
    def test_create_article_in_additional_lang_fallback(self):
        """test create article into an other language than the default"""

        article_class = get_article_class()
        
        default_lang = settings.LANGUAGES[0][0]
        other_lang = settings.LANGUAGES[1][0]
        
        activate(other_lang)
        
        art1 = article_class.objects.create(title="abcd", content="a!*%:" * 10, publication=BaseArticle.PUBLISHED)
        
        response = self.client.get(art1.get_absolute_url())
        self.assertEqual(200, response.status_code)
        self.assertContains(response, art1.content)
        
        activate(default_lang)
        
        response = self.client.get(art1.get_absolute_url())
        self.assertEqual(200, response.status_code)
        self.assertContains(response, art1.content)

    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_create_article_in_additional_lang_no_fallback(self):
        """test create article into an other language than the default"""

        article_class = get_article_class()

        default_lang = settings.LANGUAGES[0][0]
        other_lang = settings.LANGUAGES[1][0]

        activate(other_lang)

        art1 = article_class.objects.create(title="abcd", content="a!*%:" * 10, publication=BaseArticle.PUBLISHED)

        response = self.client.get(art1.get_absolute_url())
        self.assertEqual(200, response.status_code)
        self.assertContains(response, art1.content)

        activate(default_lang)

        response = self.client.get(art1.get_absolute_url())
        self.assertEqual(404, response.status_code)

    @skipIf(not has_localized_urls() or multilang_mode() < 3, "not localized")
    def test_create_article_in_third_lang(self):
        """test create article into an other language than the default"""

        article_class = get_article_class()

        default_lang = settings.LANGUAGES[0][0]
        other_lang = settings.LANGUAGES[1][0]
        third_lang = settings.LANGUAGES[2][0]

        activate(third_lang)
        content = "a!*%:"*10

        art1 = article_class.objects.create(title="abcd", content=content, publication=BaseArticle.PUBLISHED)
        self.assertEqual(art1.slug, 'abcd')
        third_lang_url = art1.get_absolute_url()
        response = self.client.get(third_lang_url)
        self.assertEqual(200, response.status_code)
        self.assertContains(response, content)

        activate(other_lang)
        art1.title = 'efgh'
        art1.save()

        self.assertEqual(art1.slug, 'efgh')

        response = self.client.get(art1.get_absolute_url())
        self.assertEqual(200, response.status_code)
        self.assertNotContains(response, content)

        response = self.client.get(third_lang_url.replace('/' + third_lang + '/', '/' + other_lang + '/', 1))
        self.assertEqual(404, response.status_code)

    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_redirect_to_language(self):
        """check redirect_to_language utility"""
        article_class = get_article_class()

        other_lang = settings.LANGUAGES[1][0]

        art1 = article_class.objects.create(title="abcd", publication=BaseArticle.PUBLISHED)

        response = redirect_to_language(art1.get_absolute_url(), other_lang)

        self.assertTrue(response.url.find("/" + other_lang + "/") == 0)
        self.assertEqual(get_language(), other_lang)

    @skipIf(not has_localized_urls() or not is_multilang(), "not localized")
    def test_redirect_to_invalid_language(self):
        """check redirect_to_language uitiliy raise error if ImproperlyConfigured"""

        article_class = get_article_class()

        art1 = article_class.objects.create(title="abcd", publication=BaseArticle.PUBLISHED)

        self.assertRaises(ImproperlyConfigured, redirect_to_language, art1.get_absolute_url(), "zz")


class SwitchLanguageTest(BaseTestCase):
    """change the language from coop-bar popup"""

    def setUp(self):
        self.user = None
        activate(settings.LANGUAGES[0][0])

    def tearDown(self):
        activate(settings.LANGUAGES[0][0])

    @skipIf(not is_localized() or not is_multilang(), "not localized")
    def test_get_language_switcher(self):
        """get article with locale slug"""

        original_text = '*!-+' * 10
        translated_text = ':%@/' * 9

        art1 = get_article_class().objects.create(title="Home", content=original_text)

        origin_lang = settings.LANGUAGES[0][0]
        trans_lang = settings.LANGUAGES[1][0]

        setattr(art1, 'title_' + trans_lang, 'Accueil')
        setattr(art1, 'content_' + trans_lang, translated_text)
        art1.save()

        url = reverse('coop_cms_switch_language_popup')

        response = self.client.get(url)
        soup = BeautifulSoup(response.content)
        self.assertEqual(len(soup.select('select#id_language option')), len(settings.LANGUAGES))

    @skipIf(not is_localized() or not is_multilang(), "not localized")
    def test_change_lang(self):
        """change language"""

        original_text = '*!-+' * 10
        translated_text = ':%@/' * 9

        art1 = get_article_class().objects.create(title="Home", content=original_text)

        origin_lang = settings.LANGUAGES[0][0]
        trans_lang = settings.LANGUAGES[1][0]

        activate(origin_lang)

        setattr(art1, 'title_' + trans_lang, 'Accueil')
        setattr(art1, 'content_' + trans_lang, translated_text)

        art1.save()

        data = {'language': trans_lang}
        response = self.client.post(
            reverse('coop_cms_switch_language_popup'),
            data=data,
            follow=True
        )
        self.assertEqual(200, response.status_code)
        self.assertEqual(trans_lang, get_language())
