"""Validate dependencies."""
import pathlib
from typing import Dict

import re
import voluptuous as vol
from voluptuous.humanize import humanize_error

from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv
from homeassistant.util.yaml import load_yaml

from .model import Integration


def exists(value):
    """Check if value exists."""
    if value is None:
        raise vol.Invalid("Value cannot be None")
    return value


FIELD_SCHEMA = vol.Schema({
    vol.Required('description'): str,
    vol.Optional('example'): exists,
    vol.Optional('default'): exists,
    vol.Optional('values'): exists,
    vol.Optional('required'): bool,
})

SERVICE_SCHEMA = vol.Schema({
    vol.Required('description'): str,
    vol.Optional('fields'): vol.Schema({
        str: FIELD_SCHEMA
    })
})

SERVICES_SCHEMA = vol.Schema({
    cv.slug: SERVICE_SCHEMA
})


def grep_dir(path: pathlib.Path, glob_pattern: str, search_pattern: str) \
        -> bool:
    """Recursively go through a dir and it's children and find the regex."""
    pattern = re.compile(search_pattern)

    for fil in path.glob(glob_pattern):
        if not fil.is_file():
            continue

        if pattern.search(fil.read_text()):
            return True

    return False


def validate_services(integration: Integration):
    """Validate services."""
    # Find if integration uses services
    has_services = grep_dir(integration.path, "**/*.py",
                            r"hass\.services\.(register|async_register)")

    if not has_services:
        return

    try:
        data = load_yaml(str(integration.path / 'services.yaml'))
    except FileNotFoundError:
        integration.add_error(
            'services', 'Registers services but has no services.yaml')
        return
    except HomeAssistantError:
        integration.add_error(
            'services', 'Registers services but unable to load services.yaml')
        return

    try:
        SERVICES_SCHEMA(data)
    except vol.Invalid as err:
        integration.add_error(
            'services',
            "Invalid services.yaml: {}".format(humanize_error(data, err)))


def validate(integrations: Dict[str, Integration], config):
    """Handle dependencies for integrations."""
    # check services.yaml is cool
    for integration in integrations.values():
        if not integration.manifest:
            continue

        validate_services(integration)