"""function to send email"""

import os
import sys
import re
from email.mime.text import MIMEText
from email.header import Header
from email.mime.multipart import MIMEMultipart
from email.utils import formataddr
import subprocess
import lxml.etree as etree
import uuid

from empro import Empro


class Emeil():

    def __init__(self, params={}, do_verbose=False):
        self.do_verbose = do_verbose
        self.empro = Empro(do_verbose=do_verbose)
        # # the only paramater we use here is envelope_address
        # # parameters are set in the header xslt and become
        # # global to the module on header parsing
        # # having several parameters per emna is not supported,
        # # when we use a new emna, the global parameter is overwritten
        self.params = {}
        # # jexes rules, only needed for html css
        try:
            from jexes import Jexes
            self.jexes = Jexes()
        except ModuleNotFoundError:
            self.jexes = None
        self.re_nama = re.compile(r"([^<]+)<([^>]+)>\s*")
        return None

    def send(self, ingest, emna, dasion=None, dont_send=False,
             base=None):
        if base is not None:
            mail_fufi = base + '.mail'
            if os.path.isfile(mail_fufi):
                if self.do_verbose:
                    print("emeil: I skip base {mail_fufi}")
                return False
        if isinstance(ingest, etree._ElementTree):
            doc = ingest
        elif os.path.isfile(ingest):
            doc = etree.parse(ingest)
        else:
            print("email does not see the injest")
            return False
        mail_string = self.string(doc, emna, dasion=dasion)
        if mail_string is None:
            print("emeil has no string")
            return False
        if base is None:
            base = '/tmp/' + str(uuid.uuid4())[:8]
        mail_fufi = base + '.mail'
        mail_file = open(mail_fufi, 'w')
        mail_file.write(mail_string)
        mail_file.close()
        if dont_send is True:
            print(f"emailer wrote {mail_fufi}, not sent")
            return None
        self.send_file(mail_fufi)
        if base is None:
            os.remove(mail_fufi)
        return True

    def headers(self, doc):
        msg = MIMEMultipart("alternative")
        # # new version: do not use namespaces
        xp = "/*[local-name()='headers']/*[local-name()='header']"
        header_eles = doc.xpath(xp)
        for header_ele in header_eles:
            if 'name' not in header_ele.attrib:
                show_head_ele = etree.tostring(header_ele, encoding='utf-8',
                                               pretty_print=True).decode()
                print(f"email: no name in {show_head_ele}", file=sys.stderr)
                continue
            name = header_ele.attrib['name']
            if 'value' not in header_ele.attrib:
                show_head_ele = etree.tostring(header_ele, encoding='utf-8',
                                               pretty_print=True).decode()
                print(f"email: no value in {show_head_ele}", file=sys.stderr)
                continue
            value = header_ele.attrib['value']
            # # if all uppercase names, it's a parameter
            if name.upper() == name:
                self.params[name] = value
                continue
            if self.is_it_ascii(value):
                msg[name] = value
                continue
            # # deal with multiple parts separated by ','
            if ',' in value and header_ele.attrib['type'] == 'address':
                joint = ''
                for part in value.split(','):
                    out = str(self.make_address(part))
                    if out is not None:
                        joint += out + ', '
                joint = joint.strip()
                # # remove trailing comma
                if joint.endswith(','):
                    joint = joint[:-1]
                msg[name] = joint
                continue
            if header_ele.get('type') == 'address':
                out = str(self.make_address(value))
                if out is not None:
                    msg[name] = out
                continue
            msg[name] = Header(value, 'utf-8')
        if 'To' not in msg or msg['To'].strip() == '':
            print_header = str(msg)
            print(f"emailer: no destination in {print_header}",
                  file=sys.stderr)
            return None
        return msg

    def string(self, doc, emna, dasion=None):
        baked = self.empro.bake(emna, doc, dasion=dasion)
        # string = etree.tostring(html, encoding='utf-8',
        # pretty_print=True).decode()
        if 'err' in baked:
            print(baked['err'], file=sys.stderr)
            return None
        if 'log' in baked and self.do_verbose:
            print(baked['log'])
        if 'head' not in baked and 'log' in baked:
            # # print reason for barrier, assumed to end with newline
            print(baked['log'], end='')
            return None
        msg = self.headers(baked['head'])
        if msg is None:
            return None
        if 'text' in baked and baked['text'] is not None:
            part = MIMEText(baked['text'], 'plain', _charset='utf-8')
            msg.attach(part)
        if 'html' in baked and baked['html'] is not None:
            if self.jesex is None:
                xhtml = baked['html']
                html = self.inces.trans_doc(xhtml)
            else:
                html = baked['html']
            string = etree.tostring(html, encoding='utf-8',
                                    pretty_print=True).decode()
            part = MIMEText(string, 'html', _charset='utf-8')
            msg.attach(part)
        out = msg.as_string()
        return out

    def send_file(self, mail_fufi):
        mail_command = f"cat {mail_fufi} | /usr/sbin/exim4 -t"
        envelope_address = self.get_envelope_address()
        if envelope_address is not None:
            mail_command += f" -f {envelope_address}"
        # # don't send on the test machine
        # if self.is_dev():
        #    print(f"emailer wrote {mail_fufi}, not send on test machine")
        #    return None
        try:
            out = subprocess.run(mail_command, shell=True, check=True)
        except subprocess.CalledProcessError as error:
            out = mail_command + " yields " + str(error)
            print(out, file=sys.stderr)
            if self.is_on_terminal():
                sys.exit(1)
            if self.do_verbose:
                print(f"emailer removes {mail_fufi}")
                gone_fufi = mail_fufi + '.gone'
                # os.remove(mail_fufi)
                os.rename(mail_fufi, gone_fufi)
        return out

    def get_envelope_address(self):
        if 'ENVELOPE_ADDRESS' in self.params:
            return self.params['ENVELOPE_ADDRESS']
        return None

    def is_it_ascii(self, string):
        try:
            string.encode('ascii')
        except UnicodeEncodeError:
            return False
        return True

    def make_address(self, nama):
        out = self.re_nama.match(nama)
        if out is None:
            print(f"emailer can't parse '{nama}'")
            return None
        name = out.group(1).strip()
        emad = out.group(2).strip()
        # # https://stackoverflow.com/questions/10551933/python-email-mod
        # # ule-form-header-from-with-some-unicode-name-email
        address = formataddr((str(Header(name, 'utf-8')), emad))
        return address
