import asyncio
import os
import sys
import ctypes

from surf_tls.exceptions.exceptions import SurfTLSClientException
from surf_tls.utils.asset import generate_asset_name, root_dir
from surf_tls.updater.file_fetch import read_version_info


async def check_and_download_dependencies():
    """
    Check if the dependencies folder is empty and download necessary files if it is.
    """
    root_directory = root_dir()
    dependencies_dir = f'{root_directory}/dependencies'
    if not os.path.exists(dependencies_dir):
        os.makedirs(dependencies_dir, exist_ok=True)
    
    contains_anything = [file for file in os.listdir(dependencies_dir) if not file.startswith('.')]
    if len(contains_anything) == 0:
        print(">> Dependencies folder is empty. Please build the surf-tls-client shared library.")
        print(">> Run: cd cffi_dist && go build -buildmode=c-shared -o ../dependencies/surf-tls-client.so")


def run_async_task(task):
    """
    Run an asynchronous task taking into account the current event loop.
    :param task: Coroutine to run.
    """
    try:
        loop = asyncio.get_running_loop()
    except RuntimeError:
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        loop.run_until_complete(task)
    else:
        if loop.is_running():
            asyncio.ensure_future(task)
        else:
            loop.run_until_complete(task)


def load_asset():
    """
    Load the asset and return its name.
    :return: Name of the asset.
    """
    # Check if dependencies folder exists
    # Dependencies are in surf_tls/dependencies/ relative to the package
    package_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    dependencies_dir = os.path.join(package_dir, 'dependencies')
    
    if not os.path.exists(dependencies_dir):
        os.makedirs(dependencies_dir, exist_ok=True)

    # Try to read version info first
    current_asset, current_version = read_version_info()
    
    if current_asset and current_version:
        # Use the asset name from version info
        asset_name = current_asset
    else:
        # Fallback to generating asset name (for local development)
        asset_name = generate_asset_name()
    
    asset_path = os.path.join(dependencies_dir, asset_name)
    if not os.path.exists(asset_path):
        raise SurfTLSClientException(
            f"Unable to find asset {asset_name} at {asset_path}. "
            f"Please run: await surf_tls.download_if_necessary() or build locally with: ./build.sh"
        )

    return asset_name


def initialize_library():
    """
    Initialize and return the library.
    :return: Loaded library object.
    """
    try:
        asset_name = load_asset()
        # Dependencies are in surf_tls/dependencies/ relative to the package
        package_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        dependencies_dir = os.path.join(package_dir, 'dependencies')
        asset_path = os.path.join(dependencies_dir, asset_name)
        library = ctypes.cdll.LoadLibrary(asset_path)
        return library
    except SurfTLSClientException as e:
        print(f">> Failed to load the Surf TLS Client asset: {e}")
        return None
    except OSError as e:
        print(f">> Failed to load the library: {e}")
        if sys.platform == "darwin":
            print(">> If you're on macOS, you may need to allow the library to be loaded:")
            print(">> System Preferences > Security & Privacy > General > Allow")
        from surf_tls.utils.asset import generate_asset_name
        package_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
        dependencies_dir = os.path.join(package_dir, 'dependencies')
        asset_path = os.path.join(dependencies_dir, generate_asset_name())
        print(f">> Asset path attempted: {asset_path}")
        return None


library = initialize_library()

if library is None:
    # Create dummy functions that will raise an error when called
    def _error_function(*args, **kwargs):
        raise SurfTLSClientException("Surf TLS Client library not loaded. Please build the shared library first by running: ./build.sh")
    
    request = _error_function
    free_memory = _error_function
else:
    # Define the request function from the shared package
    request = library.request
    request.argtypes = [ctypes.c_char_p]
    request.restype = ctypes.c_char_p

    free_memory = library.freeMemory
    free_memory.argtypes = [ctypes.c_char_p]
    free_memory.restype = ctypes.c_char_p
