import click
from glob import glob
import os
import subprocess as sp
from console import fg, bg, fx
import sys
import random
import string
import termios
import tty

from jusfltuls import verif_apt
from unidecode import unidecode
from jusfltuls.check_new_version import is_there_new_version
from importlib.metadata import version as pkg_version



# GLOBAL
rename_all = False
dir_renames = {}

#===============================================================
#    linux uconv  conversion to useful ASCII
#---------------------------------------------------------------
def run_uconv( inp):
    """
    echo Résumé | uconv -x Any-Latin\\;Latin-ASCII
    gives Resume
    """
    # res = uconv -x 'Any-Latin;Latin-ASCII'
    ###result = sp.run(['uconv', '-x', 'Any-Latin;Latin-ASCII', inp], capture_output=True, text=True)
    #outputs.append(result.stdout)
    #run(['uconv', '-x', 'Any-Latin;Latin-ASCII', file])
    result = sp.run(['uconv', '-x', 'Any-Latin;Latin-ASCII'], input=inp, capture_output=True, text=True)
    #print(result)
    return result.stdout

#===============================================================
#
#---------------------------------------------------------------
def badchars( inp ):
    chktext = inp
    chktext = chktext.replace("\n", "_")
    chktext = chktext.replace("\t", "_")
    chktext = chktext.replace("   ", "_")
    chktext = chktext.replace("  ", "_")
    chktext = chktext.replace(" ", "_")
    
    chktext = chktext.replace("(", "%")
    chktext = chktext.replace(")", "%")
    
    badchar = " :<>`~!#$%^&*=–,…?«»'{}[]'|;\""
    for i in range(len(badchar)):
        chktext = chktext.replace(badchar[i], "_")
    
    if (chktext.find("__init__") < 0) and (chktext.find("__pycache__") < 0):
        while "__" in chktext:
            chktext = chktext.replace("__", "_")
    
    if chktext.startswith("-"):
        chktext = "_" + chktext[1:]
    if chktext.startswith("+"):
        chktext = "_" + chktext[1:]
    if chktext.startswith("@"):
        chktext = "_" + chktext[1:]
    if chktext.startswith("%"):
        chktext = "_" + chktext[1:]
    
    return chktext


#===============================================================
#   Apply all detox transformations to a name
#---------------------------------------------------------------
def detox_name(name):
    result = name
    result = replace_vata(result)
    result = run_uconv(result)
    result = unidecode(result)
    result = badchars(result)
    return result


def detox_basename(path):
    parent = os.path.dirname(path)
    basename = os.path.basename(path)
    new_basename = detox_name(basename)
    if parent:
        return os.path.join(parent, new_basename)
    return new_basename


#===============================================================
#   Update all paths in list after a directory rename
#---------------------------------------------------------------
def update_paths_after_rename(old_path, new_path, path_list):
    for i in range(len(path_list)):
        if path_list[i] == old_path:
            path_list[i] = new_path
        elif path_list[i].startswith(old_path + os.sep):
            path_list[i] = new_path + path_list[i][len(old_path):]
    return path_list


#===============================================================
#   Get all directories, sorted by depth (deepest first)
#---------------------------------------------------------------
def get_all_directories(base_path, exclude_root=True):
    old_dir = os.getcwd()
    os.chdir(base_path)
    dirs = glob("**/", recursive=True)
    dirs = [d.rstrip(os.sep) for d in dirs if d.rstrip(os.sep)]
    if exclude_root:
        dirs = [d for d in dirs if d]
    dirs.sort(key=lambda x: x.count(os.sep), reverse=True)
    os.chdir(old_dir)
    return dirs


#===============================================================
#   aaa to aab to aac .............
#---------------------------------------------------------------
def get_random_string():
    random_string = ''.join(random.choices(string.ascii_letters + string.digits, k=4))
    return random_string

#===============================================================
#   aaa to aab to aac .............
#---------------------------------------------------------------
def increment_string(s):
    s = list(s)
    i = len(s) - 1
    while i >= 0:
        if s[i] == 'z':
            s[i] = 'a'
            i -= 1
        else:
            s[i] = chr(ord(s[i]) + 1)
            break
    return ''.join(s)


#===============================================================
#
#---------------------------------------------------------------
def isduplicate_in( aaa, newf ):
    #duplicates = [item for item in set(newf) if newf.count(item) > 1]
    if aaa in newf:
        return True
    return False

