from django.db import models
from django.contrib.auth.models import User
from django.db.models import Q
import logging, yaml, pytz, json, os
from django.conf import settings
from datetime import datetime

logger = logging.getLogger(__name__)


class MinecraftManagerUser(User):

    class Meta:
        proxy = True
        permissions = (
            ('coreprotect_partial', 'Can use CoreProtect GUI except Command/Chat searches'),
            ('coreprotect_full', 'Can use full CoreProtect GUI'),
            ('coreprotect_activity', 'Can use CoreProtect Activity Monitor'),
            ('bots', 'Can use the bot control page'),
            ('chat', 'Can use chat page'),
        )


class UserSettings(models.Model):
    RESULT_OPTIONS = (
        (10, '10'),
        (25, '25'),
        (50, '50'),
        (100, '100'),
        (-1, 'All')
    )
    THEME_OPTIONS = (
        ('DE', 'Default'),
        ('DA', 'Dark'),
        ('SO', 'Solar'),
        ('SL', 'Slate')
    )
    tzs = []
    for tz in pytz.common_timezones:
        tzs.append((tz, tz))
    TIMEZONES = tuple(tzs)
    auth_user = models.OneToOneField(User, on_delete=models.CASCADE, null=True, blank=True)
    default_results = models.SmallIntegerField("Default Results", default=10, choices=RESULT_OPTIONS)
    default_theme = models.CharField("Theme", max_length=2, default='DE', choices=THEME_OPTIONS)
    default_timezone = models.CharField("Timezone", max_length=20, default='UTC', choices=TIMEZONES)
    search_player_ip = models.BooleanField("Include IP in Player search", default=False)
    show_timestamp_chat = models.BooleanField("Show Timestamp By Chat", default=False)
    last_ip = models.CharField(max_length=30, default="127.0.0.1", editable=False)

    class Meta:
        verbose_name = "User Settings"
        verbose_name_plural = "User Settings"

    def __str__(self):
        return self.auth_user.username

    def __repr__(self):
        return self.auth_user.username


class Application(models.Model):
    username = models.CharField("Minecraft Username", max_length=20, unique=True)
    age = models.PositiveSmallIntegerField()
    player_type = models.TextField("What type of player are you?", max_length=300)
    ever_banned = models.BooleanField("Have you ever been banned?", default=False)
    ever_banned_explanation = models.TextField("If you were previously banned, will you share why?", max_length=300, blank=True, null=True)
    reference = models.CharField("Were you referred to our server?", max_length=50, blank=True, null=True)
    read_rules = models.CharField("Have you read the rules?", max_length=10)
    accepted = models.NullBooleanField()
    date = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    objects = models.Manager()

    @property
    def status(self):
        if self.accepted is not None:
            if self.accepted is True:
                return "Accepted"
            elif self.accepted is False:
                return "Denied"
            else:
                return "Unknown"
        else:
            return "Unanswered"

    @property
    def date_display(self):
        return str(self.date).split(".")[0]

    def __str__(self):
        return self.username

    def __repr__(self):
        return self.username


class Player(models.Model):
    auth_user = models.OneToOneField(User, on_delete=models.SET_NULL, null=True, blank=True)
    uuid = models.CharField(max_length=36, unique=True)
    username = models.CharField(max_length=20)
    application = models.ForeignKey(Application, on_delete=models.SET_NULL, null=True, blank=True)
    first_seen = models.DateField(null=True, blank=True)
    last_seen = models.DateField(null=True, blank=True)


    @property
    def is_banned(self):
        ban_file = os.path.join(settings.MINECRAFT_BASE_DIR, 'banned-players.json')
        with open(ban_file, encoding='utf-8') as f:
            bans = json.load(f)
        if bans:
            for ban in bans:
                if self.uuid == ban['uuid']:
                    return True
            return False
        else:
            return False

    @property
    def donor_status(self):

        def format_time(timestamp):
            return datetime.utcfromtimestamp(timestamp).strftime('%m/%d/%Y')

        try:
            from django_luckperms.models import LuckPermsPlayerPermissions as lppp
            donator = lppp.objects.filter(permission='group.donator', uuid=self.uuid)
            if donator.exists():
                expiry = donator.first().expiry
                if expiry > 0:
                    return "Until {}".format(format_time(expiry))
                else:
                    return "Permanent"
            else:
                return "None"
        except:
            pass
        try:
            pex_file = os.path.join(getattr(settings, 'MINECRAFT_BASE_DIR', ''), 'plugins/PermissionsEx/permissions.yml')
            with open(pex_file) as f:
                yml = yaml.load(f)
            for user in yml['users']:
                if user == self.uuid:
                    if 'donator' in yml['users'][user]['group']:
                        u = yml['users'][user]
                        if 'group-donator-until' in u['options']:
                            unix_time = float(u['options']['group-donator-until'])
                            return 'Until {0}'.format(format_time(unix_time))
                        else:
                            return 'Permanent'
                    else:
                        return 'None'
        except:
            pass
        return "N/A"

    @property
    def ips(self):
        ips = []
        if not getattr(self.auth_user, 'is_active', False):
            query = IP.objects.filter(player=self)
            for q in query:
                ips.append(q.ip)
        return " ".join(ips)

    def __str__(self):
        return "{} ({})".format(self.username, self.uuid)


