from __future__ import absolute_import
import os
import time


INTEGRATION_TESTS = os.environ.get('INTEGRATION_TESTS', False)


if INTEGRATION_TESTS:
    from compose.cli.command import get_project
    from compose.service import BuildAction
    from compose.service import ConvergenceStrategy


class ComposeMixin(object):
    """
    Manage docker-compose to ensure that needed services are running during tests
    """

    # List of required services to run INTEGRATION_TESTS
    COMPOSE_SERVICES = []

    # docker-compose.yml dir path
    COMPOSE_PROJECT_DIR = '.'

    # timeout waiting for health (seconds)
    COMPOSE_TIMEOUT = 300

    @classmethod
    def compose_up(cls):
        """
        Ensure *only* the services defined under `COMPOSE_SERVICES` are running and healthy
        """
        if not INTEGRATION_TESTS or not cls.COMPOSE_SERVICES:
            return

        if os.environ.get('NO_COMPOSE'):
            return

        def print_logs(container):
            print("---- " + container.name_without_project)
            print(container.logs())
            print("----")

        def is_healthy(container):
            return container.inspect()['State']['Health']['Status'] == 'healthy'

        project = cls.compose_project()
        project.up(
            strategy=ConvergenceStrategy.always,
            service_names=cls.COMPOSE_SERVICES,
            do_build=BuildAction.force,
            timeout=30)

        # Wait for them to be healthy
        start = time.time()
        while True:
            containers = project.containers(
                service_names=cls.COMPOSE_SERVICES,
                stopped=True)

            healthy = True
            for container in containers:
                if not container.is_running:
                    print_logs(container)
                    raise Exception(
                        "Container %s unexpectedly finished on startup" %
                        container.name_without_project)
                if not is_healthy(container):
                    healthy = False
                    break

            if healthy:
                break

            time.sleep(1)
            timeout = time.time() - start > cls.COMPOSE_TIMEOUT
            if timeout:
                for container in containers:
                    if not is_healthy(container):
                        print_logs(container)
                raise Exception(
                    "Timeout while waiting for healthy "
                    "docker-compose services: %s" %
                    ','.join(cls.COMPOSE_SERVICES))

    @classmethod
    def compose_down(cls):
        """
        Stop all running containers
        """
        if INTEGRATION_TESTS and cls.COMPOSE_SERVICES:
            cls.compose_project().kill(service_names=cls.COMPOSE_SERVICES)

    @classmethod
    def compose_hosts(cls):
        if not INTEGRATION_TESTS or not cls.COMPOSE_SERVICES:
            return []

        hosts = []
        for container in cls.compose_project().containers(service_names=cls.COMPOSE_SERVICES):
            network_settings = container.inspect()['NetworkSettings']
            for network in network_settings['Networks'].values():
                if network['IPAddress']:
                    hosts.append(network['IPAddress'])
        return hosts

    @classmethod
    def compose_project(cls):
        return get_project(cls.COMPOSE_PROJECT_DIR, project_name=os.environ.get('DOCKER_COMPOSE_PROJECT_NAME'))
