# ~*~ coding: utf-8 ~*~
from django.utils.translation import gettext_lazy as _

from audits.models import OperateLog
from perms.const import ActionChoices


class OperateLogStore(object):
    # 使用 Unicode 单元分隔符\u001f，替代旧的分隔符\0 PostgreSQL 数据库不支持\0
    SEP = '\u001f'
    OLD_SEP = '\0'

    def __init__(self, config):
        self.model = OperateLog
        self.max_length = 2048
        self.max_length_tip_msg = _(
            'The text content is too long. Use Elasticsearch to store operation logs'
        )

    @staticmethod
    def ping(timeout=None):
        return True

    @classmethod
    def split_value(cls, value):
        """
        Attempt to split the string using the new separator.
        If it fails, attempt to split using the old separator.
        If both fail, return the original string and an empty string.

        :param value: The string to split
        :return: The split parts (before, after)
        """
        for sep in (cls.SEP, cls.OLD_SEP):
            parts = value.split(sep, 1)
            if len(parts) == 2:
                return parts[0], parts[1]
        return value, ''

    @classmethod
    def convert_before_after_to_diff(cls, before, after):
        if not isinstance(before, dict):
            before = dict()
        if not isinstance(after, dict):
            after = dict()

        diff = dict()
        keys = set(before.keys()) | set(after.keys())
        for k in keys:
            before_value = before.get(k, '')
            after_value = after.get(k, '')
            diff[k] = '%s%s%s' % (before_value, cls.SEP, after_value)
        return diff

    @classmethod
    def convert_diff_to_before_after(cls, diff):
        before, after = dict(), dict()
        if not diff:
            return before, after

        for k, v in diff.items():
            before_value, after_value = cls.split_value(v)
            before[k], after[k] = before_value, after_value
        return before, after

    @staticmethod
    def _get_special_handler(resource_type):
        # 根据资源类型，处理特殊字段
        resource_map = {
            'Asset permission': lambda k, v: ActionChoices.display(int(v)) if k == 'Actions' else v
        }
        return resource_map.get(resource_type, lambda k, v: _(v))

    @classmethod
    def convert_diff_friendly(cls, op_log):
        diff_list = list()
        # 标记翻译字符串
        labels = _("labels")
        operate_log_id = _("operate_log_id")
        handler = cls._get_special_handler(op_log.resource_type)
        for k, v in op_log.diff.items():
            before_value, after_value = cls.split_value(v)
            diff_list.append({
                'field': _(k),
                'before': handler(k, before_value) if before_value else _('empty'),
                'after': handler(k, after_value) if after_value else _('empty'),
            })
        return diff_list

    def save(self, **kwargs):
        log_id = kwargs.get('id', None)
        before = kwargs.pop('before') or {}
        after = kwargs.pop('after') or {}

        op_log = self.model.objects.filter(pk=log_id).first()
        if op_log is not None:
            op_log_diff = op_log.diff or {}
            op_before, op_after = self.convert_diff_to_before_after(op_log_diff)
            before.update(op_before)
            after.update(op_after)
        else:
            # 限制长度 128 OperateLog.resource.field.max_length, 避免存储失败
            max_length = 128
            resource = kwargs.get('resource', '')
            if resource and isinstance(resource, str):
                kwargs['resource'] = resource[:max_length]
            op_log = self.model(**kwargs)

        diff = self.convert_before_after_to_diff(before, after)
        if len(str(diff)) > self.max_length:
            limit = {str(_('Tips')): self.max_length_tip_msg}
            diff = self.convert_before_after_to_diff(limit, limit)

        setattr(op_log, 'LOCKING_ORG', op_log.org_id)
        op_log.diff = diff
        op_log.save()
