# coding: utf-8

"""
Common classes and functions for work with service allocations
"""

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

import copy
import math
from six import add_metaclass

from abc import ABCMeta, abstractmethod
from enum import Enum
from saas.library.python.gencfg import GencfgAPI

LOG_SIZE_PER_REQUEST = 5 * 1024  # 5 kilobytes
DATA_VOLUME_MULTIPLIER = 2.5
BYTES_IN_GB = 2**30


def roundup(x, mul):
    return int(math.ceil(x / mul)) * mul


def calculate_logs_volume_size(target_rps, replicas=1, target_log_ttl=4):
    """
    :param target_rps: service target rps
    :param replicas: service replicas total count
    :param target_log_ttl: target log ttl in hours
    :return: volume size in gigabytes
    """
    log_ttl_seconds = target_log_ttl * 3600
    log_size = (target_rps * LOG_SIZE_PER_REQUEST * log_ttl_seconds / replicas) / BYTES_IN_GB
    if log_size > 100:
        log_size = 100
    elif log_size < 20:
        log_size = 20
    return roundup(log_size, 10)


def calculate_cores_volume_size(memory_limit):
    """
    Calculate cores volume size from memory limit
    :param memory_limit: memory limit in bytes
    :return: cores volume size in gigabytes
    """
    cores_volume_size = memory_limit * 2 / BYTES_IN_GB
    return roundup(cores_volume_size, 1)


def calculate_data_volume_size(index_size, shards=1):
    """
    :param index_size: sla index size in bytes
    :param shards: service shards count
    :return: data volume size in gigabytes
    """
    shard_size = (index_size / shards) / BYTES_IN_GB
    shard_size = shard_size if shard_size > 20 else 20
    data_volume_size = shard_size * DATA_VOLUME_MULTIPLIER
    return roundup(data_volume_size, 10)


def calculate_root_volume_size():
    return 10


def calculate_workdir_volume_size():
    return 10


class AllocationTypes(Enum):
    GENCFG = 'GENCFG'
    YP_LITE = 'YP_LITE'


@add_metaclass(ABCMeta)
class TopologyChange():
    @abstractmethod
    def added_hosts(self):
        pass

    @abstractmethod
    def removed_hosts(self):
        pass


class YpPodsetChange(TopologyChange):
    allocation_type = AllocationTypes.YP_LITE


class GencfgGroupChange(TopologyChange):
    allocation_type = AllocationTypes.GENCFG

    def __init__(self, group, cpu=None, memory=None, ssd=None, noindexing=None, removed_hosts=None, added_hosts=None):
        """
        :param group: AnyStr
        :param cpu:  int
        :param memory:
        :param ssd:
        :param noindexing:
        :param removed_hosts:
        :param added_hosts:
        :type group: AnyStr
        :type cpu: int
        :type memory: int
        :type ssd: int
        :type noindexing: bool
        :type removed_hosts: List[str]
        :type added_hosts: List[str]
        """
        self._group = group
        self._cpu = cpu
        self._memory = memory
        self._ssd = ssd
        self._noindexing = noindexing
        self._removed_hosts = removed_hosts or []
        self._added_hosts = added_hosts or []

    @property
    def group(self):
        return self._group

    @property
    def cpu(self):
        return self._cpu

    @property
    def memory(self):
        return self._memory

    @property
    def ssd(self):
        return self._ssd

    @property
    def noindexing(self):
        return self._noindexing

    @property
    def removed_hosts(self):
        return copy.deepcopy(self._removed_hosts)

    @property
    def added_hosts(self):
        return copy.deepcopy(self._removed_hosts)


class ServiceTopologyChange(object):
    def __init__(self, changes=None, **kwargs):
        """
        :type changes: List[Union(YpPodsetChange, GencfgGroupChange)]
        """
        self._changes = changes

    @property
    def changes(self):
        return copy.deepcopy(self._changes)


class ServiceGencfgTopologyChange(ServiceTopologyChange):
    def __init__(self, commit=None, tag=None, sandbox_task=None, changes=None, **kwargs):
        """
        :type commit: int
        :type tag: saas.tools.devops.lib23.gencfg_api.GencfgTag
        :type sandbox_task: int
        :type changes: List[TopologyChange]
        """
        super(ServiceGencfgTopologyChange, self).__init__(changes=changes, **kwargs)
        self._commit = commit
        self._tag = tag
        self._sandbox_task = sandbox_task

    @property
    def commit(self):
        return self._commit

    @property
    def tag(self):
        if self._tag:
            return self._tag
        elif self._commit:
            self._tag = GencfgAPI.get_tag_by_commit(self._commit)

    def get_tag_with_wait(self):
        if self._tag:
            return self._tag
        elif self._commit:
            self._tag = GencfgAPI.wait_for_tag_with_commit(self._commit)