#===============================================================
#
#---------------------------------------------------------------
def create_unique_filename(filepath, newf):
    """
    we know already filepath is duplicit. Add a suffinx and go
    newf is the list of existing filenames
    """
    # complete split.
    dir_name, file_name = os.path.split(filepath)
    base, ext = os.path.splitext(file_name)
    #suffix = 'aaa'
    new_filepath = None

    while (new_filepath is None) or (new_filepath in newf):
        suffix = get_random_string()
        if not ext:
            new_filepath = os.path.join(dir_name, f"{base}_{suffix}")
        else:
            new_filepath = os.path.join(dir_name, f"{base}_{suffix}{ext}")

    # while True:#new_filepath  in newf:
    #     # add suffix
    #     if not ext:
    #         new_filepath = os.path.join(dir_name, f"{base}_{suffix}")
    #     else:
    #         new_filepath = os.path.join(dir_name, f"{base}_{suffix}{ext}")
    #     if not(new_filepath in newf):
    #         break
    #     suffix = increment_string(suffix)
    # #print( "    dedup:", new_filepath)
    return new_filepath


#===============================================================
#   Case-insensitive duplicate detection
#---------------------------------------------------------------
def deduplicate_against( aaa, newf, lowercase_set=None ):
    dupflag = False
    
    # Check exact match first
    if isduplicate_in( aaa, newf):
        dupflag = True
        unique_name = create_unique_filename(aaa, newf)
        unique_name = badchars(unique_name)
        return unique_name, dupflag
    
    # Check case-insensitive collision
    if lowercase_set is not None:
        aaa_lower = aaa.lower()
        if aaa_lower in lowercase_set:
            # Case collision detected - need to deduplicate
            dupflag = True
            unique_name = create_unique_filename(aaa, newf)
            unique_name = badchars(unique_name)
            return unique_name, dupflag
    
    return aaa, dupflag


#===============================================================
#        VATA remove
#---------------------------------------------------------------
# Function to replace all matching parts of VATA in filename with "_"
def replace_vata(filename ):
    """
    jedna blbost
    """
    VATA = "Kukaj to - Raj online filmov a serialov"
    for i in range(len(VATA), 6, -1):
        if VATA[:i] in filename:
            filename = filename.replace(VATA[:i], "_")
    return filename


#===============================================================
#        y/n
#---------------------------------------------------------------
def ask_yes_no(question: str = "?(y/n): ") -> bool:
    print(question, end='', flush=True)
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    try:
        tty.setraw(sys.stdin.fileno())
        while True:
            key = sys.stdin.read(1).lower()
            #if key in ['y', 'n']:
            print(key, end="")
            return key == 'y'
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)

def ask_yes_no_never(question: str = "Rename? (y/n/q/a  for yes no quit all ): ") -> str:
    print(question, end='', flush=True)
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    try:
        tty.setraw(sys.stdin.fileno())
        while True:
            key = sys.stdin.read(1).lower()
            if key == 'y':
                print(key)
                return 'y'
            elif key == 'n':
                print(key)
                return 'n'
            elif key == 'a':
                print(key)
                return 'a'
            else:
                print(key)
                sys.exit(0)#return 'never'
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)




#===============================================================
# *******   CAREFULL RENAME with CYAN   ***********************
#---------------------------------------------------------------
def rename_wise(old, new, dry=True, dupflag=False, is_directory=False):
    global rename_all
    failed = True

    current_dir = os.getcwd()
    old_dir = os.path.dirname(old)
    old_file = os.path.basename(old)
    new_file = os.path.basename(new)
    if old_dir is not None and old_dir != "":
        os.chdir(old_dir)
    if not os.path.exists(old_file):
        label = "DIR" if is_directory else "FILE"
        print(fg.red, old_file, fg.default, f" NO SUCH {label}" )
        os.chdir(current_dir)
        return failed

    item_type = "DIR " if is_directory else "FILE"
    print( f"{fg.cyan}@ {os.getcwd()}{fg.default} :")
    print( f"  {fx.italic}{item_type}{fx.default}  {fg.gray}{old_file}{fg.default} " )
    print( f"  {fx.italic}new {fx.default}  {new_file} ", end="")
    if dupflag:
        print(bg.magenta, "de-duplicated", bg.default, end="")

    if os.path.exists(new_file):
        print("\n", fg.red,"    ", new_file, fg.default, " already EXISTS ")
        os.chdir(current_dir)
        return failed

    dorename = False
    if dry:
        print(fg.cyan, "DRY", fg.default)
        failed = False
        os.chdir(current_dir)
        return failed
    elif not rename_all:
        dorename = ask_yes_no_never()
        if dorename == "y":
            dorename = True
        elif dorename == "n":
            dorename = False
        elif dorename == "a":
            dorename = True
            rename_all = True
    else:
        dorename = True

    if dorename:
        try:
            os.rename(old_file, new_file)
            print( bg.green, "OK", bg.default)
            failed = False
        except Exception as e:
            print(bg.red, f" XXX {e} ", bg.default)
    os.chdir(current_dir)
    return failed

