import itertools as it

__all__ = [
    'Action',
    'ManuallyResolve',
    'Create',
    'Remove',
    'Compress',
    'Mount',
    'Unmount',
    'Freeze',
    'ConvertToReplicated',
]


class Action(object):
    """ Some change """
    _msg_templ = None
    _safe = False  # set to True if action can not destroy data
    unrealizable = False  # set to True if this action can not be performed

    def __init__(self, table):
        super(Action, self).__init__()
        self._table = table

    def __call__(self):
        raise NotImplementedError()

    def __eq__(self, value):
        if isinstance(value, Action):
            return (
                type(self) == type(value) and
                self._table.path == value._table.path and
                self._table.yt_client is value._table.yt_client
            )
        return super(Action, self).__eq__(value)

    def _get_pretty_args(self):
        return {'path': self._table.path}

    @property
    def safe(self):
        return self._safe

    @property
    def pretty_msg(self):
        """ Human-readable action description """
        if self._msg_templ is not None:
            args = self._get_pretty_args()
            return self._msg_templ % args
        raise NotImplementedError()


class ManuallyResolve(Action):
    _msg_templ = 'do not know what to do, please resolve problem manually'
    _safe = True
    unrealizable = True

    def __call__(self):
        raise ValueError('do not know what to do')


class Create(Action):
    _msg_templ = 'create %(path)s'

    def __init__(self, *args, **kwargs):
        super(Create, self).__init__(*args)
        self._args = kwargs

    @property
    def safe(self):
        return not self._args.get('force', False)

    def __call__(self):
        self._table.create_table(**self._args)


class Remove(Action):
    _msg_templ = 'remove %(path)s'

    def __call__(self):
        self._table.remove()


class Compress(Action):
    _msg_templ = 'compress %(path)s'
    _safe = True

    def __call__(self):
        self._table.compress()


class Mount(Action):
    _msg_templ = 'mount %(path)s'
    _safe = True

    def __call__(self):
        self._table.mount()


class Unmount(Action):
    _msg_templ = 'unmount %(path)s'
    _safe = True

    def __call__(self):
        self._table.unmount()


class Freeze(Action):
    _msg_templ = 'freeze %(path)s'
    _safe = True

    def __call__(self):
        self._table.freeze()


def _chunked(items, chunk_size=1):
    items = iter(items)
    while True:
        chunk = list(it.islice(items, chunk_size))
        if not chunk:
            break
        yield chunk


class ConvertToReplicated(Action):
    _msg_templ = 'convert %(path)s to replicated table'
    _safe = False

    def __init__(self, table, source_table=None):
        assert source_table
        super(ConvertToReplicated, self).__init__(table)
        self._source_table = source_table

    def __call__(self):
        yt = self._source_table.yt_client
        tmp_path = self._source_table.path + '_not_replicated_tmp'
        self._source_table.mount(sync=True)
        self._source_table.freeze(sync=True)
        yt.move(self._source_table.path, tmp_path)
        self._table.create_table()
        for chunk in _chunked(yt.read(tmp_path), chunk_size=70000):
            self._table.insert_rows(chunk)
        yt.remove(tmp_path)
