# #region providers [C:5] [TYPE Module] [SEMANTICS notification, provider, smtp, telegram, slack]
#
# @BRIEF Defines abstract base and concrete implementations for external notification delivery.
# @RELATION CALLED_BY -> [NotificationService]
# @RELATION DEPENDS_ON -> [NotificationProvider]
# @RELATION DEPENDS_ON -> [SMTPProvider]
# @RELATION DEPENDS_ON -> [TelegramProvider]
# @RELATION DEPENDS_ON -> [SlackProvider]
# @LAYER Infrastructure
# @PRE  Provider configuration dictionaries are supplied by trusted configuration sources.
# @POST  Each provider exposes async send contract returning boolean delivery outcome.
# @SIDE_EFFECT  Performs outbound network I/O to SMTP or HTTP endpoints.
# @DATA_CONTRACT  Input[target, subject, body, context?] -> Output[bool]
# @INVARIANT  Concrete providers preserve boolean send contract and swallow transport exceptions into False.
#
# @INVARIANT  Providers must be stateless and resilient to network failures.
# @INVARIANT  Sensitive credentials must be handled via encrypted config.

from abc import ABC, abstractmethod
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
import smtplib
from typing import Any

import requests

from ...core.logger import logger


# #region NotificationProvider [C:2] [TYPE Class]
# @BRIEF Abstract base class for all notification providers.
# @RELATION CALLED_BY -> [SMTPProvider]
# @RELATION CALLED_BY -> [TelegramProvider]
# @RELATION CALLED_BY -> [SlackProvider]
class NotificationProvider(ABC):
    @abstractmethod
    async def send(
        self,
        target: str,
        subject: str,
        body: str,
        context: dict[str, Any] | None = None,
    ) -> bool:
        """
        Send a notification to a specific target.
        :param target: Recipient identifier (email, channel ID, user ID).
        :param subject: Notification subject or title.
        :param body: Main content of the notification.
        :param context: Additional metadata for the provider.
        :return: True if successfully dispatched.
        """
        pass


# #endregion NotificationProvider


# #region SMTPProvider [C:3] [TYPE Class]
# @BRIEF Delivers notifications via SMTP.
# @RELATION INHERITS -> [NotificationProvider]
class SMTPProvider(NotificationProvider):
    def __init__(self, config: dict[str, Any]):
        self.host = config.get("host")
        self.port = int(config.get("port", 587))
        self.username = config.get("username")
        self.password = config.get("password")
        self.from_email = config.get("from_email")
        self.use_tls = config.get("use_tls", True)

    async def send(
        self,
        target: str,
        subject: str,
        body: str,
        context: dict[str, Any] | None = None,
    ) -> bool:
        try:
            msg = MIMEMultipart()
            msg["From"] = self.from_email
            msg["To"] = target
            msg["Subject"] = subject
            msg.attach(MIMEText(body, "plain"))

            server = smtplib.SMTP(self.host, self.port)
            if self.use_tls:
                server.starttls()
            if self.username and self.password:
                server.login(self.username, self.password)
            server.send_message(msg)
            server.quit()
            return True
        except Exception as e:
            logger.error(
                f"[SMTPProvider][FAILED] Failed to send email to {target}: {e}"
            )
            return False


# #endregion SMTPProvider


# #region TelegramProvider [C:3] [TYPE Class]
# @BRIEF Delivers notifications via Telegram Bot API.
# @RELATION INHERITS -> [NotificationProvider]
class TelegramProvider(NotificationProvider):
    def __init__(self, config: dict[str, Any]):
        self.bot_token = config.get("bot_token")

    async def send(
        self,
        target: str,
        subject: str,
        body: str,
        context: dict[str, Any] | None = None,
    ) -> bool:
        if not self.bot_token:
            logger.error("[TelegramProvider][FAILED] Bot token not configured")
            return False

        url = f"https://api.telegram.org/bot{self.bot_token}/sendMessage"
        payload = {
            "chat_id": target,
            "text": f"*{subject}*\n\n{body}",
            "parse_mode": "Markdown",
        }

        try:
            response = requests.post(url, json=payload, timeout=10)
            response.raise_for_status()
            return True
        except Exception as e:
            logger.error(
                f"[TelegramProvider][FAILED] Failed to send Telegram message to {target}: {e}"
            )
            return False


# #endregion TelegramProvider


# #region SlackProvider [C:3] [TYPE Class]
# @BRIEF Delivers notifications via Slack Webhooks or API.
# @RELATION INHERITS -> [NotificationProvider]
class SlackProvider(NotificationProvider):
    def __init__(self, config: dict[str, Any]):
        self.webhook_url = config.get("webhook_url")

    async def send(
        self,
        target: str,
        subject: str,
        body: str,
        context: dict[str, Any] | None = None,
    ) -> bool:
        if not self.webhook_url:
            logger.error("[SlackProvider][FAILED] Webhook URL not configured")
            return False

        payload = {"text": f"*{subject}*\n{body}"}

        try:
            response = requests.post(self.webhook_url, json=payload, timeout=10)
            response.raise_for_status()
            return True
        except Exception as e:
            logger.error(f"[SlackProvider][FAILED] Failed to send Slack message: {e}")
            return False


# #endregion SlackProvider

# #endregion providers
