#!/usr/bin/env python3
"""Validate all integrations have manifests and that they are valid."""
import json
import pathlib
import sys

import voluptuous as vol
from voluptuous.humanize import humanize_error


MANIFEST_SCHEMA = vol.Schema({
    vol.Required('domain'): str,
    vol.Required('name'): str,
    vol.Required('documentation'): str,
    vol.Required('requirements'): [str],
    vol.Required('dependencies'): [str],
    vol.Required('codeowners'): [str],
})


COMPONENTS_PATH = pathlib.Path('homeassistant/components')


def validate_dependency(path, dependency, loaded, loading):
    """Validate dependency is exist and no circular dependency."""
    dep_path = path.parent / dependency
    return validate_integration(dep_path, loaded, loading)


def validate_integration(path, loaded, loading):
    """Validate that an integrations has a valid manifest."""
    errors = []
    path = pathlib.Path(path)

    manifest_path = path / 'manifest.json'

    if not manifest_path.is_file():
        errors.append('Manifest file {} not found'.format(manifest_path))
        return errors  # Fatal error

    try:
        manifest = json.loads(manifest_path.read_text())
    except ValueError as err:
        errors.append("Manifest contains invalid JSON: {}".format(err))
        return errors  # Fatal error

    try:
        MANIFEST_SCHEMA(manifest)
    except vol.Invalid as err:
        errors.append(humanize_error(manifest, err))

    if manifest['domain'] != path.name:
        errors.append('Domain does not match dir name')

    for dep in manifest['dependencies']:
        if dep in loaded:
            continue
        if dep in loading:
            errors.append("Found circular dependency {} in {}".format(
                dep, path
            ))
            continue
        loading.add(dep)

        errors.extend(validate_dependency(path, dep, loaded, loading))

    loaded.add(path.name)
    return errors


def validate_all():
    """Validate all integrations."""
    invalid = []

    for fil in COMPONENTS_PATH.iterdir():
        if fil.is_file() or fil.name == '__pycache__':
            continue

        errors = validate_integration(fil, set(), set())

        if errors:
            invalid.append((fil, errors))

    if not invalid:
        return 0

    print("Found invalid manifests")
    print()

    for integration, errors in invalid:
        print(integration)
        for error in errors:
            print("*", error)
        print()

    return 1


if __name__ == '__main__':
    sys.exit(validate_all())