import os
from collections.abc import Callable
from configparser import SectionProxy
from typing import Any

from legendary.models.config import LGDConf

from rare.lgndr.core import LegendaryCore

_config: LGDConf | None = None
_save_config: Callable[[], None] | None = None


def init_config_handler(core: LegendaryCore):
    global _config, _save_config
    _config = core.lgd.config
    _save_config = core.lgd.save_config


def save_config():
    if _save_config is None:
        raise RuntimeError('Uninitialized use of config_helper')
    _save_config()


def set_option(app_name: str, option: str, value: str) -> None:
    value = value.replace('%%', '%').replace('%', '%%')
    if not _config.has_section(app_name):
        _config[app_name] = {}
    _config.set(app_name, option, value)
    # save_config()


def set_boolean(app_name: str, option: str, value: bool) -> None:
    set_option(app_name, option, str(value).lower())


def remove_section(app_name: str) -> None:
    return
    # Disabled due to env variables implementation
    # if _config.has_section(app_name):
    #     _config.remove_section(app_name)
    #     save_config()


def remove_option(app_name: str, option: str) -> None:
    if _config.has_option(app_name, option):
        _config.remove_option(app_name, option)
    # if _config.has_section(app_name) and not _config[app_name]:
    #     _config.remove_section(app_name)
    # save_config()


def adjust_option(app_name: str, option: str, value: str) -> None:
    if value:
        set_option(app_name, option, value)
    else:
        remove_option(app_name, option)


def get_option(app_name: str, option: str, fallback: Any = None) -> str:
    return _config.get(app_name, option, fallback=fallback)


def adjust_option_with_global(app_name: str, option: str, value: str) -> None:
    value = value if value else ''
    if value == get_option('default', option, ''):
        remove_option(app_name, option)
    else:
        set_option(app_name, option, value)


def get_option_with_global(app_name: str, option: str, fallback: Any = None) -> str:
    _option = get_option('default', option, fallback=fallback)
    _option = get_option(app_name, option, fallback=_option)
    return _option


def get_boolean(app_name: str, option: str, fallback: bool = False) -> bool:
    return _config.getboolean(app_name, option, fallback=fallback)


def set_envvar(app_name: str, envvar: str, value: str) -> None:
    set_option(f'{app_name}.env', envvar, value)


def remove_envvar(app_name: str, option: str) -> None:
    remove_option(f'{app_name}.env', option)


def adjust_envvar(app_name: str, option: str, value: str) -> None:
    adjust_option(f'{app_name}.env', option, value)


def get_envvar(app_name: str, option: str, fallback: Any = None) -> str:
    return get_option(f'{app_name}.env', option, fallback=fallback)


def adjust_envvar_with_global(app_name: str, option: str, value: str) -> None:
    value = value if value else ''
    if value == get_envvar('default', option, ''):
        remove_envvar(app_name, option)
    else:
        set_envvar(app_name, option, value)


def get_envvar_with_global(app_name: str, option: str, fallback: Any = None) -> str:
    _option = _config.get('default.env', option, fallback=fallback)
    _option = _config.get(f'{app_name}.env', option, fallback=_option)
    return _option


def adjust_wine_prefix(app_name: str, value: str) -> None:
    adjust_envvar(app_name, 'WINEPREFIX', value)
    adjust_option(app_name, 'wine_prefix', value)


def get_wine_prefix(app_name: str, fallback: Any = None):
    _prefix = get_envvar(app_name, 'WINEPREFIX', fallback=fallback)
    _prefix = get_option(app_name, 'wine_prefix', fallback=_prefix)
    return _prefix


def get_wine_prefix_with_global(app_name: str, fallback: Any = None) -> str:
    _prefix = get_wine_prefix('default', fallback)
    _prefix = get_wine_prefix(app_name, fallback=_prefix)
    return _prefix


def adjust_compat_data_path(app_name: str, value: str) -> None:
    adjust_envvar(app_name, 'STEAM_COMPAT_DATA_PATH', value)


def get_compat_data_path(app_name: str | None = None, fallback: Any = None) -> str:
    _compat = get_envvar(app_name, 'STEAM_COMPAT_DATA_PATH', fallback=fallback)
    # return os.path.join(_compat, "pfx") if _compat else fallback
    return _compat


def get_compat_data_path_with_global(app_name: str | None = None, fallback: Any = None) -> str:
    _compat = get_envvar_with_global(app_name, 'STEAM_COMPAT_DATA_PATH', fallback=fallback)
    # return os.path.join(_compat, "pfx") if _compat else fallback
    return _compat


def prefix_exists(pfx: str) -> bool:
    return os.path.isdir(pfx) and os.path.isfile(os.path.join(pfx, 'user.reg'))


def _get_prefixes(lookup_fn: Callable[[SectionProxy], str]) -> set[tuple[str, str]]:
    _prefixes: set[tuple[str, str]] = set()
    for name, section in _config.items():
        pfx = lookup_fn(section)
        if pfx:
            _prefixes.update([(pfx, name[: -len('.env')] if name.endswith('.env') else name)])
    _prefixes = {(os.path.expanduser(p), n) for p, n in _prefixes}
    return {(p, n) for p, n in _prefixes if prefix_exists(p)}


def get_wine_prefixes() -> set[tuple[str, str]]:
    return _get_prefixes(lambda s: s.get('WINEPREFIX') or s.get('wine_prefix'))


def get_proton_prefixes() -> set[tuple[str, str]]:
    return _get_prefixes(lambda s: os.path.join(compat_path, 'pfx') if (compat_path := s.get('STEAM_COMPAT_DATA_PATH')) else '')


def get_prefixes() -> set[tuple[str, str]]:
    return get_wine_prefixes().union(get_proton_prefixes())


def get_prefix(app_name: str = 'default') -> str | None:
    _compat_path = _config.get(f'{app_name}.env', 'STEAM_COMPAT_DATA_PATH', fallback=None)
    if _compat_path and prefix_exists(_compat_prefix := os.path.join(_compat_path, 'pfx')):
        return _compat_prefix

    _wine_prefix = _config.get(f'{app_name}.env', 'WINEPREFIX', fallback=None)
    _wine_prefix = _config.get(app_name, 'wine_prefix', fallback=_wine_prefix)
    if _wine_prefix and prefix_exists(_wine_prefix):
        return _wine_prefix

    _compat_path = _config.get('default.env', 'STEAM_COMPAT_DATA_PATH', fallback=None)
    if _compat_path and prefix_exists(_compat_prefix := os.path.join(_compat_path, 'pfx')):
        return _compat_prefix

    _wine_prefix = _config.get('default.env', 'WINEPREFIX', fallback=None)
    _wine_prefix = _config.get('default', 'wine_prefix', fallback=_wine_prefix)
    if _wine_prefix and prefix_exists(_wine_prefix):
        return _wine_prefix

    return None


__all__ = []
