import logging
import smtplib
from smtplib import SMTPAuthenticationError

from decouple import config
from fastapi import HTTPException

logger = logging.getLogger(__name__)

class EmptySMTP:
    def sendmail(self, from_addr, to_addrs, msg, mail_options=(), rcpt_options=()):
        logger.error("!! CANNOT SEND EMAIL, NO VALID SMTP CONFIGURATION FOUND")

    def send_message(self, msg):
        self.sendmail( msg["FROM"], msg["TO"], msg.as_string() )

class SMTPClient:
    server = None

    def __init__(self):
        if config("EMAIL_HOST") is None or len(config("EMAIL_HOST")) == 0:
            return
        elif not config("EMAIL_USE_SSL", cast=bool):
            self.server = smtplib.SMTP(host=config("EMAIL_HOST"), port=config("EMAIL_PORT", cast=int))
        else:
            if len(config("EMAIL_SSL_KEY")) == 0 or len(config("EMAIL_SSL_CERT")) == 0:
                self.server = smtplib.SMTP_SSL(host=config("EMAIL_HOST"), port=config("EMAIL_PORT", cast=int))
            else:
                self.server = smtplib.SMTP_SSL(host=config("EMAIL_HOST"), port=config("EMAIL_PORT", cast=int),
                                               keyfile=config("EMAIL_SSL_KEY"), certfile=config("EMAIL_SSL_CERT"))

    def __enter__(self):
        if self.server is None:
            return EmptySMTP()
        self.server.ehlo()
        if not config("EMAIL_USE_SSL", cast=bool) and config("EMAIL_USE_TLS", cast=bool):
            self.server.starttls()
            # stmplib docs recommend calling ehlo() before & after starttls()
            self.server.ehlo()
        if len(config("EMAIL_USER", default="")) > 0 and len(config("EMAIL_PASSWORD", default="")) > 0:
            try:
                self.server.login(user=config("EMAIL_USER"), password=config("EMAIL_PASSWORD"))
            except SMTPAuthenticationError:
                raise HTTPException(401, "SMTP Authentication Error")
        return self.server

    def __exit__(self, *args):
        if self.server is None:
            return
        self.server.quit()

    def test_configuration(self):
        # check server connexion
        try:
            status = self.server.noop()[0]
            if not (status == 250):
                raise Exception(f"SMTP connexion error, status:{status}")
        except Exception as e:  # smtplib.SMTPServerDisconnected
            logger.error(
                f'!! SMTP connexion error to {config("EMAIL_HOST")}:{config("EMAIL_PORT", cast=int)}')
            logger.error(e)
            return False, e

        # check authentication
        try:
            self.__enter__()
            self.__exit__()
        except Exception as e:
            logger.error(f'!! SMTP authentication error to {config("EMAIL_HOST")}:{config("EMAIL_PORT", cast=int)}')
            logger.error(e)
            return False, e

        return True, None


VALID_SMTP = None
SMTP_ERROR = None
SMTP_NOTIFIED = False


def has_smtp():
    global VALID_SMTP, SMTP_ERROR, SMTP_NOTIFIED
    if SMTP_ERROR is not None:
        logger.error("!!! SMTP error found, disabling SMTP configuration:")
        logger.error(SMTP_ERROR)

    if VALID_SMTP is not None:
        return VALID_SMTP

    if config("EMAIL_HOST") is not None and len(config("EMAIL_HOST")) > 0:
        VALID_SMTP, SMTP_ERROR = check_connexion()
        return VALID_SMTP
    elif not SMTP_NOTIFIED:
        SMTP_NOTIFIED = True
        logger.info("no SMTP configuration found")
    return False


def check_connexion():
    # check SMTP host&port
    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.settimeout(config("EMAIL_CHECK_TIMEOUT", cast=int, default=5))
    result = sock.connect_ex((config("EMAIL_HOST"), config("EMAIL_PORT", cast=int)))
    sock.close()
    if not (result == 0):
        error = f"""!! SMTP {config("EMAIL_HOST")}:{config("EMAIL_PORT", cast=int)} is unreachable
f'please make sure the host&port are correct, and the SMTP protocol is authorized on your server."""
        logger.error(error)
        sock.close()
        return False, error

    return SMTPClient().test_configuration()
