"""File containing Infoscreen models."""

from datetime import datetime

from django.db import models
from django import forms
from django.utils import timezone
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.utils.translation import gettext as _


class InfoItem(models.Model):
    """Abstract model representing single Infoscreen item."""

    class __meta__:
        abstract = True

    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255)
    # expire_date = None means never expiring item
    expire_date = models.DateTimeField(blank=True, null=True)
    display_name = "Default item"

    def get_template_url(self):
        """Get infoscreen template url."""
        raise NotImplementedError("inheriting classes must implement get_template_url")

    @staticmethod
    def get_create_template_url():
        """Get create infoscreen template url command."""
        raise NotImplementedError(
            "inheriting classes must implement get_create_template_url"
        )

    @classmethod
    def create_from_dict(cls, d):
        """Convert given dict to model."""
        item = cls()
        item.update_from_dict(d)
        return item

    def update_from_dict(self, d):
        """Update model based on given dict."""
        try:
            expire_date = d.pop("expire_date", None)
            self.expire_date = datetime.strptime(expire_date, "%Y-%m-%d %H:%M:%S")
        except:
            pass

        dmap = {
            "name": "name",
        }
        for k, v in d.items():
            try:
                self.__setattr__(dmap[k], v)
            except KeyError:
                pass
        self.save()

    def get_dict(self):
        """Convert django model to dict and return it."""
        return {
            "id": self.id,
            "name": self.name,
            "item_type": ContentType.objects.get_for_model(self).id,
            "template_url": self.get_template_url(),
            "display_name": self.display_name,
            "create_template_url": self.get_create_template_url(),
            "options": {},
        }

    def delete(self):
        """Delete infoinstance object."""
        # since generic foreign keys suck, delete info
        # items pointing here manually
        InfoInstance.objects.filter(
            item_id=self.id, item_type=ContentType.objects.get_for_model(self)
        ).delete()
        super().delete()

    @classmethod
    def get_subclasses(cls):
        """Get item subclasses."""
        for subclass in cls.__subclasses__():
            yield from subclass.get_subclasses()
            yield subclass

    def __str__(self):
        """Return class name."""
        return self.name


class ABBInfoItem(InfoItem):
    """Class for ABB Infoscreen item."""

    display_name = _("ABB jobs")

    def get_template_url(self):
        """Return ABB infoitem template url."""
        return "/static/infoscreen/html/abb.html"

    @staticmethod
    def get_create_template_url():
        """Call create ABB infoitem template url command."""
        return "/static/infoscreen/html/abb_create.html"


class ApyInfoItem(InfoItem):
    """Class for APY Infoscreen item."""

    display_name = _("APY Item")

    def get_template_url(self):
        """Return APY infoitem template url."""
        return "/static/infoscreen/html/apy.html"

    @staticmethod
    def get_create_template_url():
        """Call create APY infoitem template url command."""
        return "/static/infoscreen/html/apy_create.html"


class ExternalWebsiteInfoItem(InfoItem):
    """Class for external website info item."""

    display_name = _("External website")
    url = models.URLField()

    def get_template_url(self):
        """Return external website infoitem template url."""
        return "/static/infoscreen/html/external_website.html?url={}".format(self.name)

    @staticmethod
    def get_create_template_url():
        """Call create external website infoitem template url command."""
        return "/static/infoscreen/html/external_website_create.html"

    def get_dict(self):
        """Convert django model to dict and return it."""
        d = super().get_dict()
        d["options"] = {"url": self.url}
        return d

    @classmethod
    def create_from_dict(cls, d):
        """Convert given dict to model."""
        item = cls()
        item.update_from_dict(d)
        return item

    def get_list(self):
        """Return list containing infoitem data."""
        return {
            "id": self.id,
            "name": self.name,
            "url": self.url,
        }

    def update_from_dict(self, d):
        """Update model based on given dict."""
        try:
            expire_date = d.pop("expire_date", None)
            self.expire_date = datetime.strptime(expire_date, "%Y-%m-%d %H:%M:%S")
        except:
            pass

        dmap = {
            "name": "name",
            "url": "url",
        }
        for k, v in d.items():
            try:
                self.__setattr__(dmap[k], v)
            except KeyError:
                pass
        self.save()


class SossoInfoItem(InfoItem):
    """Class for Sosso Infoscreen item."""

    display_name = _("Sössö articles")

    def get_template_url(self):
        """Return Sosso infoitem template url."""
        return "/static/infoscreen/html/sosso.html"

    @staticmethod
    def get_create_template_url():
        """Call create Sosso infoitem template url command."""
        return "/static/infoscreen/html/sosso_create.html"


class LunchItem(InfoItem):
    """Class for Lunch Infoscreen item."""

    display_name = _("Today's lunch")

    def get_template_url(self):
        return "/static/infoscreen/html/lunch.html"

    @staticmethod
    def get_create_template_url():
        return "/static/infoscreen/html/lunch_create.html"


