import os
import docker
import json
import pprint

DOCKER_BASE_URL = os.environ.get('DOCKER_BASE_URL') or "unix://tmp/docker.sock"
VOLUME_TYPE_BIND = "bind"
VOLUME_TYPE_VOLUME = "volume"


class Container:

    def __init__(self, data):
        self.id = data.get('Id')
        self.state = data.get('State')
        self.labels = data.get('Labels', {})
        self.names = data.get('Names', [])
        self.mounts = [Mount(mnt, container=self) for mnt in data.get('Mounts')]

        self.include = self.labels.get('restic-volume-backup.enabled', '').split(',')
        self.exlude = self.labels.get('restic-volume-backup.exclude', '').split(',')

    @property
    def backup_enabled(self):
        return self.labels.get('restic-volume-backup.enabled') == 'True'

    @property
    def is_running(self):
        return self.state == 'running'

    @property
    def service_name(self):
        return self.labels['com.docker.compose.service']

    @property
    def project_name(self):
        return self.labels['com.docker.compose.project']

    @property
    def is_oneoff(self):
        return self.labels['com.docker.compose.oneoff'] == 'True'

    def filter_mounts(self):
        """Get all mounts for this container matching filters"""
        for mount in self.mounts:
            if self.include:
                for pattern in self.include:
                    if pattern in mount.source:
                        yield mount
                        continue
            elif self.exlude:
                for pattern in self.exlude:
                    if pattern in mount.source:
                        continue
                    yield mount
            else:
                yield mount

    def to_dict(self):
        return {
            'Id': self.id,
            'Names': self.names,
            'State': self.state,
            'Labels': self.labels,
            'Mounts': [mnt.data for mnt in self.mounts],
            'include': self.include,
            'exclude': self.exlude,
        }


class Mount:
    """Mount wrapper"""
    def __init__(self, data, container=None):
        self._data = data
        self._container = container

    @property
    def container(self):
        return self._container

    @property
    def type(self):
        return self._data.get('Type')

    @property
    def name(self):
        return self._data.get('Name')

    @property
    def source(self):
        return self._data.get('Source')

    @property
    def destination(self):
        return self._data.get('Destination')

    def mount_string(self):
        if self.type == VOLUME_TYPE_VOLUME:
            return "- {}:{}:ro".format(self.name.split('_')[-1], self.destination)
        elif self.type == VOLUME_TYPE_BIND:
            return "- {}:{}:ro".format(self.source, self.destination)
        else:
            raise ValueError("Uknown volume type: {}".format(self.type))

    def __repr__(self):
        return str(self)

    def __str__(self):
        return str(self._data)

    def __hash__(self):
        """Uniquness for a volume"""
        if self.type == VOLUME_TYPE_VOLUME:
            return hash(self.name)
        elif self.type == VOLUME_TYPE_BIND:
            return hash(self.source)
        else:
            raise ValueError("Uknown volume type: {}".format(self.type))


class RunningContainers:

    def __init__(self):
        client = docker.Client(base_url=DOCKER_BASE_URL)
        all_containers = client.containers()
        client.close()

        self.containers = []
        self.this_container = None

        # Find the container we are running in.
        # If we don't have this information we cannot continue
        for container_data in all_containers:
            if container_data.get('Id').startswith(os.environ['HOSTNAME']):
                self.this_container = Container(container_data)

        if not self.this_container:
            raise ValueError("Cannot find metadata for backup container")

        # Gather all containers in the current compose setup
        for container_data in all_containers:
            # pprint.pprint(container_data, indent=2)
            container = Container(container_data)
            if container.project_name == self.this_container.project_name:
                if container.id != self.this_container.id:
                    self.containers.append(container)

        # for container in self.all_containers:
        #     # Weed out containers not beloging to this project.
        #     if container.project_name != self.backup_container.project_name:
        #         continue

        #     # Keep only containers with backup enabled
        #     if not container.backup_enabled:
        #         continue

        #     # and not oneoffs (started manually with run or similar)
        #     if container.is_oneoff:
        #         continue

        #     self.containers.append(container)

    # def gen_volumes(self, volume_type):
    #     """Generator yielding volumes of a specific type"""
    #     for cont in self.containers:
    #         for mnt in cont.mounts:
    #             if mnt.type == volume_type:
    #                 yield mnt

    # def volume_mounts(self):
    #     """Docker volumes"""
    #     return set(mnt for mnt in self.gen_volumes(VOLUME_TYPE_VOLUME))

    # def bind_mounts(self):
    #     """Host mapped volumes"""
    #     return set(mnt for mnt in self.gen_volumes(VOLUME_TYPE_BIND))

    def print_services(self):
        print()
        print("Backup config for compose project '{}'".format(self.this_container.project_name))
        print()

        for container in self.containers:
            print('service: {}'.format(container.service_name))
            mounts = container.filter_mounts()
            for mount in mounts:
                print(' - {}'.format(mount.source))

        print()