import re
from pathlib import Path

import django.forms as forms
from django.http import HttpResponseRedirect

from django.shortcuts import redirect, render

import troggle.settings as settings
#from troggle.core.models.caves import Entrance, Cave
from troggle.core.utils import (
    WriteAndCommitError,
    current_expo,
    get_cookie,
    git_string,
    write_and_commit,
)
from troggle.core.models.caves import Cave, Entrance
from troggle.core.views.caves import get_cave_from_slug

"""Forms to handle renaming files and editing contents when a cave
is 'katastered', ie.e moves from an informal number, such as
1623-2024-BL-10 to 1623-999
"""

def fix(request, areacode="1626"):
    """Fix bad stuff: 2023 misplaced i/l/t  files for caves in 
    both 1623 (65 files x3) and 1626 (29 filex x 3)
    """
    triple = ["i", "l", "t"]
    
    ents_html = settings.ENTRANCEDESCRIPTIONS
    caves_html = settings.CAVEDESCRIPTIONS
    to_move = []

    caves_in_area = list(Cave.objects.filter(areacode=areacode, kataster_number=""))
    caves_nodir=[]
    ents = []
    print(f"\n---------- {areacode}")
    
    # hack because some caves in 1623 have already been katastered. Bugger. That screws things up.
    for i in range(303,316):
        more = Cave.objects.filter(areacode=areacode, kataster_number=f"{i}")
        # print(more)
        caves_in_area.append(more[0])
        
    for c in caves_in_area:
        cave_id = f"{c.areacode}-{c.unofficial_number}"
        c_html = caves_html / f"{cave_id}.html"
        if not c_html.is_file():
            cave_id = f"{c.areacode}-{c.kataster_number}"
            c_html = caves_html / f"{cave_id}.html"
            if not c_html.is_file():
                raise
        ents.append(c.entrances())
        with open(c_html, 'r') as f:
            for line in f: 
                search_term = f"{areacode}" + "/(l|t|i)/([^'\"]*)"
                if match := re.findall(search_term, line):
                   # print(c, search_term, match, line)
                   for m in match:
                       dir, filename = m
                       to_move.append(("c", dir, cave_id[5:], c_html.name, filename))
    # for item in to_move:
        # print(item)
    for ce in ents:
        cc = ce[0].cave
        # print(cc)
        original_number = cc.unofficial_number
        for e in ce:
            # print("  ",e.entrance.slug, e.entrance.filename)         
            e_html = ents_html / e.entrance.filename
            print(range(303,316), cc.kataster_number)
            if cc.kataster_number and int(cc.kataster_number) in range(303,316):
                original_number = cc.kataster_number
                u = cc.unofficial_number
                k = cc.kataster_number
                print(u,k)
                e_html = ents_html /  e_html.name.replace(f"{cc.unofficial_number}",f"{cc.kataster_number}")
            if not e_html.is_file():
                e_html = ents_html /  e_html.name.replace(f"{original_number}",f"{cc.kataster_number}")
                original_number = cc.kataster_number
                if not e_html.is_file():
                    raise            
            with open(e_html, 'r') as f:
                for line in f: 
                    search_term = f"{areacode}" + "/(l|t|i)/([^'\"]*)"
                    if match := re.findall(search_term, line):
                       print(c, search_term, match, line)
                       for m in match:
                           dir, filename = m
                           to_move.append(("e", dir, original_number, e.entrance.filename, filename))
    to_move = list(set(to_move))
    to_move.sort(key=lambda tup: tup[2])
    # for c in caves_in_area:
        # cave_dir = settings.EXPOWEB / areacode / c.unofficial_number
        # if cave_dir.is_dir():
            # print(f"YES {c.unofficial_number}")
            # for subfile in cave_dir.iterdir():
                # if subfile.name not in triple:
                    # print(f"    ---- {subfile.name}")
                # else:  
                    # print(f"    ++++ {subfile.name}")
        # else:    
            # # print(f"NO  {c.unofficial_number}")
            # caves_nodir.append(c.unofficial_number)
            
    for key, dir, cave_id, filename, target in to_move:
        if key == "c":
            print((key, dir, cave_id, filename, target))
    print("")
    for key, dir, cave_id, filename, target in to_move:
        if key == "e":
            print((key, dir, cave_id, filename, target))
        
    newdirs = set()
    for key, dir, cave_id, filename, target in to_move:
        newdirs.add(cave_id)
        
    x_script = f"cd {settings.EXPOWEB / areacode}\n"
    for id in newdirs:
        # PATH.mkdir(parents=True, exist_ok=True)
        for d in triple:
            x_script += f"mkdir -p {id}/{d}\n"
        
    # This duplicates things horribly: every individual /l/ means a whole set of duplicate sed lines
    # simplify with a set somehow..
    # don't trigger this on seeing a /l/, just do it at the end on all the cave_data and entrance_data files we have seen.
    x_script += f"cd {settings.EXPOWEB / areacode}\n"
    for key, dir, cave_id, filename, target in to_move:
        x_script += f"mv {dir}/\"{target}\" {cave_id}/{dir}\n"
        if dir =="t":
            x_script += f"mv i/\"{target}\" {cave_id}/i\n"
        if dir =="l":
            x_script += f"sed -i 's|\/{areacode}\/i\/|/{areacode}/{cave_id}/i/|g'   {cave_id}/l/*.html\n"
            x_script += f"sed -i \"s|\/{areacode}\/i\/|/{areacode}/{cave_id}/i/|g\"   {cave_id}/l/*.html\n"
            for q in triple:
                if key =="e":
                    x_script += f"sed -i 's|\/{areacode}\/{q}\/|/{areacode}/{cave_id}/{q}/|g'   ../entrance_data/'{filename}'\n"
                    x_script += f"sed -i \"s|\/{areacode}\/{q}\/|/{areacode}/{cave_id}/{q}/|g\"   ../entrance_data/'{filename}'\n"
                if key == "c":
                    x_script += f"sed -i 's|\/{areacode}\/{q}\/|/{areacode}/{cave_id}/{q}/|g'   ../cave_data/'{filename}'\n"
                    x_script += f"sed -i \"s|\/{areacode}\/{q}\/|/{areacode}/{cave_id}/{q}/|g\"   ../cave_data/'{filename}'\n"

    script_rows =  str(max(35,3+x_script.count('\n')))   
            
    return render(
        request,
        "cave_fix.html", 
        {"areacode": areacode, "caves_nodir": caves_nodir, 
        "x_script": x_script, "rows": script_rows,
         }, ) 
    
    
