import json
import logging
import uuid
import urllib.parse

from django.db import models
from datetime import timedelta
from django.utils import timezone
from django.utils.text import slugify
from django.conf import settings
from channels import Group
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth.models import User
from django.core.mail import send_mail
from django.db.utils import OperationalError

import radio.choices as choice

from pinax.stripe.models import Plan as pinax_Plan

log = logging.getLogger(__name__)

class Agency(models.Model):
    name = models.CharField(max_length=100)
    short = models.CharField(max_length=5, unique=True)

    def __str__(self):
        return self.name

    def get_short(self):
        return self.short


class City(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(null=True, blank=True)
    url = models.URLField(max_length=400, null=True, blank=True)
    google_maps_url = models.URLField(max_length=400, null=True, blank=True)
    fire_service = models.ForeignKey(Agency, related_name='fire_service', null=True, blank=True, on_delete=models.CASCADE)
    police_service = models.ForeignKey(Agency, related_name='police_service', null=True, blank=True, on_delete=models.CASCADE)
    ems_service = models.ForeignKey(Agency, related_name='ems_service', null=True, blank=True, on_delete=models.CASCADE)
    visible = models.BooleanField(default=True)

    class Meta:
        ordering = ["name"]
    
    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        self.slug = slugify(self.name)
        super(City, self).save(*args, **kwargs)


class System(models.Model):
    name = models.CharField(max_length=100, db_index=True, unique=True)
    system_id = models.CharField(max_length=20, blank=True, null=True)

    def __str__(self):
        return self.name


class Source(models.Model):
    description = models.CharField(max_length=100, db_index=True, unique=True)

    class Meta:
        ordering = ["pk"]

    def __str__(self):
        return self.description

class Unit(models.Model):
    dec_id = models.IntegerField()
    description = models.CharField(max_length=100, blank=True, null=True)
    agency = models.ForeignKey(Agency, default=settings.RADIO_DEFAULT_UNIT_AGENCY)
    #agency = models.ForeignKey(Agency, default=2)
    type = models.CharField(max_length=1, choices=choice.RADIO_TYPE_CHOICES, default=choice.RADIO_TYPE_MOBILE)
    number = models.IntegerField(default=1)
    system = models.ForeignKey(System, default=0)
    slug = models.SlugField(null=True, blank=True)

    class Meta:
        unique_together = ('dec_id', 'system',)

    def __str__(self):
        if(self.description):
            return self.description
        else:
            return str(self.dec_id)

    def save(self, *args, **kwargs):
        if self.description:
            self.slug = slugify(self.description)
        else:
            self.slug = slugify(self.dec_id)
        super(Unit, self).save(*args, **kwargs)


class RepeaterSite(models.Model):
    name = models.CharField(max_length=100, unique=True)
    description = models.CharField(max_length=500, blank=True, null=True)

    def __str__(self):
        return self.name


class Service(models.Model):
    name = models.CharField(max_length=100, unique=True)
    description = models.CharField(max_length=500, blank=True, null=True)

    def __str__(self):
        return self.name


class TalkGroup(models.Model):
    dec_id = models.IntegerField()
    alpha_tag = models.CharField(max_length=30)
    common_name = models.CharField(max_length=10, blank=True, null=True)
    description = models.CharField(max_length=100, blank=True, null=True)
    slug = models.SlugField(null=True)
    public = models.BooleanField(default=True)
    comments = models.CharField(max_length=100, blank=True, null=True)
    system = models.ForeignKey(System, default=0)
    mode = models.CharField(max_length=1, choices=choice.TG_MODE_CHOICES, default=choice.TG_MODE_DIGITAL, help_text='mode used by trunk-recorder')
    priority = models.IntegerField(default=3, help_text='record priority used by trunk-recorder')
    _home_site = models.ForeignKey(RepeaterSite, blank=True, null=True)
    _service_type = models.ForeignKey(Service, blank=True, null=True)
    last_transmission = models.DateTimeField()
    recent_usage = models.IntegerField(default=0)
    play_source = models.ForeignKey(Source, blank=True, null=True, help_text='default record source for playback')

    class Meta:
        ordering = ["alpha_tag"]
        unique_together = ('dec_id', 'system',)

    def __str__(self):
        return self.alpha_tag

    def save(self, *args, **kwargs):
        self.slug = slugify(self.alpha_tag)
        if not self.last_transmission:
            self.last_transmission = timezone.now()
        super(TalkGroup, self).save(*args, **kwargs)

    def get_absolute_url(self):
        return '/tg/{}/'.format(self.slug)

    @property
    def home_site(self):
        return self._home_site

    @home_site.setter
    def home_site(self, value):
        hs, created = RepeaterSite.objects.get_or_create(name=value)
        self._home_site = hs

    @property
    def service_type(self):
        return self._service_type

    @service_type.setter
    def service_type(self, value):
        st, created = Service.objects.get_or_create(name=value)
        self._service_type = st


def AddToDefaultAccessGroup(sender, instance, created, **kwargs):
    if created:
        new_tg = TalkGroupWithSystem.objects.get(pk=instance.pk)
        for access_group in TalkGroupAccess.objects.filter(default_new_talkgroups=True):
            access_group.talkgroups.add(new_tg)
            access_group.save()


post_save.connect(AddToDefaultAccessGroup, sender=TalkGroup)


class TalkGroupWithSystem(TalkGroup):
    class Meta:
        proxy = True

    def __str__(self):
        return "{} ({})".format(self.alpha_tag, self.system)


class Transmission(models.Model):
    slug = models.UUIDField(db_index=True, default=uuid.uuid4, editable=False) 
    start_datetime = models.DateTimeField(db_index=True)
    end_datetime = models.DateTimeField(null=True, blank=True)
    audio_file = models.FileField()
    audio_file_type = models.CharField(max_length=3, null=True, default='mp3')
    audio_file_url_path = models.CharField(max_length=100, default='/')
    talkgroup = models.IntegerField()
    talkgroup_info = models.ForeignKey(TalkGroup)
    freq = models.IntegerField()
    emergency = models.BooleanField(default=False)
    units = models.ManyToManyField(Unit, through='TranmissionUnit')
    play_length = models.FloatField(default=0.0)
    source = models.ForeignKey(Source, default=0)
    system = models.ForeignKey(System, default=0)
    from_default_source = models.BooleanField(default=True)
    has_audio = models.BooleanField(default=True)

    def __str__(self):
        return '{} {}'.format(self.talkgroup, self.start_datetime)

    def get_absolute_url(self):
        return '/audio/{}/'.format(self.slug)

    @property
    def local_start_datetime(self):
        return timezone.localtime(self.start_datetime).strftime('%H:%M:%S %m/%d/%Y')

    def as_dict(self):
        return {'start_datetime': str(self.start_datetime), 
                'audio_file': str(self.audio_file), 
                'talkgroup_desc': str(self.talkgroup_info.alpha_tag),
                'talkgroup_dec_id' : str(self.talkgroup_info.dec_id),
                'audio_url': str("{}{}.{}".format(settings.AUDIO_URL_BASE, self.audio_file, self.audio_file_type)),
               }


    def print_play_length(self):
        m, s = divmod(int(self.play_length), 60)
        return '{:02d}:{:02d}'.format(m,s)

    def freq_mhz(self):
        return '{0:07.3f}'.format(self.freq / 1000000)

    def tg_name(self):
        """Returns TalkGroup name used for page title
           First tries TalkGroup.common_name
           then TalkGroup.alpha_tag
        """
        if self.talkgroup_info.common_name:
            return self.talkgroup_info.common_name
        else:
            return self.talkgroup_info.alpha_tag

    def is_playable(self, user):
        """If the user can play this transmission
        """
        return True

    def _get_user_profile(self, user):
        if user.is_authenticated():
            user_profile = Profile.objects.get(user=user)
        else:
            try:
                anon_user = User.objects.get(username='ANONYMOUS_USER')
            except User.DoesNotExist:
                raise ImproperlyConfigured('ANONYMOUS_USER is missing from User table, was "./manage.py migrations" not run?')
            user_profile = Profile.objects.get(user=anon_user)
        return user_profile



    def _get_history_allow(self,user):
        user_profile = self._get_user_profile(user)
        if user_profile:
            history_minutes = user_profile.plan.history
        else:
            history_minutes = settings.ANONYMOUS_TIME
        return history_minutes


    def audio_file_history_check(self, user):
        history_minutes = self._get_history_allow(user)
        if history_minutes > 0:
            time_threshold = timezone.now() - timedelta(minutes=history_minutes)
            if self.start_datetime < time_threshold:
                return None
        return str(self.audio_file)

    @property
    def audio_url(self):
        base_path = settings.AUDIO_URL_BASE
        return urllib.parse.urljoin(base_path, self.audio_file_url_path.lstrip('/'))

    class Meta:
        ordering = ["-pk"]
        permissions = (
            ('download_audio', 'Can download audio clips'),
        )

    def save(self, *args, **kwargs):
        if settings.FIX_AUDIO_NAME:
            file_name = str(self.audio_file)
            self.audio_file = file_name.replace('+', '%2B')
        if self.talkgroup_info.play_source is not None and \
               self.talkgroup_info.play_source != self.source:
           self.from_default_source = False
        else:
           self.from_default_source = True
        super(Transmission, self).save(*args, **kwargs)


@receiver(post_save, sender=Transmission, dispatch_uid="send_mesg")
def send_mesg(sender, instance, **kwargs):
    #log.debug('Hit post save()')
    #log.debug('DATA %s', json.dumps(instance.as_dict()))
    #log.error('DATA %s', json.dumps(instance.as_dict()))
    tg = TalkGroup.objects.get(pk=instance.talkgroup_info.pk)
    tg.last_transmission = timezone.now()
    tg.save()
    groups = tg.scanlist_set.all()
    for g in groups:
        Group('livecall-scan-'+g.slug, ).send({'text': json.dumps(instance.as_dict())})
    Group('livecall-tg-' + tg.slug, ).send({'text': json.dumps(instance.as_dict())})
    # Send notification to default group all the time
    Group('livecall-scan-default').send({'text': json.dumps(instance.as_dict())})



    #def save(self, *args, **kwargs):
    #    try:
    #        self.talkgroup_info = TalkGroup.objects.get(dec_id=self.talkgroup)
    #    except TalkGroup.DoesNotExist:
    #        new_tg = TalkGroup(dec_id=self.talkgroup, alpha_tag='UNK')
    #        new_tg.save
    #        self.talkgroup_info = TalkGroup.objects.get(dec_id=self.talkgroup)
    #    super(Transmission, self).save(*args, **kwargs)

class TranmissionUnit(models.Model):
    transmission = models.ForeignKey(Transmission, on_delete=models.CASCADE)
    unit = models.ForeignKey(Unit, on_delete=models.CASCADE)
    order = models.IntegerField()

    class Meta:
        ordering = ["order"]

    def __str__(self):
        return '{} on {}'.format(self.unit,self.transmission)

class ScanList(models.Model):
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL)
    public = models.BooleanField(default=False)
    name = models.CharField(max_length=30, unique=True)
    description = models.CharField(max_length=100)
    talkgroups = models.ManyToManyField(TalkGroup)
    slug = models.SlugField(null=True, blank=True)

    class Meta:
        ordering = ["name"]

    def __str__(self):
        return '{}'.format(self.name)

    def save(self, *args, **kwargs):
        self.slug = slugify(self.name)
        super(ScanList, self).save(*args, **kwargs)

    def get_absolute_url(self):
        return '/scan/{}/'.format(self.slug)

