# -*- coding: utf-8 -*-
import logging
import os
import pwd

try:  # Temporary hack to support maps build
    import maps.pylibs.utils.lib.yandex_environment
    import library.python.find_root
except:
    pass

from lxml import etree as ET


class ConfigValueNotFound(RuntimeError):
    pass


class NotFound(LookupError):
    pass


class SENTINEL(object):
    pass


def login():
    return pwd.getpwuid(os.getuid())[0]


def iter_config_files():
    try:  # Temporary hack to support maps build
        yenv = maps.pylibs.utils.lib.yandex_environment.type()
        if yenv == 'development':
            root = library.python.find_root.detect_root(os.getcwd())
            if root is not None:
                yield os.path.join(root, 'maps/wikimap/mapspro/cfg/services/services.local.{}.xml'.format(login()))
    except GeneratorExit:
        raise
    except:
        pass

    yield '/etc/yandex/maps/wiki/services/services.xml'


def guess_config():
    for fpath in iter_config_files():
        if os.access(fpath, os.F_OK):
            return fpath
    # Temporary hack to fix arcadia import errors
    logging.error("can't find config")
    # raise NotFound("can't find config")
    return None


class Config(object):
    """ Этот класс предназначен для разбора конфигов и доступа к
        параметрам по XPath.
    """
    def __init__(self, config_filename=None):
        """ Конструктор сначала пытается загрузить конфиг из файла с именем `config_filename`.
            Если аргумент не задан, то будет предпринята попытка угадать имя конфигурационного с помощью
            функции guess_config."""
        self._cfg = None
        self._base = None

        self.config_filename = config_filename or guess_config()

        # Temporary hack to fix arcadia import errors
        if self.config_filename is None:
            return

        self._cfg = ET.parse(self.config_filename)

        base_filename = self.get('extends', None)
        if base_filename is not None:
            if os.path.sep not in base_filename and \
                    os.path.sep in self.config_filename:
                base_filename = os.path.join(
                    os.path.dirname(self.config_filename),
                    base_filename)

            self._base = Config(config_filename=base_filename)

    def get(self, path, default=SENTINEL):
        """ Возвращает значение элемента по указанному XPath.
            Может принимать значение по умолчанию. Если значение
            по-умолчанию не указано, а элемент на найден, то кидается
            исключение ytools.exceptions.ConfigValueNotFound.
        """
        try:
            return self._cfg.find(path).text
        except AttributeError:
            if self._base:
                return self._base.get(path, default)

            if default is SENTINEL:
                raise ConfigValueNotFound('Node "%s" not found' % path)
            return default

    def get_attr(self, path, attrname, default=SENTINEL):
        """ Возвращает значение атрибута элемента по указанному XPath.
            Может принимать значение по умолчанию. Если значение
            по-умолчанию не указано, а элемент на найден или у него
            отстутствует атрибут с заданным именем, то кидается
            исключение ytools.exceptions.ConfigValueNotFound.
        """
        try:
            return self._cfg.find(path).attrib[attrname]
        except (KeyError, AttributeError):
            if self._base:
                return self._base.get_attr(path, attrname, default)

            if default is SENTINEL:
                raise ConfigValueNotFound('Attribute "%s" at path "%s" not found' % (attrname, path))
            return default

    def xpath(self, path):
        """Возвращает xpath от конфига"""
        res = self._cfg.xpath(path)
        if not res and self._base:
            res = self._base.xpath(path)
        return res

    def get_all(self, path):
        """ Возвращает значения всех элементов, соответствующих
            указанному XPath выражению, как список. В случае, если ничего
            не найдено, результатом будет пустой список.
        """
        return [value.text for value in self._cfg.findall(path)]
