from unittest import mock

from django.contrib.auth import get_user_model
from django.contrib.contenttypes.models import ContentType
from django.contrib.sessions.middleware import SessionMiddleware
from django.http import HttpResponse, QueryDict
from django.test import RequestFactory, TestCase
from django.utils.translation import gettext_lazy

from social_django.utils import load_backend, load_strategy


class TestStrategy(TestCase):
    def setUp(self):
        self.request_factory = RequestFactory()
        self.request = self.request_factory.get("/", data={"x": "1"})
        SessionMiddleware(lambda: None).process_request(self.request)
        self.strategy = load_strategy(request=self.request)

    def test_request_methods(self):
        self.assertEqual(self.strategy.request_port(), "80")
        self.assertEqual(self.strategy.request_path(), "/")
        self.assertEqual(self.strategy.request_host(), "testserver")
        self.assertEqual(self.strategy.request_is_secure(), False)
        self.assertEqual(self.strategy.request_data(), QueryDict("x=1"))
        self.assertEqual(self.strategy.request_get(), QueryDict("x=1"))
        self.assertEqual(self.strategy.request_post(), {})
        self.request.method = "POST"
        self.assertEqual(self.strategy.request_data(merge=False), {})

    def test_build_absolute_uri(self):
        self.assertEqual(self.strategy.build_absolute_uri("/"), "http://testserver/")

    def test_settings(self):
        with self.settings(LOGIN_ERROR_URL="/"):
            self.assertEqual(self.strategy.get_setting("LOGIN_ERROR_URL"), "/")
        with self.settings(LOGIN_ERROR_URL=gettext_lazy("/")):
            self.assertEqual(self.strategy.get_setting("LOGIN_ERROR_URL"), "/")

    def test_session_methods(self):
        self.strategy.session_set("k", "v")
        self.assertEqual(self.strategy.session_get("k"), "v")
        self.assertEqual(self.strategy.session_setdefault("k", "x"), "v")
        self.assertEqual(self.strategy.session_pop("k"), "v")

    def test_random_string(self):
        rs1 = self.strategy.random_string()
        self.assertEqual(len(rs1), 12)
        self.assertNotEqual(rs1, self.strategy.random_string())

    def test_session_value(self):
        user_model = get_user_model()
        user = user_model._default_manager.create_user(username="test")
        ctype = ContentType.objects.get_for_model(user_model)

        val = self.strategy.to_session_value(val=user)
        self.assertEqual(val, {"pk": user.pk, "ctype": ctype.pk})

        instance = self.strategy.from_session_value(val=val)
        self.assertEqual(instance, user)

    def test_get_language(self):
        self.assertEqual(self.strategy.get_language(), "en-us")

    def test_html(self):
        result = self.strategy.render_html(tpl="test.html")
        self.assertEqual(result, "test")

        result = self.strategy.render_html(html="xoxo")
        self.assertEqual(result, "xoxo")

        with self.assertRaisesMessage(
            ValueError, "Missing template or html parameters"
        ):
            self.strategy.render_html()

        result = self.strategy.html(content="xoxo")
        self.assertIsInstance(result, HttpResponse)
        self.assertEqual(result.content, b"xoxo")

        ctx = {"x": 1}
        result = self.strategy.tpl.render_template(tpl="test.html", context=ctx)
        self.assertEqual(result, "test")

        result = self.strategy.tpl.render_string(html="xoxo", context=ctx)
        self.assertEqual(result, "xoxo")

    def test_authenticate(self):
        backend = load_backend(
            strategy=self.strategy, name="facebook", redirect_uri="/"
        )
        user = mock.Mock()
        with mock.patch(
            "social_core.backends.base.BaseAuth.pipeline", return_value=user
        ):
            result = self.strategy.authenticate(backend=backend, response=mock.Mock())
            self.assertEqual(result, user)
            self.assertEqual(
                result.backend, "social_core.backends.facebook.FacebookOAuth2"
            )

    def test_clean_authenticate_args(self):
        args, kwargs = self.strategy.clean_authenticate_args(self.request)
        self.assertEqual(args, ())
        self.assertEqual(kwargs, {"request": self.request})

    def test_clean_authenticate_args_none(self):
        # When called from continue_pipeline(), request is None. Issue #222
        args, kwargs = self.strategy.clean_authenticate_args(None)
        self.assertEqual(args, ())
        self.assertEqual(kwargs, {"request": None})