class MenuList(models.Model):
    order = models.IntegerField(default=1)

    class Meta:
        abstract = True
        ordering = ["order", "name"]

    def __str__(self):
        return '{}'.format(self.name)

    @property
    def scan_name(self):
        return self.name

    @property
    def scan_description(self):
        return self.name.description

    @property
    def scan_slug(self):
        return self.name.slug

class MenuScanList(MenuList):
    name = models.ForeignKey(ScanList)

    @property
    def scan_name(self):
        return self.name.name

    @property
    def scan_description(self):
        return self.name.description


class MenuTalkGroupList(MenuList):
    name = models.ForeignKey(TalkGroupWithSystem)

    @property
    def tg_name(self):
        return self.name.alpha_tag

    @property
    def tg_slug(self):
        return self.name.slug

class TalkGroupAccess(models.Model):
    name = models.CharField(max_length=30, unique=True)
    talkgroups = models.ManyToManyField(TalkGroupWithSystem)
    default_group = models.BooleanField(default=False, help_text='new users get this group by default')
    default_new_talkgroups = models.BooleanField(default=False, help_text='new talkgroups are added to this group')
    

    class Meta:
        verbose_name_plural = 'talk group access'

    def __str__(self):
        return '{}'.format(self.name)


class Plan(models.Model):
    DEFAULT_PK = 1 # This is added via a migration
    name = models.CharField(max_length=30, unique=True)
    history = models.IntegerField(default=15, help_text='visible history in minutes')

    def __str__(self):
        return '{}'.format(self.name)