@click.command()
@click.option('-p', '--path', default='.', help='Path to process (default: current directory)')
@click.option('-y', '--yes', is_flag=True, help='Auto-confirm all renames')
@click.option('--dry', is_flag=True, help='Dry run - show changes without renaming')
@click.option('-v', '--version', is_flag=True, help='Show version')
def main(path, yes, dry, version):
    global rename_all, dir_renames
    
    if version:
        print(f"dtox {pkg_version('jusfltuls')}")
        is_there_new_version(package="jusfltuls", printit=True, printall=True)
        sys.exit(0)

    is_there_new_version(package="jusfltuls", printit=True, printall=True)
    verif_apt.main()
    
    dry_run = dry
    yesreally = yes and not dry_run
    if yesreally:
        global rename_all
        rename_all = True
    base_path = os.path.abspath(path)
    
    if not os.path.isdir(base_path):
        print(fg.red, f"Path not found: {base_path}", fg.default)
        sys.exit(1)
    
    print(f"Working in: {base_path}")

    dir_to_rename = 0
    dir_failed = 0
    dir_renamed = 0
    file_to_rename = 0
    file_failed = 0
    file_renamed = 0

    # ========== PASS 1: DIRECTORIES ==========
    print(f"\n{fg.yellow}=== PASS 1: DIRECTORIES ==={fg.default}")
    dirs = get_all_directories(base_path, exclude_root=True)
    dir_renames = {}
    newf_dirs = []
    
    print(f" ... directories found: {len(dirs)}")
    
    old_cwd = os.getcwd()
    os.chdir(base_path)
    
    dirs_lowercase = set()
    for i in dirs:
        apc = detox_basename(i)
        apc, dupflag = deduplicate_against(apc, newf_dirs, dirs_lowercase)
        newf_dirs.append(apc)
        dirs_lowercase.add(apc.lower())
        
        if i != apc or dupflag:
            dir_to_rename += 1
            resfa = rename_wise(i, apc, dry=dry_run, dupflag=dupflag, is_directory=True)
            if resfa:
                dir_failed += 1
            else:
                if not dry_run:
                    dir_renamed += 1
                    dir_renames[i] = apc
                    dirs = update_paths_after_rename(i, apc, dirs)
    
    os.chdir(old_cwd)
    print(f"i... DIRS:  total={len(dirs)}  to_rename={dir_to_rename}  failed={fg.red}{dir_failed}{fg.default}  renamed={fg.green}{dir_renamed}{fg.default}")

    # ========== PASS 2: FILES (re-scan after directory renames) ==========
    print(f"\n{fg.yellow}=== PASS 2: FILES ==={fg.default}")
    
    os.chdir(base_path)
    currf = glob("**/*", recursive=True)
    currf = [f for f in currf if not os.path.isdir(f)]
    currf.sort(key=lambda x: x.count(os.sep), reverse=True)
    os.chdir(old_cwd)
    
    print(f" ... files to process: {len(currf)}")
    
    # Pre-compute detox names to determine processing order
    # Process files that don't need renaming first (they become targets for dedup)
    files_with_detox = [(i, detox_basename(i)) for i in currf]
    # Sort: files with no change first (i == apc), then files needing rename
    files_with_detox.sort(key=lambda x: 0 if x[0] == x[1] else 1)
    
    os.chdir(base_path)
    newf = []
    files_lowercase = set()
    for i, apc in files_with_detox:
        apc, dupflag = deduplicate_against(apc, newf, files_lowercase)
        newf.append(apc)
        files_lowercase.add(apc.lower())
        
        if i != apc or dupflag:
            file_to_rename += 1
            resfa = rename_wise(i, apc, dry=dry_run, dupflag=dupflag, is_directory=False)
            if resfa:
                file_failed += 1
            else:
                if not dry_run:
                    file_renamed += 1
    
    os.chdir(old_cwd)
    print("___________________________________________________________________dtox")
    print(f"i... FILES: total={len(currf)}  to_rename={file_to_rename}  failed={fg.red}{file_failed}{fg.default}  renamed={fg.green}{file_renamed}{fg.default}")

if __name__ == "__main__":
    main()
