"""
Test signal handlers.
"""

from datetime import datetime

import ddt
from django.test import TestCase
from mock import patch
from opaque_keys.edx.keys import CourseKey
from pytz import utc
import six
from xblock.completable import XBlockCompletionMode
from xblock.core import XBlock

from lms.djangoapps.grades.signals.signals import PROBLEM_WEIGHTED_SCORE_CHANGED
from student.tests.factories import UserFactory

from .. import handlers
from ..models import BlockCompletion
from ..test_utils import CompletionWaffleTestMixin


class CustomScorableBlock(XBlock):
    """
    A scorable block with a custom completion strategy.
    """
    has_score = True
    has_custom_completion = True
    completion_mode = XBlockCompletionMode.COMPLETABLE


class ExcludedScorableBlock(XBlock):
    """
    A scorable block that is excluded from completion tracking.
    """
    has_score = True
    has_custom_completion = False
    completion_mode = XBlockCompletionMode.EXCLUDED


@ddt.ddt
class ScorableCompletionHandlerTestCase(CompletionWaffleTestMixin, TestCase):
    """
    Test the signal handler
    """

    def setUp(self):
        super(ScorableCompletionHandlerTestCase, self).setUp()
        self.course_key = CourseKey.from_string('edx/course/beta')
        self.scorable_block_key = self.course_key.make_usage_key(block_type='problem', block_id='red')
        self.user = UserFactory.create()
        self.override_waffle_switch(True)

    def call_scorable_block_completion_handler(self, block_key, score_deleted=None):
        """
        Call the scorable completion signal handler for the specified block.

        Optionally takes a value to pass as score_deleted.
        """
        if score_deleted is None:
            params = {}
        else:
            params = {'score_deleted': score_deleted}
        handlers.scorable_block_completion(
            sender=self,
            user_id=self.user.id,
            course_id=six.text_type(self.course_key),
            usage_id=six.text_type(block_key),
            weighted_earned=0.0,
            weighted_possible=3.0,
            modified=datetime.utcnow().replace(tzinfo=utc),
            score_db_table='submissions',
            **params
        )

    @ddt.data(
        (True, 0.0),
        (False, 1.0),
        (None, 1.0),
    )
    @ddt.unpack
    def test_handler_submits_completion(self, score_deleted, expected_completion):
        self.call_scorable_block_completion_handler(self.scorable_block_key, score_deleted)
        completion = BlockCompletion.objects.get(
            user=self.user,
            course_key=self.course_key,
            block_key=self.scorable_block_key,
        )
        self.assertEqual(completion.completion, expected_completion)

    @XBlock.register_temp_plugin(CustomScorableBlock, 'custom_scorable')
    def test_handler_skips_custom_block(self):
        custom_block_key = self.course_key.make_usage_key(block_type='custom_scorable', block_id='green')
        self.call_scorable_block_completion_handler(custom_block_key)
        completion = BlockCompletion.objects.filter(
            user=self.user,
            course_key=self.course_key,
            block_key=custom_block_key,
        )
        self.assertFalse(completion.exists())

    @XBlock.register_temp_plugin(ExcludedScorableBlock, 'excluded_scorable')
    def test_handler_skips_excluded_block(self):
        excluded_block_key = self.course_key.make_usage_key(block_type='excluded_scorable', block_id='blue')
        self.call_scorable_block_completion_handler(excluded_block_key)
        completion = BlockCompletion.objects.filter(
            user=self.user,
            course_key=self.course_key,
            block_key=excluded_block_key,
        )
        self.assertFalse(completion.exists())

    def test_signal_calls_handler(self):
        user = UserFactory.create()

        with patch('lms.djangoapps.completion.handlers.scorable_block_completion') as mock_handler:
            PROBLEM_WEIGHTED_SCORE_CHANGED.send_robust(
                sender=self,
                user_id=user.id,
                course_id=six.text_type(self.course_key),
                usage_id=six.text_type(self.scorable_block_key),
                weighted_earned=0.0,
                weighted_possible=3.0,
                modified=datetime.utcnow().replace(tzinfo=utc),
                score_db_table='submissions',
            )
        mock_handler.assert_called()


class DisabledCompletionHandlerTestCase(CompletionWaffleTestMixin, TestCase):
    """
    Test that disabling the ENABLE_COMPLETION_TRACKING waffle switch prevents
    the signal handler from submitting a completion.
    """
    def setUp(self):
        super(DisabledCompletionHandlerTestCase, self).setUp()
        self.user = UserFactory.create()
        self.course_key = CourseKey.from_string("course-v1:a+valid+course")
        self.block_key = self.course_key.make_usage_key(block_type="video", block_id="mah-video")
        self.override_waffle_switch(False)

    def test_disabled_handler_does_not_submit_completion(self):
        handlers.scorable_block_completion(
            sender=self,
            user_id=self.user.id,
            course_id=six.text_type(self.course_key),
            usage_id=six.text_type(self.block_key),
            weighted_earned=0.0,
            weighted_possible=3.0,
            modified=datetime.utcnow().replace(tzinfo=utc),
            score_db_table='submissions',
        )
        with self.assertRaises(BlockCompletion.DoesNotExist):
            BlockCompletion.objects.get(
                user=self.user,
                course_key=self.course_key,
                block_key=self.block_key
            )