class EventInfoItem(InfoItem):
    """Class for Event Infoscreen item."""

    display_name = _("Events")

    def get_template_url(self):
        """Return Event infoitem template url."""
        return "/static/infoscreen/html/events.html"

    @staticmethod
    def get_create_template_url():
        """Call create Event infoitem template url command."""
        return "/static/infoscreen/html/events_create.html"


class ImageInfoItem(InfoItem):
    """Class for Image Infoscreen item."""

    display_name = _("Image")
    img = models.ImageField(upload_to="infoimages/")

    def get_template_url(self):
        """Return Image infoitem template url."""
        # get param to avoid angular from optimizing same template
        # with different options
        return "/static/infoscreen/html/generic_image.html?img={}".format(self.name)

    @staticmethod
    def get_create_template_url():
        """Call create Image infoitem template url command."""
        return "/static/infoscreen/html/generic_image_create.html"

    def get_dict(self):
        """Convert django model to dict and return it."""
        d = super().get_dict()
        d["options"] = {"img": self.img.url}
        return d


class VideoInfoItem(InfoItem):
    """Class for Video Infoscreen item."""

    display_name = "Video"
    video = models.FileField(upload_to="infovideos/")

    def get_template_url(self):
        """Return Video infoitem template url."""
        return "/static/infoscreen/html/generic_video.html?video={}".format(self.name)

    @staticmethod
    def get_create_template_url():
        """Call create Video infoitem template url command."""
        return "/static/infoscreen/html/generic_video_create.html"

    def get_dict(self):
        """Convert django model to dict and return it."""
        d = super().get_dict()
        d["options"] = {"video": self.video.url}
        return d


class ExternalImageInfoItem(InfoItem):
    """Class for External Image Infoscreen item."""

    display_name = _("External image")
    url = models.URLField()

    def get_template_url(self):
        """Return External Image infoitem template url."""
        return "/static/infoscreen/html/generic_image.html?img={}".format(self.name)

    @staticmethod
    def get_create_template_url():
        """Call create External Image infoitem template url command."""
        return "/static/infoscreen/html/generic_external_image_create.html"

    def get_dict(self):
        """Convert django model to dict and return it."""
        d = super().get_dict()
        d["options"] = {"img": self.url}
        return d

    @classmethod
    def create_from_dict(cls, d):
        """Convert given dict to model."""
        item = cls()
        item.update_from_dict(d)
        return item

    def update_from_dict(self, d):
        """Update model based on given dict."""
        try:
            expire_date = d.pop("expire_date", None)
            self.expire_date = datetime.strptime(expire_date, "%Y-%m-%d %H:%M:%S")
        except:
            pass

        dmap = {
            "name": "name",
            "url": "url",
        }
        for k, v in d.items():
            try:
                self.__setattr__(dmap[k], v)
            except KeyError:
                pass
        self.save()


class InfoInstance(models.Model):
    """Class for Info instance in Infoscreen."""

    id = models.AutoField(primary_key=True)
    rotation = models.ForeignKey(
        "Rotation", related_name="instances", on_delete=models.CASCADE
    )
    duration = models.FloatField(default=15.0)  # seconds
    # generic relation to some kind of InfoItem
    item_id = models.PositiveIntegerField()
    item_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    item = GenericForeignKey("item_type", "item_id")

    @classmethod
    def create_from_dict(cls, d):
        """Convert given dict to model."""
        try:
            rotation = Rotation.objects.get(pk=int(d["rotation_id"]))
            ct = ContentType.objects.get_for_id(int(d["item_type"]))
            item = ct.get_object_for_this_type(pk=int(d["item_id"]))
            duration = float(d["duration"])
        except:
            raise RuntimeError("invalid parameters supplied supplied")
        try:
            return cls.objects.create(rotation=rotation, item=item, duration=duration)
        except:
            raise RuntimeError("error while adding instance to db")

    def get_dict(self):
        """Convert django model to dict and return it."""
        return {
            "id": self.id,
            "item": self.item.get_dict(),
            "duration": self.duration,
        }

    def __str__(self):
        """Return model name."""
        return "{}: {} ({}s)".format(self.rotation.name, self.item.name, self.duration)


class Rotation(models.Model):
    """Class for rotation model."""

    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=255)

    def get_dict(self):
        """Convert django model to dict and return it."""
        # exclude expired items from rotation (note: using tricky syntax
        # to avoid excluding items with no expire_date)
        now = timezone.now()
        instances = self.instances.all()
        filtered = filter(lambda i: (i.item.expire_date or now) >= now, list(instances))
        instance_list = list(map(lambda i: i.get_dict(), filtered))

        return {
            "id": self.id,
            "name": self.name,
            "instances": instance_list,
        }

    def get_list(self):
        """Return list containing infoitem data."""
        return {
            "id": self.id,
            "name": self.name,
        }

    def __str__(self):
        """Return model name."""
        return self.name


class ImageUploadForm(forms.Form):
    """Form used to handle imageuploads to infoscreen app."""

    id = models.AutoField(primary_key=True)
    name = forms.CharField()
    image = forms.ImageField()


class UploadFileForm(forms.Form):
    """Form used for uploading file."""

    id = models.AutoField(primary_key=True)
    name = forms.CharField()
    video = forms.FileField()