def misplaced_html_files(cave):
    filelist = ""
    bad_place = settings.EXPOWEB / cave.areacode /"l"
    if bad_place.is_dir():
        for subfile in bad_place.iterdir():
            if subfile.name.lower().startswith(cave.unofficial_number.lower()):
                filelist += "&nbsp;&nbsp;&nbsp;" + subfile.name + "<br />\n"

    return filelist
    
def get_loser_dir(cave):
    """ Copes with capitalisation mismatch between declared loser diretory and actual.
    
    Two places to look:
    1. in caves-1623/<cave id>/*.svx
    2. in parent of survex file specified in the cave_data html page.
    
    return loser_dir, loser_name
    e.g.
    /home/philip/expo/loser/caves-1623/2024-jc-01 , caves-1623/2024-jc-01
    """
    loser_name = str(cave.unofficial_number)
    parent = settings.SURVEX_DATA / f"caves-{str(cave.areacode)}"

    for dn in [loser_name, loser_name.lower(), loser_name.upper()]:
        if ( parent / dn).is_dir():
            return parent / dn, Path(f"caves-{str(cave.areacode)}") /  dn
        if (settings.SURVEX_DATA / cave.survex_file).is_file():
            loser_dir = (settings.SURVEX_DATA / cave.survex_file).parent
            return loser_dir, Path(cave.survex_file).parent
                
    raise FileNotFoundError("This cave has no directory for survex files in the loser repo")
    
def get_cd(path):
    return f"$BASE_DIR/{path.relative_to(settings.REPOS_ROOT_PATH)}"
    
