import logging

import secrets
import hashlib
import base64

import orjson

from ..abc.processor import Processor

###

L = logging.getLogger(__name__)

###


class IntegrityEnricher(Processor):
	"""
	IntegrityEnricher is a enricher processor, which enriches JSON data
	by hashed events.

	Supported algorithms for cryptographic signing, default is SHA256
	'SHA256', 'dsaEncryption', 'MD4', 'sha256', 'sha3_512', 'DSA', 'sha3_256', 'sha3_384', 'SHA512', 'md5', 'SHA224',
	'MD5', 'sha', 'whirlpool', 'ripemd160', 'SHA384', 'ecdsa-with-SHA1', 'RIPEMD160', 'sha1', 'blake2s', 'shake_128',
	'blake2b', 'sha512', 'sha224', 'md4', 'SHA', 'dsaWithSHA', 'sha384', 'sha3_224', 'shake_256', 'DSA-SHA', 'SHA1'
	"""

	ConfigDefaults = {
		'algorithm': 'SHA256',
		'hash_key': '_id',
		'prev_hash_key': '_prev_id',
		'salt_length': 3,
	}

	def __init__(self, app, pipeline, id=None, config=None):
		super().__init__(app, pipeline, id, config)
		self.Algorithm = self.Config['algorithm']
		self.HashKey = self.Config['hash_key']
		self.PrevHashKey = self.Config['prev_hash_key']
		self.SaltLength = int(self.Config['salt_length'])
		self.PreviousHash = None

	def process(self, context, event):

		# Check that the event is a dictionary
		assert isinstance(event, dict)

		# Check if hash / previous hash already present in event and if so, delete it from event
		event.pop(self.HashKey, None)

		# Salt event - to ensure that events are not going to be the same after hash
		event["_s"] = secrets.token_urlsafe(self.SaltLength)

		# Set previous hash
		if self.PreviousHash is not None:
			event[self.PrevHashKey] = self.PreviousHash
		else:
			event.pop(self.PrevHashKey, None)

		# Hash event using key, value, key, value ... sequence
		h = hashlib.new(self.Algorithm)
		h.update(orjson.dumps(event, option=orjson.OPT_SORT_KEYS))
		hash_base64 = h.hexdigest()

		# Store the hash as base64 string
		event[self.HashKey] = hash_base64

		# Actual hash will become previous hash in the next iteration
		self.PreviousHash = hash_base64

		return event


def orjson_default(obj):
	if isinstance(obj, bytes):
		return base64.b64encode(obj).decode('ascii')
	raise TypeError
