import datetime
from pathlib import Path
from urllib.parse import unquote as urlunquote

from django.conf import settings
from django.http import HttpResponse
from django.shortcuts import render

from troggle.core.models.caves import GetCaveLookup
from troggle.core.models.survex import SingleScan, SurvexBlock, SurvexPersonRole
from troggle.core.models.troggle import DataIssue, Expedition, Person, PersonExpedition
from troggle.core.models.wallets import Wallet
from troggle.core.utils import DatabaseResetOngoing, current_expo
from troggle.core.views.expo import getmimetype
from troggle.parsers.caves import add_cave_to_pending_list
from troggle.parsers.people import GetPersonExpeditionNameLookup
from troggle.parsers.survex import set_walletdate

"""
Note that caveifywallet() etc do NOT save the object to the db. They are ephemeral, just for the page rendering of the
manywallets dict.
"""

todo="""
- one of these views serves files as binary blobs, and simply set the mime type based on the file extension,
as does the urls.py dispatcher which sends them here. Here they should actually have the filetype checked 
by looking inside the file before being served.

- need to check if inavlid query string is invalid, or produces multiple replies
and render a user-friendly error page.
"""
       
def fix_manywallets(many):
    for w in many:
        fillblankpeople(w)
        fillblankothers(w)
        w.ticks = w.get_ticks()  # the complaints in colour form
        fixsurvextick(w, w.ticks)
            
def populatewallet(w):
    """Need to tidy this up, now all the work is done at parse time not here
    Only gets data from the survex file when it was parsed on import, or edited (& thus parsed) online,
    so doesn't work if there was no *ref value
    """
    w.slugpeople = w.persons.all()


def caveifywallet(w):
    """Gets the block names from the list of blocks, set by the survex parser
     """
    # print(f' - Caveify {w=}')
    blocknames = []
    blocks = SurvexBlock.objects.filter(scanswallet=w)
    for b in blocks:
        # NB b.cave is not populated by parser. Use b.survexfile.cave instead, or we could parse b.survexpath
        #if b.survexfile.cave:
            #w.caves.add(b.survexfile.cave)
        if b.name:
            blocknames.append(b.name)

    if w.name():
        w.displaynames = [w.name()]
    else:
        w.displaynames = blocknames


def fillblankpeople(w):
    """Find people  attached to a wallet via the survex files
    if no one explicitly attached.
    the JSON string which may OR MAY NOT be formatted as a list.
    
    w.slugpeople is from the survexfiles
    w.persons is from the explicit list of peoples' names in the wallet
    The template choses how to display them.
    """
    def nobody(wplist):
        if len(wplist) > 1:
            return False
            
        nobod = wp[0].lower()
        if nobod == "unknown" or nobod == "nobody" or nobod == " " or nobod == "":
            return True
        else:
            return False
    
    wp = w.people() # just a list of names as strings, direct from JSON. 
    if not isinstance(wp, list): # might be None 
        print(f"{w} NOT A LIST {type(wp)}: {wp}")
        populatewallet(w)
        return
   
    if not wp: # e.g. empty list
        # print(f"{w} {wp=}")
        populatewallet(w) # sets w.slugpeople
        return
    
    
    if nobody(wp):
        populatewallet(w) # sets w.slugpeople
    else:
        w.peeps = parse_name_list(w)
        populatewallet(w) # sets w.slugpeople
        if hasattr(w, "slugpeople"):
            w.peeps = w.peeps.difference(w.slugpeople)
    return


def is_cave(wallet, id):
    if not id:
        return False
    Gcavelookup = GetCaveLookup()
    if id in Gcavelookup:
        return True
    else:
        # Historic wallets  were all 1623 area. So, just for wallets, 
        # assume it is 1623-xxx
        if f"1623-{id}" in Gcavelookup:
            print(f" - Should modify wallet {wallet} to use 1623- prefix for cave <{id}>")
            Gcavelookup[id] = Gcavelookup[f"1623-{id}"] # restoring an ambiguous alias, BAD idea.
            return True
        else:
            print(f" - Wallet {wallet} Failed to find cave object from id <{id}>")
            if id.lower() != "unknown" and id != "":
                print(f" - adding <{id}> to pendingcaves DataIssues")
                add_cave_to_pending_list(id, wallet, f"Wallet {wallet} - Could not find cave id <{id}>")
            return False