def entrances_list(cave):            
    entrances = []
    for e in cave.entrances(): # horrid CaveAndEntrance indirection we need to refactor out in due course
        if e.entrance.best_station():
            entrances.append(e.entrance)
    return entrances       

def entrances_stations(cave):            
    entrance_stations = []
    for e in cave.entrances(): # horrid CaveAndEntrance indirection we need to refactor out in due course
        if e.entrance.tag_station:
            entrance_stations.append((e.entrance, e.entrance.tag_station[4:]))
        if e.entrance.other_station:
            entrance_stations.append((e.entrance, e.entrance.other_station[4:]))
    return entrance_stations       

def kataster(request, slug=None):
    """Create the page which analyses how to rename a cave and all the files from the unofficial_number
    identifier, e.g. 1623-2023-mg-03 to the kataster number e.g. 1623-999
    """
    
    def do_file_finding(knum):
        
        global cavefilename, cave_data, entrance_data, loser_name, loser_data
        
        mvscript = f"BASE_DIR={settings.REPOS_ROOT_PATH}\n\n"

        ent_dir = settings.ENTRANCEDESCRIPTIONS # settings.EXPOWEB / "entrance_data"
        mvscript += f"cd {get_cd(settings.ENTRANCEDESCRIPTIONS)}\n"
        
        if cave.areacode == "1623":
            pt_target = "$BASE_DIR/loser/fixedpts/gps/gps*.svx"
        elif cave.areacode == "1626":
            pt_target = "$BASE_DIR/loser/fixedpts/*.svx"
        else: # 1627, 1624
            pt_target = "nowt.svx"
        for ent, station in entrances_stations(cave):
            new = station.replace(cave.unofficial_number,str(knum))
            sed_cmd = f"s/{station}/{new}/g".replace(".","\.")
            mvscript += f'sed -i {sed_cmd} {ent}.html\n'        
            mvscript += f'sed -i {sed_cmd} {pt_target}\n'  
            
        # 2024-DM-01 has no tag_ or other_station, but does have a *fix, cited in the survex file, catch this case:
        fix = (cave.unofficial_number)
        new = fix.replace(cave.unofficial_number,str(knum))
        sed_cmd = f"s/{fix}/{new}/g".replace(".","\.")
        mvscript += f'sed -i "{sed_cmd}" {pt_target}\n'
        
           
        entrance_data = []
        for ent in ent_dir.iterdir():
            if str(ent.name).startswith(str(cave)):
                print(ent.name)
                entrance_data.append("entrance_data/"+ent.name) 
                
                mvscript += f'sed -i "/<slug>{str(cave)}/d" {ent.name}\n'    
                mvscript += f'sed -i "/href=\|src=/s/\/{cave.areacode}\/{cave.unofficial_number}\//\/{cave.areacode}\/{knum}\//g"  {ent.name}\n'
                mvscript += f"mv {ent.name} {ent.name.replace(str(cave.unofficial_number),str(knum))}\n"
 
        mvscript += f"\ncd {get_cd(settings.CAVEDESCRIPTIONS)}\n"
        cavefilename = str(cave) + ".html"
        target= f"{cave.areacode}-{str(knum)}"
        
        cave_data = Path( "cave_data", cavefilename  )
        if not (settings.CAVEDESCRIPTIONS / cavefilename).is_file: # settings.EXPOWEB / cave_data
            cave_data = "does not exist"
            
        mvscript += f'sed -i "/<kataster_number>/s/<kataster_number>/<kataster_number>{knum}/" {cavefilename}\n'        
        mvscript += f'sed -i "/<survex_file>/s/<survex_file>[^<]*/<survex_file>caves-{cave.areacode}\/{knum}\/{knum}.svx/" {cavefilename}\n'        
        mvscript += f'sed -i "/<entranceslug>/s/<entranceslug>{str(cave)}/<entranceslug>{cave.areacode}-{knum}/" {cavefilename}\n'        
        mvscript += f'sed -i "/href=\|src=/s/\/{cave.areacode}\/{cave.unofficial_number}\//\/{cave.areacode}\/{knum}\//g" {cavefilename}\n'        

        mvscript += f"mv {cavefilename} {target}.html\n\n"

        mvscript += f"cd {get_cd(settings.EXPOWEB / cave.areacode / cave.unofficial_number)}\n"
        mvscript += f'sed -i "/href=\|src=/s/\/{cave.areacode}\/{cave.unofficial_number}\//\/{cave.areacode}\/{knum}\//g" *.html\n'        
        mvscript += f'sed -i "/href=\|src=/s/\/{cave.areacode}\/{cave.unofficial_number}\//\/{cave.areacode}\/{knum}\//g" */*.html\n\n'        

        mvscript += f"cd {get_cd(settings.EXPOWEB / cave.areacode)}\n"
        mvscript += f"mv {cave.unofficial_number} {knum}\n\n"


        loser_data = []
        try:
            loser_dir, loser_name = get_loser_dir(cave)
        except:
            return mvscript
        if (loser_dir).is_dir():
            print(loser_dir)
            for svx in loser_dir.iterdir():
                print(svx)
                loser_data.append(Path(loser_dir , svx).name)   
        return mvscript

    def script_loser(knum):
        global cavefilename, cave_data, entrance_data, loser_name, loser_data
        
        try:
            loser_dir, loser_name = get_loser_dir(cave) #/home/philip/expo/loser/caves-1623/2024-jc-01 , caves-1623/2024-jc-01
            target = loser_name.parent / str(knum)
            survex_name = str(loser_name.name)
            l_script = f"\ncd {get_cd(settings.SURVEX_DATA)}\n"
            l_script += f'sed -i "/^*include/s/{survex_name}/{knum}/g" {loser_name.parent}/caves.svx\n'

            l_script += f'sed -i "/^*equate/s/{survex_name}/{knum}/g" {loser_name}/{survex_name}.svx\n'        
            l_script += f'sed -i "/^*entrance/s/{survex_name}/{knum}/g" {loser_name}/{survex_name}.svx\n'        
            l_script += f'sed -i "/^*begin/s/{survex_name}/{knum}/" {loser_name}/{survex_name}.svx\n'
            l_script += f'sed -i "/^*end/s/{survex_name}/{knum}/" {loser_name}/{survex_name}.svx\n'
            l_script +=f"# These 'sed' edits will not do everything in all cases, but they do the basics\n\n"
        except FileNotFoundError as e:
            print(e)
            loser_name = ""
            l_script = f"\n# {e}\n\n"
    
        area_dir = get_cd(settings.SURVEX_DATA / f"caves-{cave.areacode}")
        l_script +=f"cd {area_dir}\n"
        l_script +=f"mv {cave.unofficial_number} {knum}\n"
        l_script +=f"cd {knum}\n"
        for filename in loser_data:
            if survex_name in filename:
                l_script +=f"mv {filename} {filename.replace(survex_name,str(knum))}\n"
        l_script +=f"# But note that git ignores .log and .3d files\n\n"
        
        error = ""
        for dir in [(settings.SURVEX_DATA / f"caves-{cave.areacode}" / str(knum)), (settings.EXPOWEB / cave.areacode / str(knum))]:
            if dir.is_dir():
                error += f"STOP: a target directory exists. REMOVE or RENAME it first: {dir}<br>\n"

        # 1623 : fixedpts/gps/gps23.svx:26:*fix p2023-mg-03  13.81514 47.69169 1767           
        # 1626: fixedpts/1626-no-schoenberg-hs-not-tied-to-caves.svx
        return l_script, error

    knum = 9999

    if not slug:
        slug = "1623-2013-BL-01"
    if cave := get_cave_from_slug(slug.lower()):
        pass
    elif cave := get_cave_from_slug(slug.upper()):
        pass
    else:
        return HttpResponseRedirect("/caves")   

    entrances = entrances_list(cave)
    # for e in cave.entrances(): # horrid CaveAndEntrance indirection we need to refactor out in due course
        # if e.entrance.best_station():
            # entrances.append(e.entrance)
    _ = do_file_finding(knum)     

    try:
        # this is a python generator idiom.
        # see https://realpython.com/introduction-to-python-generators/
        
        # THIS IS VERY SILLY. We have this in the databse fom when this file was parsed. Don't read it again !
        # However we will need to parse the href= and src= strings in the cave description text, notes etc.
        with open(settings.CAVEDESCRIPTIONS / cavefilename, 'r') as f:
            for line in f: 
                if match := re.search(r'<entranceslug>(.*?)</entranceslug>', line):
                    entrance = match.group(1)
                    print(entrance)
               
    except PermissionError as e:
        msg=f"CANNOT  save this file.\nPERMISSIONS incorrectly set on server for this file {cavefilename}. Ask a nerd to fix this: {e}"
        print(msg)
        raise
    except Exception as e:
        msg=f"CANNOT  write this file {cavefilename}. Ask a nerd to fix this: {e}"
        print(msg)

    # Restart script with POST data
    script = "# Conversion script - nearly complete\n# When doing it by hand, it is less error-prone to do the file re-namings last,\n"
    script += "# after the edits, but in a script it does not matter so much\n"
    script += "# so long as everything is consistent and tested.\n"
    script += "# Except that reversing changes using git does not always restore directories exactly\n# (because of .gitignore).\n\n"
    script += "# Be careful with the BASE_DIR directory: usually NOT the same on your PC as on the server\n\n"
    
    warning =""

    if request.method == "POST":  # If the form has been submitted...
        form = KatasterForm(request.POST)  # A form bound to the POST data
        if form.is_valid(): 
            clean = form.cleaned_data
            knum = clean['kataster_number']
            print(f" # kataster_number {clean['kataster_number']}")
            if test := get_cave_from_slug(f"{cave.areacode}-{knum}"):
                warning = "<br /><br />DANGER DANGER You will overwrite an existing katastered cave !!!<br />"
                warning += "resetting proposed new kataster number to 666.<br /><br />"
                knum = 666
    else: # GET and fall-through if POST is not valid        
        form = KatasterForm()
        
    script += do_file_finding(knum)
    scr, error = script_loser(knum)
    script += scr
    
    alias = f'(\\"{cave.slug()}\\", \\"{cave.areacode}-{knum}\\"),'
    aliasfile = settings.CAVEDESCRIPTIONS / "cavealiases.txt"
    script += f'\necho "{alias}" >> {get_cd(aliasfile)}\n\n'
    
    script += f"# grep to see what we have missed, though should still be records of the old name in expoweb.\n"
    script += f"# (and gps_essentials will need refreshing)\n"
    script += f'grep -nirI --exclude-dir=.git --exclude-dir=gpx --exclude="*.gpx" --exclude="*.log" --exclude="*.kml" --exclude="*.pos" "{cave.unofficial_number}" $BASE_DIR/loser\n'
    script += f'grep -nirI --exclude-dir=.git --exclude="*.gpx" "{cave.unofficial_number}" $BASE_DIR/expoweb\n'
    
    misplaced = misplaced_html_files(cave)
    script_rows =  str(max(35,3+script.count('\n')))   
    return render(
        request,
        "cave_kataster.html",
        {
            "form": form, "warning": warning, "error": error,
            "cave": cave, "entrances": entrances, "misplaced": misplaced,
            "cave_data": cave_data, "entrance_data": entrance_data,
            "loser_name": loser_name, "loser_data": loser_data,
            "knum": knum, "script": script, "rows": script_rows,
         }, )    
    
class KatasterForm(forms.Form):
    kataster_number= forms.IntegerField(label="New kataster no.", widget=forms.TextInput(attrs={'tabindex': 1, 'size':1, 'placeholder': '9999'}))
    
"""
SURVEX_DATA = REPOS_ROOT_PATH / "loser"
EXPOWEB = REPOS_ROOT_PATH / "expoweb"
CAVEDESCRIPTIONS = EXPOWEB / "cave_data"
ENTRANCEDESCRIPTIONS = EXPOWEB / "entrance_data"
    
"""