#!/usr/bin/env python3

"""
Cuchulufm is a series of wrappers for bottle, mako and others and construct a simple headless cms.

Copyright (C) 2023 Antonio de la Rosa Caballero

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
"""

import os
import smtplib
import mimetypes
from email import encoders
from email.message import Message
from email.mime.audio import MIMEAudio
from email.mime.base import MIMEBase
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.utils import formataddr, formatdate
import ssl as ssl_module
import sys

class SendMail:
    """Class for send email
    """
    
    port=587
    
    host='localhost'
    
    username=''
    
    password=''

    #ssl=True

    def __init__(self, ssl=True):
        """Class for send email

        Class for send email using standard python library
        
        Attributes:
            port (int): The port used for send email, by default 587
            host (str): The hostname of mail server used for send the email
            username (str): The username for login in mail server
            password (str): The password for login in mail server
            smtp (smtplib.SMTP): The python SMTP object used for send emails
            txt_error: (str): If error, is saved in this attribute
            
        """


        self.smtp=None #smtplib.SMTP(host=self.host, port=self.port)
        self.txt_error=''
        self.ssl=ssl
    
        if sys.version_info < (3, 10):
        
            self.context = ssl_module.SSLContext(ssl_module.PROTOCOL_TLS)
        else:
            self.context = ssl_module.SSLContext(ssl_module.PROTOCOL_TLS_CLIENT)
    
    def connect(self):
        
        self.smtp=smtplib.SMTP(host=self.host, port=self.port)
        
        if self.ssl==True:
            
            error=False
            
            try:
                
                self.smtp.starttls(context=self.context)
                
            except smtplib.SMTPHeloError:
                
                self.txt_error='Error: cannot make HELO to this server'
                
                error=True

            except RuntimeError:
                
                self.txt_error='Error: SSL/TLS is not supported in your python interpreter'
                
                error=True
            
            
            except ssl.SSLCertVerificationError: 
                
                self.txt_error='Error: certificate verify failed, unable to get local issuer certificate. Install pip-system-certs can help.'
                
                error=True
                
                
            except smtplib.SMTPException as e:
                
                self.txt_error=e.__str__()
                
                error=True
            
            if error:
                
                self.smtp.quit()
                
                return False
        
    
        #login
    
        if self.smtp!=None:
            
            try:
                
                self.smtp.login(self.username, self.password)
                
            except smtplib.SMTPHeloError:
                
                self.txt_error='Error: cannot make HELO to this server'
                
                return False
            
            except smtplib.SMTPAuthenticationError as eauth:
                
                self.txt_error='Error: cannot login. Wrong username or password '+eauth.__str__()
                
                return False
            
            except smtplib.SMTPException as e:
                #                self.txt_error=e.__str__()
                self.txt_error='Error: any method for login is avaliable - '+e.__str__()
                
                return False
                
        return True
    
    def send(self, from_address, to_address: list, subject, message, content_type='plain', attachments=[], reply_to=()):
        """ Method for send email
        
        With this method you can send email to multiple address, html, and add attachments to email
        
        Args:
            from_address (str): The adress used for send the email
            to_address (list): A list of emails where the email will be sended.
            subject (str): The subject of the email
            message (str): The content of the message
            content_type (str): The type of mail content, can be plain or html.
            attachments (list): A list with a serie of file paths for attach to the email. 
        """
        
        if self.smtp==None:
            if not self.connect():
                return False
        
        if len(reply_to)==0:
            reply_to=(from_address, from_address)
        
        
        COMMASPACE=', '

        if len(attachments)==0:
            
            msg=MIMEText(message, content_type)
            
            msg['Subject']=subject
            
            msg['Reply-To']=formataddr(reply_to)
            
            msg['From']=from_address
            
            msg['To']=COMMASPACE.join(to_address)
            
            self.smtp.send_message(msg)
            
            #self.quit()
            
            return True

        else:
            
            outer=MIMEMultipart()
            
            outer['Subject']=subject
            
            msg['Reply-To']=formataddr(reply_to)
            
            outer['From']=from_address
            
            outer['To']=COMMASPACE.join(to_address)
            
            #outer['Date']=formatdate()
            
            #Attach message text
            
            msg=MIMEText(message, content_type)
            
            outer.attach(msg)
            
            for path in attachments:
                
                ctype, encoding = mimetypes.guess_type(path)
                
                if ctype is None or encoding is not None:
                    #No guess could be made, or the file is encoded (compressed), so
                    #use a generic bag-of-bits type.
                    ctype = 'application/octet-stream'
                    
                maintype, subtype = ctype.split('/', 1)
                
                if maintype == 'text':
                    with open(path) as fp:
                            # Note: we should handle calculating the charset
                        msg = MIMEText(fp.read(), _subtype=subtype)
                        
                elif maintype == 'image':
                    with open(path, 'rb') as fp:
                        msg = MIMEImage(fp.read(), _subtype=subtype)
                
                elif maintype == 'audio':
                    with open(path, 'rb') as fp:
                        msg = MIMEAudio(fp.read(), _subtype=subtype)
                
                else:
                    with open(path, 'rb') as fp:
                        msg = MIMEBase(maintype, subtype)
                        msg.set_payload(fp.read())
                    #Encode the payload using Base64
                    encoders.encode_base64(msg)
                
                #Set the filename parameter
                msg.add_header('Content-Disposition', 'attachment', filename=os.path.basename(path))
                    
                outer.attach(msg)
            
            self.smtp.send_message(outer)
            
            #self.quit()
            
            return True
        
    def quit(self):
        """Function used when you need quit connection for any reason"""
        
        if self.smtp!=None:
            self.smtp.quit()
            self.smtp=None
    
    def __del__(self):
        """Method for clean the connection when the object is closed"""
        
        if self.smtp!=None:
        
            self.smtp.quit()
    
