import os
from typing import Optional

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: Optional[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
