"""Run: venv/bin/python manage.py test apps.games"""
import json

from django.contrib.auth.models import User
from django.test import TestCase, override_settings

from .models import Game, GameCard, Play


@override_settings(ALLOWED_HOSTS=['testserver'])
class GameTests(TestCase):
    def post(self, url, data=None):
        return self.client.post(url, json.dumps(data or {}), content_type='application/json')

    def start(self, slug):
        r = self.post(f'/games/{slug}/start/').json()
        return r['token'], r['question']

    def test_seeded_games_and_pages(self):
        self.assertEqual(Game.objects.count(), 3)
        self.assertContains(self.client.get('/games/'), 'কালচক্র')
        for g in Game.objects.all():
            page = self.client.get(g.get_absolute_url())
            self.assertContains(page, '"@type":["Game","LearningResource"]')
            self.assertContains(page, 'id="engage"')
        self.assertContains(self.client.get('/'), 'ইতিহাসের খেলা')
        self.assertIn('/games/kalchakra/', self.client.get('/sitemap.xml').content.decode())
        self.assertIn('Kalchakra', self.client.get('/llms.txt').content.decode())

    def test_answers_never_sent_and_timeline_scoring(self):
        token, q = self.start('kalchakra')
        raw = json.dumps(q)
        play = Play.objects.get(token=token)
        card = GameCard.objects.get(pk=play.cards[1])
        self.assertNotIn(str(card.year), raw)  # the year of the card being placed is hidden
        first = GameCard.objects.get(pk=play.cards[0])
        right = 0 if card.year < first.year else 1
        d = self.post(f'/games/play/{token}/', {'position': right}).json()
        self.assertTrue(d['correct'])
        self.assertEqual(d['score'], 10)

    def test_timeline_ends_after_three_mistakes(self):
        token, q = self.start('kalchakra')
        play = Play.objects.get(token=token)
        for _ in range(12):
            play.refresh_from_db()
            if play.finished:
                break
            card = GameCard.objects.get(pk=play.cards[play.step + 1])
            years = [GameCard.objects.get(pk=i).year for i in play.state['placed']]
            # choose a wrong slot whenever one exists
            wrong = next((i for i in range(len(years) + 1)
                          if not ((i == 0 or years[i - 1] <= card.year) and (i == len(years) or card.year <= years[i]))), None)
            self.post(f'/games/play/{token}/', {'position': wrong if wrong is not None else 0})
        play.refresh_from_db()
        self.assertTrue(play.finished)
        self.assertLessEqual(play.mistakes, 3)

    def test_fact_game_and_reward_coupon(self):
        User.objects.create_user('ana', 'ana@example.com', 'pw-12345678')
        self.client.login(username='ana', password='pw-12345678')
        token, q = self.start('sotti-na-gujob')
        play = Play.objects.get(token=token)
        result = None
        for cid in play.cards:
            truth = GameCard.objects.get(pk=cid).is_true
            result = self.post(f'/games/play/{token}/', {'value': truth}).json()
        fin = result['finished']
        self.assertEqual(fin['percent'], 100)
        self.assertEqual(fin['reward_percent'], 20)
        code = fin['coupon']['code']
        from apps.store.models import Coupon
        c = Coupon.objects.get(code=code)
        self.assertEqual((c.owner.username, c.max_uses, c.source, c.applies_to), ('ana', 1, 'game', 'magazines'))
        # second great game inside the cooldown → no second coupon
        token2, _ = self.start('sotti-na-gujob')
        play2 = Play.objects.get(token=token2)
        for cid in play2.cards:
            r = self.post(f'/games/play/{token2}/', {'value': GameCard.objects.get(pk=cid).is_true}).json()
        self.assertIn('cooldown_until', r['finished'])
        self.assertEqual(Coupon.objects.filter(source='game').count(), 1)

    def test_guest_can_claim_after_sign_in_and_others_cannot_play_their_game(self):
        token, q = self.start('ke-ami')
        play = Play.objects.get(token=token)
        stranger = self.client_class()
        self.assertEqual(stranger.post(f'/games/play/{token}/', '{}', content_type='application/json').status_code, 404)
        for cid in play.cards:
            play.refresh_from_db()
            answer = GameCard.objects.get(pk=cid).title
            r = self.post(f'/games/play/{token}/', {'action': 'guess', 'option': play.state['options'].index(answer)}).json()
        self.assertTrue(r['finished']['needs_login'])
        User.objects.create_user('bo', 'bo@example.com', 'pw-12345678')
        self.client.login(username='bo', password='pw-12345678')
        d = self.post(f'/games/play/{token}/claim/').json()
        self.assertTrue(d['coupon']['code'].startswith('KHELA-'))
        play.refresh_from_db()
        self.assertEqual(play.user.username, 'bo')

    def test_whoami_extra_clue_costs_points(self):
        token, q = self.start('ke-ami')
        self.assertEqual(len(q['clues']), 1)
        d = self.post(f'/games/play/{token}/', {'action': 'clue'}).json()
        self.assertEqual(len(d['question']['clues']), 2)
        self.assertEqual(d['question']['worth'], 20)
