"""
Tests for helpers methods
"""
from unittest.mock import Mock, patch

import ddt
from django.test import TestCase

from edx_recommendations.api.utils import (
    _has_country_restrictions,
    filter_recommended_courses,
    get_active_course_run,
    get_amplitude_course_recommendations,
    get_cross_product_recommendations,
)
from test_utils.constants import mock_cross_product_recommendation_keys
from test_utils.factories import CourseEnrollmentFactory, UserFactory


@ddt.ddt
class TestRecommendationsHelper(TestCase):
    """Test course recommendations helper methods."""

    def setUp(self):
        super().setUp()
        self.user = UserFactory()

    @ddt.data(
        ({}, 0),
        ({"userData": {}}, 0),
        ({"userData": {"recommendations": []}}, 0),
        (
            {
                "userData": {
                    "recommendations": [
                        {
                            "items": ["MITx+6.00.1x", "IBM+PY0101EN", "HarvardX+CS50P"],
                            "is_control": True,
                            "has_is_control": False,
                        }
                    ],
                }
            },
            3,
        ),
    )
    @patch("edx_recommendations.api.utils.requests.get")
    @ddt.unpack
    def test_get_amplitude_course_recommendations_method(
        self, mocked_response, expected_recommendations_count, mock_get
    ):
        """
        Tests the get_amplitude_recommendations method returns course key list.
        """
        mock_get.return_value = Mock(status_code=200, json=lambda: mocked_response)
        _, _, course_keys = get_amplitude_course_recommendations(
            self.user.id, "amplitude-rec-id"
        )
        self.assertEqual(len(course_keys), expected_recommendations_count)

    @ddt.data(
        ({}, False),
        ({"restriction_type": "blocklist", "countries": []}, False),
        ({"restriction_type": "blocklist", "countries": ["SA"]}, False),
        ({"restriction_type": "blocklist", "countries": ["US"]}, True),
        ({"restriction_type": "allowlist", "countries": []}, False),
        ({"restriction_type": "allowlist", "countries": ["SA"]}, True),
        ({"restriction_type": "allowlist", "countries": ["US"]}, False),
    )
    @ddt.unpack
    def test_has_country_restrictions_method(
        self,
        location_restriction,
        expected_response,
    ):
        """
        Helper method to test the _has_country_restrictions method.
        """
        product = {"location_restriction": location_restriction}
        assert _has_country_restrictions(product, "US") == expected_response