def fillblankothers(w):
    # has this already been done? Do we need to do it on every refresh ?
    if not w.walletdate:
        set_walletdate(w)

    Gcavelookup = GetCaveLookup()

    caveifywallet(w)
 
    # Is this not done when we scan for wallets when we create them in the first place ? 
    # needs to be refactored into models/wallets.py anyway
    wcaveid = w.cave()
    if wcaveid:
        if wcaveid == "":
            if type(wcaveid) == list:
                for i in wcaveid:
                    i = i.strip("' []\"")
                    if is_cave(w,i):
                        w.caves.add(Gcavelookup[i])
            elif wcaveid.find(',') != -1:
                # it's a list of cave ids as a string
                ids = wcaveid.split(',')
                for i in ids:
                    i = i.strip("' []\"")
                    if is_cave(w,i):
                        w.caves.add(Gcavelookup[i])
            else:
                if is_cave(w,wcaveid):
                    w.caves.add(Gcavelookup[i])
  
                
def fixsurvextick(w, ticks):
    ticks["S"] = w.fixsurvextick(ticks["S"])

def parse_name_list(w):
    """Given a list of strings, which are names, and the year of an expo,
    return a set of Persons
    """
    namelist = w.people()
    peeps = set()
    expo = Expedition.objects.get(year=w.year())
    crew = GetPersonExpeditionNameLookup(expo)
    
    for n in namelist:
        if n.lower() in crew:
            peeps.add(crew[n.lower()].person)
        else:
            if check := n.startswith("*"): #ignore people flagged as guests or not-expo anyway, such as ARGE
                continue
            nobod = n.lower()
            if nobod == "unknown" or nobod == "nobody" or nobod == " " or nobod == "":
                continue
            else:
                wurl = f"/walletedit/{w.walletname.replace('#',':')}"
                message = f"{w} name '{n.lower()}' NOT found in GetPersonExpeditionNameLookup({w.year()}) n.startswith* = {check} ?!"
                print(message)
                DataIssue.objects.update_or_create(parser="wallets", message=message, url=wurl)    
    return peeps
    
def walletslistperson(request, slug):
    """Page which displays a list of all the wallets for a specific person
    """
    
    # Remember that 'personexpedition__expedition' is interpreted by Django to mean the
    # 'expedition' object which is connected by a foreign key to the 'personexpedition'
    # object, which is a field of the PersonLogEntry object:
    # PersonLogEntry.objects.filter(personexpedition__expedition=expo)

    def personwallet(p):
        manywallets = set()
        
        # Get the persons from the survexblocks on the survexfiles attached to the wallet directly
        sps = SurvexPersonRole.objects.filter(person=p)
        for sp in sps:
            w = sp.survexblock.scanswallet
            if w:
                 manywallets.add(w)
                 
        # Now read the text strings in the list of wallet people and identify the person
        pes = PersonExpedition.objects.filter(person=p)
        for person_expo in pes:
            expo = person_expo.expedition
            year = expo.year
        
            crew = GetPersonExpeditionNameLookup(expo)
            wallets = Wallet.objects.filter(walletyear__year=year)
            for w in wallets:
                if w in manywallets:
                    # we already know this is a wallet we need to report on
                    continue
                if not w.people(): # bug? skip
                    # msg = f"{w} no w.people() found "
                    # print(msg)
                    continue
                for n in w.people():
                    if n.lower() in crew:
                        if crew[n.lower()] == person_expo:
                            manywallets.add(w)
                            # print(f"{w} Found a non-survex wallet for {person_expo}")
                    else:
                        if n.startswith("*"): #ignore people flagged as guests or not-expo anyway, such as ARGE
                            pass
                        nobod = n.lower()
                        if nobod == "unknown" or nobod == "nobody" or nobod == " " or nobod == "":
                            pass
                        else:
                            wurl = f"/walletedit/{w.walletname.replace('#',':')}"
                            message = f"{w} name '{n.lower()}' NOT found in GetPersonExpeditionNameLookup({year}) ?!"
                            print(message)
                            DataIssue.objects.update_or_create(parser="wallets", message=message, url=wurl)
                         
        fix_manywallets(manywallets)
        return manywallets

    # print("-walletslistperson")
    p = Person.objects.get(slug=slug)
   
    manywallets = personwallet(p)
    expeditions = Expedition.objects.all()
    length_ug = 0.0
    for w in manywallets:
        print(f"{w}: no .persons in this wallet but people() is  {w.people()}")
        for sb in w.survexblock_set.all():
            length_ug += sb.legslength
    return render(
        request,
        "personwallets.html",
        {"manywallets": manywallets, "settings": settings, "person": p, "expeditions": expeditions,           
        "length_ug": length_ug, "year": current_expo()}
    )