class Ticket(models.Model):
    PRIORITY = (
        ('L', 'Low'),
        ('M', 'Medium'),
        ('H', 'High')
    )
    WORLDS = (
        ('O', 'Overworld'),
        ('N', 'Nether'),
        ('E', 'The End')
    )
    player = models.ForeignKey(Player, related_name="ticket_player", on_delete=models.CASCADE, blank=True, null=True)
    message = models.CharField(max_length=500)
    priority = models.CharField(max_length=1, choices=PRIORITY, blank=True, default='L')
    staff = models.ForeignKey(User, related_name="ticket_staff", on_delete=models.CASCADE, null=True, blank=True)
    resolved = models.BooleanField(default=False)
    world = models.CharField(max_length=1, choices=WORLDS, blank=True, null=True)
    x = models.CharField(max_length=20, blank=True, null=True)
    y = models.CharField(max_length=20, blank=True, null=True)
    z = models.CharField(max_length=20, blank=True, null=True)
    date = models.DateTimeField(auto_now_add=True, null=True, blank=True)
    objects = models.Manager()

    @property
    def location(self):
        return "{0}, {1}, {2} in {3}".format(self.x, self.y, self.z, self.world_label)

    @property
    def world_label(self):
        for W in self.WORLDS:
            if W[0] == self.world:
                return W[1]
        return ""

    @property
    def resolved_label(self):
        if self.resolved:
            return "Resolved"
        else:
            return "Unresolved"

    @property
    def snippet(self):
        if len(self.message) > 50:
            return self.message[:50] + "..."
        else:
            return self.message

    @property
    def issuer(self):
        if self.player:
            pl = Player.objects.get(id=self.player_id)
            return pl.username
        else:
            return "Unknown"

    @property
    def claimed_by(self):
        if self.staff:
            pl = User.objects.get(id=self.staff_id)
            return pl.username
        else:
            return "Unclaimed"

    @property
    def is_claimed(self):
        if self.staff:
            return True
        else:
            return False

    @property
    def priority_display(self):
        for P in self.PRIORITY:
            if P[0] == self.priority:
                return P[1]
        return "Unknown"

    @staticmethod
    def priority_code_to_display(piority_code):
        for P in Ticket.PRIORITY:
            if P[0] == piority_code:
                return P[1]
        return "Unknown"

    @property
    def date_display(self):
        return str(self.date).split(".")[0]

    def __str__(self):
        return "{}: {}".format(self.issuer, self.snippet)


class TicketNote(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
    message = models.TextField(max_length=1000)
    last_update = models.DateTimeField(auto_now_add=True, blank=True, null=True)
    date = models.DateTimeField(auto_now_add=True, blank=True, null=True)

    class Meta:
        verbose_name = "Ticket Note"
        verbose_name_plural = "Ticket Notes"

    def __str__(self):
        return "Ticket: {0}".format(self.ticket.snippet)


class Note(models.Model):
    IMPORTANCE = (
        ('L', 'Low'),
        ('M', 'Medium'),
        ('H', 'High')
    )
    player = models.ForeignKey(Player, related_name="note_player", on_delete=models.CASCADE)
    message = models.CharField(max_length=200)
    importance = models.CharField(max_length=1, choices=IMPORTANCE)
    staff = models.ForeignKey(User, related_name="note_staff", on_delete=models.CASCADE, null=True, blank=True, limit_choices_to=Q(is_active=True))
    date = models.DateTimeField(auto_now_add=True, blank=True, null=True)

    @property
    def snippet(self):
        if len(self.message) > 50:
            return self.message[:50] + "..."
        else:
            return self.message

    @property
    def importance_display(self):
        for S in self.IMPORTANCE:
            if S[0] == self.importance:
                return S[1]
        return "Unknown"

    @staticmethod
    def importance_code_to_display(importance_code):
        for S in Note.IMPORTANCE:
            if S[0] == importance_code:
                return S[1]
        return "Unknown"

    @property
    def issuer(self):
        if self.staff:
            pl = User.objects.get(id=self.staff_id)
            return pl.username
        else:
            return "System"

    @property
    def issuee(self):
        if self.player:
            pl = Player.objects.get(id=self.player_id)
            return pl.username
        else:
            return "Unknown"

    @property
    def date_display(self):
        return str(self.date).split(".")[0]

    def __str__(self):
        return "{}: {}".format(self.issuee, self.snippet)


class IPManager(models.Manager):
    def get_queryset(self):
        users = User.objects.filter(is_active=True)
        filtered = []
        for user in users:
            if getattr(user, 'player', False):
                ips = IP.objects.filter(player=user.player)
                for ip in ips:
                    filtered.append(ip.ip)
        return super(IPManager, self).get_queryset().exclude(ip__in=filtered)


class IP(models.Model):
    player = models.ForeignKey(Player, on_delete=models.CASCADE)
    ip = models.CharField(max_length=30)
    last_used = models.DateField(null=True, blank=True)
    objects = models.Manager()
    api = IPManager()

    class Meta:
        verbose_name = "IP"
        verbose_name_plural = "IPs"

    @property
    def last_used_formatted(self):
        if self.last_used:
            return self.last_used
        else:
            return "N/A"

    @property
    def associated(self):
        ips = IP.api.filter(ip=self.ip)
        players = []
        for ip in ips:
            if self.player != ip.player:
                players.append(ip.player)
        if players:
            return players
        else:
            return None

    def __str__(self):
        player = Player.objects.get(id=self.player_id)
        return "{}: {}".format(player.username, self.ip)


class Alert(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    message = models.TextField(max_length=1000)
    seen = models.BooleanField(default=False)
    date = models.DateTimeField(auto_now_add=True, blank=True, null=True)

    @property
    def snippet(self):
        if len(self.message) > 50:
            return self.message[:50] + "..."
        else:
            return self.message

    def __str__(self):
        return "Alert for {}".format(self.user.username)




