from django.test import TestCase
from graphene import Context
from graphene.test import Client
from graphql_relay import to_global_id

from api.schema import schema
from api.utils import get_graphql_mutation
from core.factories import TeacherFactory, UserFactory
from users.factories import TeamFactory
from users.models import Team, User

ME_QUERY = """
        query MeQuery {
          me {
            team {
                name
                code
            }
          }
        }
        """

TEAM_PARTS = """
fragment TeamParts on TeamNode {
  name
  code
  id
  members {
    firstName
    lastName
    id
    isMe
  }
}
"""

CREATE_TEAM_MUTATION = TEAM_PARTS + get_graphql_mutation('createTeam.gql')
JOIN_TEAM_MUTATION = TEAM_PARTS + get_graphql_mutation('joinTeam.gql')
UPDATE_TEAM_MUTATION = get_graphql_mutation('updateTeam.gql')
LEAVE_TEAM_MUTATION = get_graphql_mutation('me/leaveTeam.gql')


class TeamTest(TestCase):
    def setUp(self):
        self.client = Client(schema=schema)
        self.team_name = 'Fiterativ'
        self.code = 'AAAA'
        self.team = TeamFactory(name=self.team_name, code=self.code)
        self.team_id = to_global_id('TeamNode', self.team.id)
        self.user = TeacherFactory(username='ueli', team=self.team)
        self.student = UserFactory(username='fritzli')
        self.context = Context(user=self.user)

    @staticmethod
    def get_team(result):
        return result.get('data').get('me').get('team')

    def no_error(self, result):
        self.assertIsNone(result.get('errors'))

    def permission_error(self, result):
        errors = result.get('errors')
        self.assertIsNotNone(errors)
        self.assertIn('Permission denied', errors[0]['message'])

    def check_me(self, context, name, code):
        result = self.client.execute(ME_QUERY, context=context)
        self.no_error(result)
        team = self.get_team(result)

        self.assertEqual(team.get('name'), name)
        self.assertEqual(team.get('code'), code)

    def join_team(self, code, context):
        variables = {
            "input": {
                "code": code
            }
        }
        result = self.client.execute(JOIN_TEAM_MUTATION, variables=variables, context=context)
        self.no_error(result)
        success = result['data']['joinTeam']['success']
        self.assertTrue(success)

    def test_team_query(self):
        self.check_me(self.context, self.team_name, self.code)

    def test_join_team_mutation(self):
        teacher = TeacherFactory(username='hansli')
        context = Context(user=teacher)
        self.join_team(self.code, context)

        self.check_me(context, self.team_name, self.code)

    def test_join_second_team_mutation(self):
        teacher = TeacherFactory(username='peterli')
        context = Context(user=teacher)
        second_team = TeamFactory()
        self.join_team(self.code, context)
        self.check_me(context, self.team_name, self.code)
        self.join_team(second_team.code, context)
        self.check_me(context, second_team.name, second_team.code)

    def test_create_team_mutation(self):
        team_name = "Dunder Mifflin"
        variables = {
            "input": {
                "name": team_name
            }
        }
        result = self.client.execute(CREATE_TEAM_MUTATION, context=self.context, variables=variables)
        self.no_error(result)
        create_team = result.get('data').get('createTeam')
        team = create_team.get('team')
        success = create_team.get('success')
        self.assertTrue(success)
        self.assertEqual(team.get('name'), team_name)
        self.assertIsNotNone(team.get('code'))

    def test_update_team_name(self):
        new_name = 'Team Böhmermann'
        variables = {
            "input": {
                "name": new_name,
                "id": self.team_id
            }
        }
        result = self.client.execute(UPDATE_TEAM_MUTATION, context=self.context, variables=variables)
        self.no_error(result)
        update_team = result.get('data').get('updateTeam')
        team = update_team.get('team')
        success = update_team.get('success')
        self.assertTrue(success)
        self.assertEqual(team.get('name'), new_name)

    def test_update_team_name_as_student_fails(self):
        context = Context(user=self.student)
        variables = {
            "input": {
                "name": 'Not gonna happen',
                "id": self.team_id
            }
        }
        result = self.client.execute(UPDATE_TEAM_MUTATION, context=context, variables=variables)
        self.permission_error(result)

    def test_update_team_name_not_in_team_fails(self):
        schelm = TeacherFactory(username='schelm')
        context = Context(user=schelm)
        team = Team.objects.get(pk=self.team.pk)
        old_name = team.name
        self.assertFalse(self.team.members.filter(pk=schelm.pk).exists()) 
        variables = {
            "input": {
                "name": 'Not gonna happen',
                "id": self.team_id
            }
        }
        result = self.client.execute(UPDATE_TEAM_MUTATION, context=context, variables=variables)
        self.permission_error(result)
        team = Team.objects.get(pk=self.team.pk)
        self.assertEqual(team.name, old_name)

    def test_create_team_mutation_as_student_fails(self):
        self.assertEqual(Team.objects.count(), 1)
        variables = {
            "input": {
                "name": 'Nope'
            }
        }
        context = Context(user=self.student)
        result = self.client.execute(CREATE_TEAM_MUTATION, context=context, variables=variables)
        self.permission_error(result)
        self.assertEqual(Team.objects.count(), 1)

    def test_join_create_team_mutation_as_student_fails(self):
        self.assertIsNone(self.student.team)
        variables = {
            "input": {
                "code": 'ZZZZ'
            }
        }
        context = Context(user=self.student)
        result = self.client.execute(JOIN_TEAM_MUTATION, context=context, variables=variables)
        self.permission_error(result)
        self.assertIsNone(self.student.team)

    def test_leave_team(self):
        self.student.team = self.team
        self.student.save()
        student_before = User.objects.get(pk=self.student.pk)
        self.assertIsNotNone(student_before.team)
        context = Context(user=student_before)
        result = self.client.execute(LEAVE_TEAM_MUTATION, context=context)
        self.no_error(result)
        leave_team = result.get('data').get('leaveTeam')
        success = leave_team.get('success')
        self.assertTrue(success)

        student_after = User.objects.get(pk=self.student.pk)
        self.assertIsNone(student_after.team)

