import os
import json
import logging
from typing import Dict, Any, List, Optional

import boto3
from botocore.exceptions import ClientError

logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)


class SNS:
    def __init__(self, region_name: Optional[str] = None, environment: Optional[str] = None):
        """
        SNS helper service.
        :param region_name: AWS region, falls back to AWS_DEFAULT_REGION or us-east-1
        :param environment: environment prefix used for topic names (falls back to NODE_ENVIRONMENT or "Test")
        """
        self.environment = environment or os.getenv("NODE_ENVIRONMENT", "Test")
        region = region_name or os.getenv("AWS_DEFAULT_REGION", "us-east-1")
        self.client = boto3.client("sns", region_name=region)

    def _create_or_get_topic(self, topic_name: str) -> Optional[str]:
        """Create topic if it doesn't exist and return the TopicArn."""
        try:
            resp = self.client.create_topic(Name=topic_name)
            topic_arn = resp.get("TopicArn")
            logger.info("Topic ARN is %s", topic_arn)
            return topic_arn
        except ClientError as e:
            logger.exception("Error creating/getting topic %s: %s", topic_name, e)
            return None

    def get_attributes(self, context: List[Dict[str, str]]) -> Dict[str, Dict[str, str]]:
        """
        Convert array of {Key, Value} to boto3 MessageAttributes shape.
        Input: [{"Key": "k1", "Value": "v1"}, ...]
        Output: {"k1": {"DataType": "String", "StringValue": "v1"}, ...}
        """
        message_attributes: Dict[str, Dict[str, str]] = {}
        for item in context:
            key = item.get("Key")
            value = item.get("Value")
            if key is None or value is None:
                continue
            message_attributes[key] = {"DataType": "String", "StringValue": str(value)}
        return message_attributes

    def publish(
        self,
        topic: str,
        subject: str,
        message: str,
        message_attributes: Optional[Dict[str, Dict[str, str]]] = None,
    ) -> Optional[Dict[str, Any]]:
        """
        Publish a message to SNS topic.
        :param topic: topic suffix (will be prefixed with environment-)
        :param subject: subject string
        :param message: message body (string)
        :param message_attributes: boto3-style message attributes
        :return: boto3 response dict or None on error
        """
        try:
            topic_name = f"{self.environment}-{topic}"
            topic_arn = self._create_or_get_topic(topic_name)
            if not topic_arn:
                logger.error("Could not obtain topic ARN for %s", topic_name)
                return None

            publish_kwargs: Dict[str, Any] = {
                "TopicArn": topic_arn,
                "Message": message,
                "Subject": subject,
            }
            if message_attributes:
                publish_kwargs["MessageAttributes"] = message_attributes

            resp = self.client.publish(**publish_kwargs)
            logger.info("MessageID is %s", resp.get("MessageId"))
            return resp
        except ClientError:
            logger.exception("error occured while publishing event %s to SNS", subject)
            return None
        except Exception:
            logger.exception("unexpected error occured while publishing event %s to SNS", subject)
            return None

    def email_publish(self, tag_data: Any, context: List[Dict[str, str]]) -> None:
        """
        Publish to Chatbot-EmailHandler topic (JSON-stringified tag_data).
        """
        message_attributes = self.get_attributes(context)
        # Reuse publish — message is JSON string
        self.publish("Chatbot-EmailHandler", "Chatbot-EmailHandler", json.dumps(tag_data), message_attributes)
        logger.info("email notification published!!")

    def dispatch_email_notification(self, tag_data: Any, context: List[Dict[str, str]]) -> None:
        """
        Alias for email_publish (keeps parity with your TS code).
        """
        self.email_publish(tag_data, context)
