# coding: utf-8

from __future__ import absolute_import
from __future__ import division
from __future__ import unicode_literals

import logging

from saas.library.python.nanny_rest.service_mutable_proxy.data_volume import (
    from_nanny_dict, VaultSecretVolume, InstanceDataVolume
)
from saas.library.python.nanny_rest.service_mutable_proxy.layers_config import LayersConfig
from saas.library.python.nanny_rest.service_mutable_proxy.container import Container
from saas.library.python.yav import get_yav_client

_instance_spec_fields = ('networkProperties', 'hostProvidedDaemons', 'instancectl', 'notifyAction', 'hqPolicy',
                         'volume', 'initContainers', 'dockerImage', 'osContainerSpec', 'auxDaemons', 'instanceAccess',
                         'portoSettings', 'qemuKvm', 'layersConfig', 'type', 'id', 'containers',
                         'dockerLayersTransport', 'fileBindSettings', 'gdbSource', 'hqStatusReporter', 'boxes')


class ReadonlyInstanceSpec(object):
    __slots__ = _instance_spec_fields
    _fields = _instance_spec_fields

    def __init__(self, volume, containers, **kwargs):
        self.volume = [from_nanny_dict(v) for v in volume]
        self.containers = {c['name']: Container(self, **c) for c in containers}
        if 'layersConfig' in kwargs:
            layers_config = kwargs.pop('layersConfig')
            self.layersConfig = LayersConfig(**layers_config)
        else:
            self.layersConfig = LayersConfig()

        for k, v in kwargs.items():
            setattr(self, k, v)

    @property
    def volumes_dict(self):
        return {v.name: v for v in self.volume}

    def get_volume(self, volume_name):
        name_volume_map = self.volumes_dict
        if volume_name in name_volume_map:
            return name_volume_map[volume_name]
        else:
            return None


class InstanceSpec(ReadonlyInstanceSpec):
    LOGGER = logging.getLogger(__name__)
    INSTANCECTL_TVM_ID = 2002924
    __slots__ = _instance_spec_fields + ('nanny_service', )
    _rich_fields = ('volume', 'env', 'containers', 'layersConfig')

    def __init__(self, nanny_service, volume, containers, **kwargs):
        super(InstanceSpec, self).__init__(volume, containers, **kwargs)
        self.nanny_service = nanny_service

    def _process_vault_secret(self, secret):
        if not secret or not secret.secretId:
            raise ValueError('Vault Secret without secretId!')
        secret_id = secret.secretId
        if not all([secret.delegationToken, secret.secretVer, secret.secretName]):
            yav_client = get_yav_client()
            yav_secret = yav_client.get_secret(secret_uuid=secret_id)
            if not secret.delegationToken:
                secret.delegationToken, delegation_token_uid = yav_client.create_token(
                    secret_id,
                    tvm_client_id=self.INSTANCECTL_TVM_ID,
                    signature=self.nanny_service.name
                )
                self.LOGGER.info('Delegation token %s got for YAV secret %s', delegation_token_uid, secret_id)
            if not secret.secretVer:
                secret.secretVer = sorted(yav_secret['secret_versions'], key=lambda v: v['created_at'])[-1]['version']
            if not secret.secretName:
                secret.secretName = yav_secret['name']

    def ensure_volume(self, volume):
        volume = volume if isinstance(volume, InstanceDataVolume) else from_nanny_dict(volume)
        existing_volume = self.get_volume(volume.name)
        if existing_volume:
            if volume != existing_volume:
                raise ValueError('Partial duplicate {} found for {}'.format(existing_volume, volume))
            else:
                self.LOGGER.info('{} already present in service {}'.format(volume, self.nanny_service))
        else:
            self.add_volume(volume)

    def add_volume(self, volume):
        volume = volume if isinstance(volume, InstanceDataVolume) else from_nanny_dict(volume)
        existing_volume = self.get_volume(volume.name)
        if existing_volume:
            raise ValueError('Volume {} already present'.format(existing_volume))
        else:
            if isinstance(volume, VaultSecretVolume):
                self._process_vault_secret(volume)
            self.volume.append(volume)

    def dict_repr(self):
        base = {k: getattr(self, k, None) for k in self._fields if k not in self._rich_fields}
        base['volume'] = [v.dict() for v in self.volume]
        base['containers'] = [c.dict() for c in self.containers.values()]
        base['layersConfig'] = self.layersConfig.dict()
        return {k: v for k, v in base.items() if v is not None}