class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    plan = models.ForeignKey(Plan, default=Plan.DEFAULT_PK)
    talkgroup_access = models.ManyToManyField(TalkGroupAccess, blank=True)


class WebHtml(models.Model):
    name = models.CharField(max_length=30, unique=True)
    bodytext = models.TextField()
    
    def __str__(self):
        return self.name


class StripePlanMatrix(models.Model):
    name = models.CharField(max_length=30, unique=True)
    stripe_plan = models.ForeignKey(pinax_Plan)
    radio_plan = models.ForeignKey(Plan)
    active = models.BooleanField(default=True)
    order = models.IntegerField(default=99)

    class Meta:
        ordering = ["order"]


    def __str__(self):
       return self.name


    def stripe_amount(self):
       return int(self.stripe_plan.amount * 100)

    def history_days(self):
       return int(self.radio_plan.history / 1440)


class SiteOption(models.Model):
    name = models.CharField(max_length=30, unique=True)
    value = models.CharField(max_length=60)
    javascript_visible = models.BooleanField(default=False)
    template_visible = models.BooleanField(default=False)
    description = models.CharField(max_length=100, null=True, blank=True)

    def __str__(self):
        return self.name

    def value_boolean_or_string(self):
        if self.value.lower() == 'true':
            return True
        elif self.value.lower() == 'false':
            return False
        else:
            return self.value


