import datetime
import os
import re
import resource
import string
from decimal import Decimal, getcontext
from subprocess import call
from urllib.parse import urljoin

getcontext().prec=2 #use 2 significant figures for decimal calculations

from django.conf import settings
from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.contenttypes.models import ContentType
from django.core.files.storage import FileSystemStorage
from django.db import models
from django.template import Context, loader
from django.urls import reverse

import settings
import troggle.core.models.survex
from troggle.core.utils import get_process_memory

"""This file declares TroggleModel which inherits from django.db.models.Model 
All TroggleModel and models.Model subclasses inherit persistence in the django relational database. This is known as
the django Object Relational Mapping (ORM).
There are more subclasses define in models_caves.py models_survex.py etc.
"""

class TroggleModel(models.Model):
    """This class is for adding fields and methods which all of our models will have.
    """
    new_since_parsing = models.BooleanField(default=False, editable=False)
    non_public = models.BooleanField(default=False)
    def object_name(self):
        return self._meta.object_name

    def get_admin_url(self):
        return urljoin(settings.URL_ROOT, "/admin/core/" + self.object_name().lower() + "/" + str(self.pk))

    class Meta:
        abstract = True

class DataIssue(TroggleModel):
    """When importing cave data any validation problems produce a message which is 
    recorded as a DataIssue. The django admin system automatically prodiuces a page listing 
    these at /admin/core/dataissue/
    This is a use of the NOTIFICATION pattern: 
    https://martinfowler.com/eaaDev/Notification.html
    
    We have replaced all assertions in the code with messages and local fix-ups or skips:
    https://martinfowler.com/articles/replaceThrowWithNotification.html
    
    See also the use of stash_data_issue() & store_data_issues() in parsers/survex.py which defer writing to the database until the end of the import.
    """
    date = models.DateTimeField(auto_now_add=True, blank=True)
    parser = models.CharField(max_length=50, blank=True, null=True)
    message = models.CharField(max_length=800, blank=True, null=True)
    url = models.CharField(max_length=300, blank=True, null=True) # link to offending object

    class Meta:
        ordering = ['date']

    def __str__(self):
        return f"{self.parser} - {self.message}"

# 
# single Expedition, usually seen by year
#
class Expedition(TroggleModel):
    year        = models.CharField(max_length=20, unique=True)
    name        = models.CharField(max_length=100)
    logbookfile = models.CharField(max_length=100, blank=True, null=True)
        
    def __str__(self):
        return self.year

    class Meta:
        ordering = ('-year',)
        get_latest_by = 'year'
    
    def get_absolute_url(self):
        return urljoin(settings.URL_ROOT, reverse('expedition', args=[self.year]))
    

class ExpeditionDay(TroggleModel):
    """Exists only on Expedition now. Removed links from logbookentry, persontrip, survex stuff etc.
    """
    expedition  = models.ForeignKey("Expedition",on_delete=models.CASCADE)
    date        = models.DateField()

    class Meta:
        ordering = ('date',)

    # def GetPersonTrip(self, personexpedition):
        # """returns all logbook trips for this expeditonday
        # """
        # personexpeditions = self.persontrip_set.filter(expeditionday=self)
        # return personexpeditions and personexpeditions[0] or None

class Person(TroggleModel):
    """single Person, can go on many years
    """
    first_name  = models.CharField(max_length=100)
    last_name   = models.CharField(max_length=100)
    fullname    = models.CharField(max_length=200)
    nickname    = models.CharField(max_length=200)
    is_vfho     = models.BooleanField(help_text="VFHO is the Vereines für Höhlenkunde in Obersteier, a nearby Austrian caving club.", default=False)
    mug_shot    = models.CharField(max_length=100, blank=True,null=True)
    blurb       = models.TextField(blank=True,null=True)
    orderref    = models.CharField(max_length=200)  # for alphabetic 

    def get_absolute_url(self):
        return urljoin(settings.URL_ROOT,reverse('person',kwargs={'first_name':self.first_name,'last_name':self.last_name}))

    class Meta:
        verbose_name_plural = "People"
        ordering = ('orderref',)  # "Wookey" makes too complex for: ('last_name', 'first_name') 
    
    def __str__(self):
        if self.last_name:
            return f"{self.first_name} {self.last_name}"
        return self.first_name


    def notability(self):
        """This is actually recency: all recent cavers, weighted by number of expos
        """
        notability = Decimal(0)
        max_expo_val = 0

        max_expo_year = Expedition.objects.all().aggregate(models.Max('year'))
        max_expo_val = int(max_expo_year['year__max']) + 1

        for personexpedition in self.personexpedition_set.all():
             if not personexpedition.is_guest:
                notability += Decimal(1) / (max_expo_val - int(personexpedition.expedition.year))
        return notability

    def bisnotable(self):
        """Boolean: is this person notable?
        """
        return self.notability() > Decimal(1)/Decimal(3)
    
    def surveyedleglength(self):
        return sum([personexpedition.surveyedleglength()  for personexpedition in self.personexpedition_set.all()])
    
    def first(self):
        return self.personexpedition_set.order_by('-expedition')[0]
    def last(self):
        return self.personexpedition_set.order_by('expedition')[0]

class PersonExpedition(TroggleModel):
    """Person's attendance to one Expo
    """
    expedition  = models.ForeignKey(Expedition,on_delete=models.CASCADE)
    person      = models.ForeignKey(Person,on_delete=models.CASCADE)
    slugfield   = models.SlugField(max_length=50,blank=True, null=True) # 2022 to be used in future

    is_guest    = models.BooleanField(default=False)  
    nickname    = models.CharField(max_length=100,blank=True, null=True) # removbe this

    class Meta:
        ordering = ('-expedition',)
        #order_with_respect_to = 'expedition'

    def __str__(self):
        return f"{self.person}: ({self.expedition})"
    
    #why is the below a function in personexpedition, rather than in person? - AC 14 Feb 09
    def name(self):
        if self.nickname:
            return f"{self.person.first_name} ({self.nickname}) {self.person.last_name}"
        if self.person.last_name:
            return f"{self.person.first_name} {self.person.last_name}"
        return self.person.first_name

    def get_absolute_url(self):
        return urljoin(settings.URL_ROOT, reverse('personexpedition',kwargs={'first_name':self.person.first_name,'last_name':self.person.last_name,'year':self.expedition.year}))
	
    def surveyedleglength(self):
        """Survey length for this person on all survex trips on this expedition
        """
        survexblocks = [personrole.survexblock  for personrole in self.survexpersonrole_set.all() ]
        return sum([survexblock.legslength  for survexblock in set(survexblocks)])