def setwalletsdates():
    """This sets all the undated wallets, but they should already all be dated on
    import or on edit"""
    wallets = Wallet.objects.filter(walletdate=None)
    print(f"undated wallets: {len(wallets)}")
    for w in wallets:
        w.walletdate = w.date()
        w.save()


def walletslistyear(request, year):
    """Page which displays a list of all the wallets in a specific year.
    We have a field .walletyear, which we set on import.
    """

    def ticksyearwallet(year):
        manywallets = []
        try:
            manywallets = Wallet.objects.filter(walletyear__year=year)
        except OperationalError:
            # Reset is ongoing on server
            raise DatabaseResetOngoing(
                    f"Expo Database re-import ongoing on server.\nPlease wait 6 minutes.\n If still not working, contact a nerd."
                ) 

        fix_manywallets(manywallets)
        return manywallets

    # print("-walletslistyear")
    if year < 1976 or year > 2050:
        return render(request, "errors/generic.html", {"message": "Year out of range. Must be between 1976 and 2050"})

    year = str(year)
    manywallets = ticksyearwallet(year)
    expeditions = Expedition.objects.all() 
    expedition = expeditions.filter(year=year)
    length_ug = 0.0
    for w in manywallets:
        for sb in w.survexblock_set.all():
            length_ug += sb.legslength
    return render(
        request,
        "yearwallets.html",
        {
            "manywallets": manywallets,
            "settings": settings,
            "year": year,
            "expeditions": expeditions,
            "expedition": expedition,
            "length_ug": length_ug,
        },
    )


def cavewallets(request, caveid):
    """Returns all the wallets for just one cave"""
    print("-cavewallets")

    Gcavelookup = GetCaveLookup()
    if caveid in Gcavelookup:
        cave = Gcavelookup[caveid]
    else:
        return render(request, "errors/badslug.html", {"badslug": f"{caveid} - from cavewallets()"})

    wallets = cave.wallets.all()
    manywallets = list(set(wallets))
    fix_manywallets(manywallets)

    expeditions = Expedition.objects.all()
    length_ug = 0.0
    for w in manywallets:
        for sb in w.survexblock_set.all():
            length_ug += sb.legslength    
    return render(
        request,
        "cavewallets.html",
        {"manywallets": manywallets, "settings": settings, "cave": cave, "expeditions": expeditions,
        "length_ug": length_ug, "year": current_expo()}
    )


def oldwallet(request, path):
    """Now called only for non-standard wallet structures for pre-2000 wallets"""
    # print([ s.walletname  for s in Wallet.objects.all() ])
    print(f"! - oldwallet path:{path}")
    try:
        wallet = Wallet.objects.get(walletname=urlunquote(path))
        return render(request, "wallet_old.html", {"wallet": wallet, "settings": settings, "year": current_expo()})
    except:
        message = f"Scan folder error or not found '{path}' ."
        return render(request, "errors/generic.html", {"message": message,"year": current_expo()})


def scansingle(request, path, file):
    """sends a single binary file to the user for display - browser decides how using mimetype
    This is very unsafe"""
    try:
        wallet = Wallet.objects.get(walletname=urlunquote(path))
        singlescan = SingleScan.objects.get(wallet=wallet, name=file)
        imagefile = Path(singlescan.ffile, file)
        if imagefile.is_file():
            message = f" - scansingle {imagefile}  {path}:{file}:{getmimetype(file)}:"
            print(message)
            return HttpResponse(content=open(imagefile, "rb"), content_type=getmimetype(file))  # any type of image
        else:
            message = f"Scan folder file  '{imagefile}' not found. {path=} {file=}"
            print(message)
            return render(request, "errors/generic.html", {"message": message})

    except:
        message = f"Scan folder or scan item access error '{path}'  and '{file}'."
        return render(request, "errors/generic.html", {"message": message})


def allscans(request):
    """Returns all the wallets in the system, we would like to use
    the Django queryset SQL optimisation https://docs.djangoproject.com/en/dev/ref/models/querysets/#prefetch-related
    to get the related singlescan and survexblock objects but that requires rewriting this to do the query on those, not on
    the wallets
    """
    manywallets = Wallet.objects.all()  # NB all of them
    # manywallets = Wallet.objects.all().prefetch_related('singlescan') fails as the link is defined on 'singlescan' not on 'wallet'
    expeditions = Expedition.objects.all()
    return render(
        request, "walletsall.html", {"manywallets": manywallets, "settings": settings, "expeditions": expeditions, "year": current_expo()}
    )
