import os
from typing import Mapping, Any, Optional, cast, Dict

from debputy.lsp.config.config_options import (
    DebputyConfigOption,
    ALL_DEBPUTY_CONFIG_OPTIONS,
)
from debputy.lsp.config.parser import DEBPUTY_CONFIG_PARSER
from debputy.manifest_parser.util import AttributePath
from debputy.util import T
from debputy.yaml import MANIFEST_YAML


class DebputyConfig:

    def __init__(self, config: Dict[DebputyConfigOption[Any], Any]) -> None:
        self._config = config

    def config_value(self, config_option: DebputyConfigOption[T]) -> Optional[T]:
        return cast("Optional[T]", self._config.get(config_option))


def _resolve(parsed_config: Mapping[str, Any], config_name: str) -> Optional[Any]:
    parts = config_name.split(".")
    container = parsed_config
    value: Optional[Any] = None
    for part in parts:
        if isinstance(container, Mapping):
            value = container.get(part)
            container = value
        else:
            return None
    return value


def _read_option(
    parsed_config: Mapping[str, Any],
    config_option: DebputyConfigOption[T],
) -> Optional[T]:
    result = _resolve(parsed_config, config_option.config_name)
    if result is None:
        return config_option.default_value
    assert isinstance(result, config_option.value_type)
    return result


def load_debputy_config() -> DebputyConfig:
    config_base = os.environ.get("XDG_CONFIG_DIR", os.path.expanduser("~/.config"))
    config_file = os.path.join(config_base, "debputy", "debputy-config.yaml")
    try:
        with open(config_file) as fd:
            parsed_yaml = MANIFEST_YAML.load(fd)
    except FileNotFoundError:
        parsed_yaml = None
    if not parsed_yaml:
        return DebputyConfig({})
    parsed_config = DEBPUTY_CONFIG_PARSER.parse_input(
        parsed_yaml,
        AttributePath.root_path(config_file),
        parser_context=None,
    )
    debputy_config = {}
    for config_option in ALL_DEBPUTY_CONFIG_OPTIONS:
        value = _read_option(parsed_config, config_option)
        debputy_config[config_option] = value
    return DebputyConfig(debputy_config)
