import io
import json
import logging
import typing as t
from json import JSONDecodeError

import poyo

GivenInterpreters = t.Mapping[str, t.Sequence[str]]

logger = logging.getLogger(__name__)


def load_yaml(config_file) -> t.MutableMapping:
    # TODO use a proxy to load yaml
    with io.open(config_file, encoding='utf-8') as file_handle:
        try:
            yaml_dict = poyo.parse_string(file_handle.read())
        except poyo.exceptions.PoyoException as error:
            raise InvalidYamlFormatError(
                'Unable to parse YAML file {}. Error: {}' ''.format(config_file, error)
            ) from error
    return yaml_dict


def get_interpreters_from_yaml(config_file: str) -> t.Optional[GivenInterpreters]:
    """Parse the 'interpreters' variable out of the user's config yaml file.

    Args:
        config_file (str): path to the user's config yaml file

    Raises:
        InvalidYamlFormatError: if yaml parser fails to load the user's config
        UserYamlDesignError: if yaml does not contain the 'default_context' key

    Returns:
        GivenInterpreters: dictionary with intepreters as a sequence of strings,
            mapped to the 'supported-interpreters' key
    """
    data = load_yaml(config_file)
    if 'default_context' not in data:
        raise UserYamlDesignError(
            "User config (is valid yaml but) does not contain a 'default_context' outer key!"
        )
    context = data['default_context']

    try:
        return json.loads(context['interpreters'])
    except (KeyError, JSONDecodeError) as error:
        logger.warning(
            "User's yaml config 'interpreters' value Error: %s",
            json.dumps(
                {
                    'error': str(error),
                    'message': "Expected json-parsable value for the 'interpreters' key",
                },
                sort_keys=True,
                indent=4,
            ),
        )
    return None


class UserYamlDesignError(Exception):
    pass


class InvalidYamlFormatError(Exception):
    pass
