import os
import boto3

SNS_TOPIC_ARN = os.environ.get('SNS_TOPIC_ARN')


class Notifier():
    def __init__(self, client=None, sns_topic_arn: str = ""):
        assert SNS_TOPIC_ARN or sns_topic_arn, 'SNS_TOPIC_ARN ENV VAR MUST BE SET'
        self.client = client if client else boto3.Session().client('sns')
        self.sns_topic = sns_topic_arn if sns_topic_arn else SNS_TOPIC_ARN

    def notify(self, message: str, subject: str = "", attributes: dict = None):
        if not attributes:
            attributes = {}
        message_attributes = {
            key: {
                'DataType': 'String',
                'StringValue': value
            } for key, value in attributes.items()
        }
        self.client.publish(
            TopicArn=self.sns_topic,
            Message=self._validate_message(message),
            Subject=self._validate_subject(subject),
            MessageAttributes=message_attributes
        )

    @staticmethod
    def _validate_subject(subject: str) -> str:
        '''Validate a message subject by asserting it is of type 'str' and
        < 100 characters. if greater, truncates the message to 96 characters
        with a trailing elipsis to indicate the string was truncated
        '''
        assert isinstance(subject, str), f'Subject must be a string! Got: {type(subject)}'
        if len(subject) > 100:
            subject = subject[:96] + ' ...'
        return subject

    @staticmethod
    def _validate_message(message) -> str:
        '''Validate a message body by asserting it is of type 'str'
        and < 256kb. if greater, the message is split in half, and only the
        first 128kb-3 and last 128kb-3 are sent, seperated by "\n...\n" to
        indicate the message was truncated
        '''
        assert isinstance(message, str), f'Message body must be a string! Got: {type(message)}'
        if len(message.encode('utf-8')) > 256 * 1000:
            start = message.encode('utf-8')[:128 * 1000 - 3].decode('utf-8')
            end = message.encode('utf-8')[-128 * 1000 + 3:].decode('utf-8')
            message = start + '\n...\n' + end
        return message
