import logging

from django.db import models
from django.core.urlresolvers import reverse_lazy
from django.core.serializers import serialize
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _
from django.template.defaultfilters import filesizeformat as fmt
from django.conf import settings

from tictactoe.tools.validators import ByteLengthValidator
from tictactoe.tools.models import OwnManager

from . import fixtures


logger = logging.getLogger(__name__)


class Entry(models.Model):
    _max_len = ByteLengthValidator(settings.MAX_CODE_SIZE)

    user = models.ForeignKey(User)
    code = models.TextField(validators=[_max_len])

    uploaded = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    objects = models.Manager()
    own = OwnManager('user')

    @property
    def codesize(self):
        return len(self.code.encode('utf8'))

    def get_absolute_url(self):
        return reverse_lazy('tictactoe.contest.views.entry', args=[self.id])

    def __str__(self):
        return "<Entry %d by %s (%s)>" % (
            self.id, self.user, fmt(self.codesize))

    def add_latest(self):
        """After qualifying, make it LatestEntry of this user"""
        latest = LatestEntry.objects.filter(user=self.user).first()
        if latest is None:
            LatestEntry(user=self.user, entry=self).save()
        else:
            latest.entry = self
            latest.save()

    def qualify(self):
        """Scheulde a qualification with dumb_player and maybe go compete"""
        from .tasks import schedule_qualification
        logger.info("Scheduling %s for qualification" % self)
        schedule_qualification.delay(serialize('json', [self]))

    @staticmethod
    def qualification_entry():
        u = User.objects.get(username='Qualification-Bot')
        return Entry.objects.get(user=u)

    class Meta:
        verbose_name_plural = 'Entries'
        ordering = '-uploaded',


class LatestEntry(models.Model):
    user = models.ForeignKey(User)
    entry = models.ForeignKey(Entry)

    def calc_results(self):
        r1 = Fight.objects.filter(
            x__latestentry=self, o__latestentry__isnull=False
            ).values('result').annotate(cnt=models.Count('result'))
        r2 = Fight.objects.filter(
            o__latestentry=self, x__latestentry__isnull=False
            ).values('result').annotate(cnt=models.Count('result'))
        d1 = dict(((d['result'], d['cnt']) for d in r1))
        d2 = dict(((d['result'], d['cnt']) for d in r2))
        win = d1.get('win', 0) + d2.get('loss', 0)
        draw = d1.get('draw', 0) + d2.get('draw', 0)
        loss = d1.get('loss', 0) + d2.get('win', 0)
        return win, draw, loss

    def __str__(self):
        return "<LatestEntry by %s>" % (self.user)

    class Meta:
        verbose_name_plural = 'Latest Entries'


class Fight(models.Model):
    """e1 is x, e2 is o. Results relative to e1.

    Each Entry has two Fight objects: one with x, one with o"""

    FIGHT_RESULT = (
        ('win', _('win')),
        ('draw', _('draw')),
        ('loss', _('loss')),
    )

    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)
    x = models.ForeignKey(Entry, related_name='e1')
    o = models.ForeignKey(Entry, related_name='e2')

    # len(",".join(map(str, range(1, 81)))) == 230
    # Zero signifies error and in case of error is always the last number
    gameplay = models.CommaSeparatedIntegerField(
        max_length=230,
        help_text=_(
            "Gameplay flow. Board is separated to 81 cells. Each "
            "number means a move by alternating player. For example, "
            "'10,1,0' means: x placed (2,1,1,1), o placed (1,1,1,1) and "
            "x made an error. In case 0 is at the end (like in the example), "
            "'error' field is non-empty.")
    )

    error = models.CharField(
        max_length=255, blank=True,
        help_text=_("Non-empty if `gameplay' ends with zero")
    )

    result = models.CharField(
        max_length=10, choices=FIGHT_RESULT,
        help_text=_("Fight result of x (e1) versus o (e2). Relative to e1.")
    )

    def result_of_x(self):
        return self.result

    def result_of_o(self):
        if self.result == 'draw':
            return 'draw'
        return self.result == 'loss' and 'win' or 'win'

    class Meta:
        index_together = (
            ('x', 'result'),
            ('o', 'result'),
        )
        unique_together = ('x', 'o')

    @staticmethod
    def from_compete(x, o, round_result):
        """Creates Fight instance from output of tictactoelib.compete"""
        fight, _created = Fight.objects.get_or_create(x=x, o=o)
        if round_result[0] == 'ok':
            _, xodraw, gameplay = round_result
            res = 'draw'
            if xodraw == 'x' or xodraw == 'o':
                res = xodraw == 'x' and 'win' or 'loss'
            fight.gameplay = gameplay
            fight.result = res
            return fight
        elif round_result[0] == 'error':
            _, xo, reason, gameplay = round_result
            res = 'win' if xo == 'o' else 'loss'
            fight.gameplay = gameplay
            fight.error = reason
            fight.result = res
            return fight

    def get_absolute_url(self):
        return reverse_lazy('tictactoe.contest.views.fight', args=[self.id])

    def __str__(self):
        return "<Fight %s vs %s. Result: %s>" % (self.x, self.o, self.result)


models.signals.post_migrate.connect(fixtures.qualification_bot)