def create_profile(sender, **kwargs):
    user = kwargs["instance"]
    if kwargs["created"]:
        default_plan = Plan.objects.get(pk=Plan.DEFAULT_PK)
        up = Profile(user=user, plan=default_plan)
        up.save()
        try:
            for tg in TalkGroupAccess.objects.filter(default_group=True):
                up.talkgroup_access.add(tg)
        except OperationalError:
            pass
        try:
            new_user_email = SiteOption.objects.get(name='SEND_ADMIN_EMAIL_ON_NEW_USER')
            if new_user_email.value_boolean_or_string() == True:
                send_mail(
                      'New {} User {}'.format(settings.SITE_TITLE, user.username),
                      'New User {} {} Username {} Email {} just registered'.format(user.first_name, user.last_name, user.username, user.email),
                      settings.SERVER_EMAIL,
                      [ mail for name, mail in settings.ADMINS],
                      fail_silently=False,
                     )
        except (SiteOption.DoesNotExist, OperationalError):
            pass

post_save.connect(create_profile, sender=User)

class Incident(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(null=True, blank=True)
    description = models.TextField(blank=True)
    transmissions = models.ManyToManyField(Transmission, blank=True)
    public = models.BooleanField(default=True)

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        if not self.slug:  # Dont change if already set
            self.slug = slugify(self.name)
        super(Incident, self).save(*args, **kwargs)


class MessagePopUp(models.Model):
    mesg_type = models.CharField(max_length=1, choices=choice.MESG_CHOICES, unique=True)
    mesg_html = models.TextField()
    active = models.BooleanField(default=True)

    def __str__(self):
        return self.mesg_html