class TestFilterRecommendedCourses(TestCase):
    """Test for filter_recommended_courses helper method."""

    def setUp(self):
        super().setUp()
        self.user = UserFactory()
        self.recommended_course_keys = [
            "MITx+6.00.1x",
            "IBM+PY0101EN",
            "HarvardX+CS50P",
            "UQx+IELTSx",
            "HarvardX+CS50x",
            "Harvard+CS50z",
            "BabsonX+EPS03x",
            "TUMx+QPLS2x",
            "NYUx+FCS.NET.1",
            "MichinX+101x",
        ]
        self.unrestricted_course_keys = self.recommended_course_keys[0:2]
        self.course_run_keys = [f"course-v1:{course_key}+Run_0" for course_key in self.recommended_course_keys]
        self.course_keys_with_active_course_runs = self.recommended_course_keys[0:8]
        self.enrolled_course_run_keys = self.course_run_keys[4:10]

    def _mock_get_course_data(self, course_id, fields=None, querystring=None):  # pylint: disable=unused-argument
        """
        Mocked response for the get_course_data call
        """
        course_data = {
            "course_key": course_id,
            "title": "Mocked course title",
            "owners": [{"logo_image_url": "https://www.logo_image_url.com"}],
            "marketing_url": "https://www.marketing_url.com",
        }

        if course_id not in self.unrestricted_course_keys:
            course_data.update(
                {
                    "location_restriction": {
                        "restriction_type": "blocklist",
                        "countries": ["US"],
                    }
                }
            )

        if course_id in self.course_keys_with_active_course_runs:
            course_data.update(
                {
                    "course_runs": [
                        {
                            "key": f"course-v1:{course_id}+Run_0",
                        }
                    ]
                }
            )

        return course_data

    @patch("edx_recommendations.api.utils.get_course_data")
    def test_enrolled_courses_are_removed_from_recommendations(
        self, mocked_get_course_data
    ):
        """
        Tests that given a recommended course list, the filter_recommended_courses
        method removes the enrolled courses from it.
        """
        total_enrolled_courses = len(self.enrolled_course_run_keys)
        total_recommendations = len(self.recommended_course_keys)
        mocked_get_course_data.side_effect = self._mock_get_course_data
        for course_run_key in self.enrolled_course_run_keys:
            CourseEnrollmentFactory(course_id=course_run_key, user=self.user)

        filtered_courses = filter_recommended_courses(
            self.user, self.recommended_course_keys, total_recommendations
        )
        assert len(filtered_courses) == (total_recommendations - total_enrolled_courses)

    @patch("edx_recommendations.api.utils.get_course_data")
    def test_request_course_is_removed_from_the_recommendations(
        self,
        mocked_get_course_data,
    ):
        """
        Test that if the "request course" is one of the recommended courses,
        we filter that from the final recommendation list.
        """
        request_course = self.course_run_keys[0]
        mocked_get_course_data.side_effect = self._mock_get_course_data
        filtered_courses = filter_recommended_courses(
            self.user,
            self.recommended_course_keys,
            request_course_key=request_course,
        )

        assert all(course["course_key"] != request_course for course in filtered_courses) is True

    @patch("edx_recommendations.api.utils.get_course_data")
    def test_country_restrictions_for_the_recommended_course(
        self,
        mocked_get_course_data,
    ):
        """
        Test that if a recommended course is restricted in the country the user
        is logged from, the course is filtered out.
        """
        mocked_get_course_data.side_effect = self._mock_get_course_data
        filtered_courses = filter_recommended_courses(
            self.user, self.recommended_course_keys, user_country_code="US"
        )
        expected_recommendations = []
        for course_key in self.unrestricted_course_keys:
            expected_recommendations.append(self._mock_get_course_data(course_key))

        assert filtered_courses == expected_recommendations

    @patch("edx_recommendations.api.utils.get_course_data")
    def test_recommend_only_active_courses(
        self,
        mocked_get_course_data,
    ):
        """
        Test that courses having no active course runs are filtered out from recommended courses.
        """
        mocked_get_course_data.side_effect = self._mock_get_course_data
        filtered_courses = filter_recommended_courses(
            self.user, self.recommended_course_keys
        )
        expected_recommendations = []
        for course_key in self.course_keys_with_active_course_runs:
            expected_recommendations.append(self._mock_get_course_data(course_key))

        assert filtered_courses == expected_recommendations


@ddt.ddt
class TestGetCrossProductRecommendationsMethod(TestCase):
    """Test for get_cross_product_recommendations method"""

    @ddt.data(
        ("edx+HL0", ["edx+HL1", "edx+HL2"]),
        ("edx+BZ0", ["edx+BZ1", "edx+BZ2"]),
        ('NoKeyAssociated', None)
    )
    @patch("django.conf.settings.CROSS_PRODUCT_RECOMMENDATIONS_KEYS", mock_cross_product_recommendation_keys)
    @ddt.unpack
    def test_get_cross_product_recommendations_method(self, course_key, expected_response):
        assert get_cross_product_recommendations(course_key) == expected_response


class TestGetActiveCourseRunMethod(TestCase):
    """Tests for get_active_course_run method"""

    advertised_course_run_uuid = "jh76b2c9-589b-4d1e-88c1-b01a02db3a9c"

    def _mock_get_course_data(self, active_course_run=False):
        """
        Returns a course with details based on the status passed
        """
        return {
            "key": "edx+BLN",
            "uuid": "6f8cb2c9-589b-4d1e-88c1-b01a02db3a9c",
            "course_runs": [
                {
                    "key": "course-v1:Test+2023_T1",
                    "uuid": "hb86b3cf-589b-4d1e-88c1-b01a02db3a9c",
                    "status": "published",
                },
                {
                    "key": "course-v1:Test+2023_T2",
                    "uuid": self.advertised_course_run_uuid if active_course_run else "other-uuid",
                    "status": "published",
                }
            ],
            "course_run_statuses": ["published"],
            "advertised_course_run_uuid": self.advertised_course_run_uuid if active_course_run else None,
        }

    def test_advertised_course_run_returned(self):
        """
        Test that the course run with the uuid that matches the advertised_uuid_course_run_uuid is returned
        """
        course = self._mock_get_course_data(active_course_run=True)
        active_course_run = get_active_course_run(course)

        self.assertDictEqual(
            active_course_run,
            {
                "key": "course-v1:Test+2023_T2",
                "uuid": self.advertised_course_run_uuid,
                "status": "published"
            }
        )

    def test_no_course_run_returned(self):
        """
        Test that if there is no advertised_course_run_uuid value, no course run is returned
        """
        course = self._mock_get_course_data(active_course_run=False)
        active_course_run = get_active_course_run(course)

        self.assertIsNone(active_course_run)
