import os
import shutil
import re
import ast
from concurrent.futures import ThreadPoolExecutor, as_completed

# Functions related to VaM/VarBrowser in OS context


def read_varbr_file(file_path, set_status) -> list:
    try:
        # Check if the file exists
        if not os.path.isfile(file_path):
            raise FileNotFoundError(f"The file {file_path} was not found.")
        
        with open(file_path, 'r', encoding='utf-8') as file:
            content = file.read().strip()
        
        # Remove braces at the beginning and end of the content
        if content.startswith('{') and content.endswith('}'):
            content = content[1:-1].strip()
        else:
            raise ValueError("The file does not contain a valid dictionary structure.")
        
        # Convert the string to a dictionary using ast.literal_eval for safe evaluation
        dictionary = ast.literal_eval(f"{{{content}}}")

        return dictionary
    
    except Exception as e:
        set_status(f"An error occurred: {e}")
        return None


def save_list_to_varbr_dict(pkglist, key, file_path, set_status):
    if pkglist is not None:

        # Prepare the format for compatibility with VarBrowser's AutoInstall.txt
        new_dict = { key : [] }
        new_dict[key] = list(pkglist)

        try:
            # Convert the dictionary to a formatted string with double quotes
            names = ','.join(f'"{name}"' for name in new_dict[key])
            content = f'{{"{key}":[{names}]}}'

            with open(file_path, 'w', encoding='utf-8') as file:
                file.write(content)
            
            set_status(f"File saved successfully to {file_path}")
    
        except Exception as e:
            set_status(f"An error occurred while saving the file: {e}")


def save_varbrowser_dict(path, key, pkgset, set_status):
    if pkgset is not None:
        # Prepare the format for compatibility with VarBrowser AutoInstall.txt
        bak = path + '.bak'
        shutil.copy(path, bak)
        save_list_to_varbr_dict(pkgset, key, path, set_status)


def move_files(source_dir, dest_dir, move_list, set_status):
    if move_list:
        set_status(f'Moving files...')
        for source_path in move_list:
            # Get relative path
            relative_path = os.path.relpath(source_path, start=source_dir)
            # Create destination path with relative path
            dest_path = os.path.join(dest_dir, relative_path)
            # Create dirs if not exist
            os.makedirs(os.path.dirname(dest_path), exist_ok=True)
            # Move file
            shutil.move(source_path, dest_path)
            set_status(f'Moving: {source_path}')
        set_status('File(s) moved.')
        return True


def restore_dummyvars_dir(source, dest):
    try:
        # Move directory dummyvar
        shutil.move(source, dest)
    except PermissionError as e:
        # print(f"PermissionError: {e}")
        pass
    except FileNotFoundError as e:
        print(f"FileNotFoundError: {e}")
    except Exception as e:  
        print(f"unexpected error: {e}")    


def copy_files(common_dir, dest_dir, copy_list, set_status):
    if copy_list:
        set_status(f'Copying files...')
        for source_path in copy_list:
            try:
                # Get relative path
                path_parts = os.path.splitdrive(source_path)[1].split(os.sep)
                start_index = path_parts.index(common_dir) + 1
                relative_path = os.path.join(*path_parts[start_index:])
                # Create destination path with relative path
                dest_path = os.path.join(dest_dir, relative_path)
                # Create dirs if not exist
                os.makedirs(os.path.dirname(dest_path), exist_ok=True)
                # Copy file preserving timestamp (copy2)
                shutil.copy2(source_path, dest_path)
                set_status(f'Copying: {source_path}')

            except Exception as e:
                set_status(f"Error copying {source_path}: {e}")
        set_status('File(s) copied.')


import os

def create_symlinks(common_dir, dest_dir, link_list, set_status):
    if link_list:
        # Crear el subdirectorio 'symlinked' dentro de dest_dir si no existe
        symlinked_dest_dir = os.path.join(dest_dir, 'symlinked')
        os.makedirs(symlinked_dest_dir, exist_ok=True)

        set_status(f'Creating symlinks in {symlinked_dest_dir}...')
        for source_path in link_list:
            try:
                # Get relative path
                path_parts = os.path.splitdrive(source_path)[1].split(os.sep)
                start_index = path_parts.index(common_dir) + 1
                relative_path = os.path.join(*path_parts[start_index:])
                # Create destination path with relative path inside 'symlinked' directory
                dest_path = os.path.join(symlinked_dest_dir, relative_path)
                # Create dirs if not exist
                os.makedirs(os.path.dirname(dest_path), exist_ok=True)
                # Create symlink (os.symlink)
                os.symlink(source_path, dest_path)
                set_status(f'Creating symlink: {source_path} -> {dest_path}')

            except Exception as e:
                set_status(f"Error creating symlink for {source_path}: {e}")
        set_status('Symlink(s) created in {symlinked_dest_dir}.')
        return True
    return False

def start_vam(vam_dir, args):
    os.startfile(os.path.join(vam_dir,'VaM.exe'), arguments=args)


def find_packages_in_json(dir_path, set_status, max_threads) -> set:
    results = set()

    # Verify dir_path exists
    if not os.path.exists(dir_path):
        set_status(f"Directory '{dir_path}' does not exist.")
        return results

    pattern = r'"\s*:\s*"([^\.]+\.[^\.]+\.[^\.]+):'

    # Prepare the json counter
    total_files = len([name for name in os.listdir(dir_path) if name.endswith('.json')])
    set_status(f'Processing {total_files} files. Please wait...')
    # percent counter
    percent_t = total_files * 0.01
    percent = 0
    n = 0

    def find_in_json(file_path, results):
        with open(file_path, 'r', encoding='utf-8') as json_file:
            for line in json_file:
                found = re.findall(pattern, line)
                if found:
                    results.update(found)  # Add found patterns to set

    # Iterate on files in dir_path
    for file_name in os.listdir(dir_path):
        # Verify if file is .json
        if file_name.endswith(".json"):
            file_path = os.path.join(dir_path, file_name)

            # Print scene processing information
            n += 1
            if (n + 1) >= (percent + 1) * percent_t:
                percent += 1
                set_status(f'Processing scenes... {percent}%')

            with ThreadPoolExecutor(max_workers=max_threads) as executor:
                try:
                    futures = {executor.submit(find_in_json, file_path, results)}
                except Exception as e:
                    set_status(f"Error processing '{file_path}': {e}")
    return results
