"""functions to add css to mail html, successor in inces"""

import os
import re
import sys

import lxml.etree as etree

import filing
from sheets import Sheets
import docing

class Jexes():

    def __init__(self, do_verbose=False, cssx_fufis=[]):
        self.sheets = Sheets()
        self.do_verbose = do_verbose
        # self.css_doc = self.load_css()
        # # parse for variable substitution
        # self.x = Xpaths()
        self.var = None
        self.prefix = None
        self.doc = None
        ## use parameter, note this is a list
        self.cssx_fufis = cssx_fufis
        if len(cssx_fufis) == 0:
            ## sets self.cssx_fufis
            self.set_fufi_from_environment()
        if len(self.cssx_fufis) == 0:
            print("jesex has no cssx files")
            sys.exit(1)
        # self.load_css()
        self.locs = None
        ## read the locs at module start
        self.read_locs()
        return None

    def set_fufi_from_environment(self):
        if 'CSSXFILES' not in os.environ:
            self.path = None
            print("jexes has no CSSXFILES", file=sys.stderr)
            sys.exit()
        path = os.environ['CSSXFILES']
        parts = path.split(':')
        cssx_fufis = []
        for part in parts:
            # # optionally allow for relative paths
            if not path.startswith('/') and 'HOME' in os.environ:
                part = os.environ['HOME'] + '/' + part
            if not os.path.isfile(part):
                print(f"jesex does not see {part}", file=sys.stderr)
                continue
            cssx_fufis.append(part)
        if len(cssx_fufis) == 0:
            print("jesex has no cssx files", file=sys.stderr)
            return
        self.cssx_fufis = cssx_fufis

    def trans_fufi(self, fufi, locs=None):
        """external callers transform a fufi"""
        if not os.path.isfile(fufi):
            if self.do_verbose:
                print(f"jexes does not see {fufi}", file=sys.stderr)
                return None
        if not fufi.endswith('.xhtml'):
            print("jexes needs .xhtml input",
                  file=sys.stderr)
        doc = filing.parse_lax(fufi)
        # # if we run from the comand line, we don’t have the locs
        # locs = self.read_locs()
        out_doc = self.trans_doc(doc, locs)
        out_fufi = fufi[:-6] + '.html'
        string = docing.show(out_doc)
        has_it_been_written = filing.srite(out_fufi, string,
                                           do_verbose=self.do_verbose)
        return has_it_been_written

    def trans_doc(self, doc, locs=None):
        if locs is None:
            locs = self.locs
        """external callers by doc"""
        self.prefix = '{' + self.get_ns(doc) + '}'
        self.remove_css_link(doc)
        eles = doc.xpath('//*')
        for ele in eles:
            self.trans_ele(ele, locs)
        return doc

    def remove_css_link(self, doc):
        # xp = "/h:html/h:head/h:link[@rel='stylesheet']"
        xp = "/*[local-name()='html']/*[local-name()='head']"
        xp += "/*[local-name()='link'][@rel='stylesheet']"
        # link_ele = self.x.none_or_one(doc, xp)
        link_eles = doc.xpath(xp)
        if link_eles is None:
            return False
        count_links = len(link_eles)
        if count_links > 1:
            print(f"jexes sees {count_links} stylesheet links",
                  file=sys.stderr)
            quit()
        link_ele = link_eles[0]
        link_eles[0].getparent().remove(link_ele)
        return True

    def get_ns(self, doc):
        """there should be a better way to do this"""
        root_ele = doc.getroot()
        tag = root_ele.tag
        tag = tag[1:]
        ns = tag.partition('}')[0]
        self.ns_len = len(ns)
        # self.const['nsmap']['h'] = ns
        ## makes is available to self.x
        #self.x.const['nsmap'] = self.const['nsmap']
        return ns

    def trans_ele(self, ele, locs):
        if 'class' in ele.attrib:
            the_class = '.' + ele.attrib['class']
            if the_class in locs['class']:
                self.add_style(ele, locs['class'][the_class])
        if 'id' in ele.attrib:
            the_id = '#' + ele.attrib['id']
            if the_id in locs['id']:
                self.add_style(ele, locs['id'][the_id])
        ## the should be a better way to get the local name
        name = ele.tag.replace(self.prefix, '')
        if name in locs['name']:
            self.add_style(ele, locs['name'][name])

    def add_style(self, ele, string):
        if 'style' in ele.attrib:
            ele.attrib['style'] += ' ' + string
            return
        ele.attrib['style'] = string

    def get_res(self):
        res = {}
        ## simple class
        res['class'] = re.compile(r'\.[a-z_]+$')
        res['id'] = re.compile(r'#[a-z_]+$')
        res['name'] = re.compile(r'[a-z]([a-z]+|[0-9])$')
        return res

    def get_var(self):
        # xp = '/n:rules/n:var'
        xp = "/*[local-name()='rules']/*[local-name()='var']"
        var_eles = self.css_doc.xpath(xp)
        var = {}
        for var_ele in var_eles:
            if 'name' not in var_ele.attrib:
                err = docing.show(var_ele)
                print("jexes: no name for variable {err}",
                      file=sys.stderr)
                continue
            if 'value' not in var_ele.attrib:
                err = docing.show(var_ele)
                print(f"jexes: no value for variable {err}",
                      file=sys.stderr)
                continue
            var[var_ele.attrib['name']] = var_ele.attrib['value']
        return var

    def read_locs(self):
        """externel caller gets the locs"""
        # # populate self.css_doc
        self.css_doc = self.load_css()
        # # variable names set in css.xml (var= vs lit=)
        self.var = self.get_var()
        # # regular expressions to parse <loc> element
        self.res = self.get_res()
        # # we don't use /n:rules/n:media, should be obsoleted anyway
        xp = "/*[local-name()='rules']/*[local-name()='rule']"
        xp += "/*[local-name()='loc']"
        # xp = '/n:rules/n:rule/n:loc'
        # loc_eles = self.x.run(self.css_doc, xp)
        loc_eles = self.css_doc.xpath(xp)
        locs = {}
        # # make sure these always exist
        locs['id'] = {}
        locs['class'] = {}
        locs['name'] = {}
        for loc_ele in loc_eles:
            loc = loc_ele.text
            found = False
            for name in self.res:
                if self.res[name].match(loc):
                    found = True
                    rules = self.get_rules(loc)
                    locs[name][loc] = rules
                    break
            if not found and self.do_verbose:
                print(f"jexes can not implement '{loc}'")
                continue
        self.locs = locs
        return locs

    def load_css(self):
        ## this works for nitpo
        #if not self.has_conf('files', 'css_xml'):
        #    print("jexes needs Ф[files][css_xml]")
        #    quit()
        #css_fufi = self.conf['files']['css_xml']
        cssx_fufis = self.cssx_fufis
        css_xml = filing.parse_lax(cssx_fufis[0]).getroot()
        count = 1
        while count < len(self.cssx_fufis):
            cssx_fufi = cssx_fufis[count]
            doc = filing.parse_lax(cssx_fufi)
            for child in doc.xpath('/child::node()'):
                css_xml.append(child)
                count = count + 1
        self.css_doc = etree.ElementTree(css_xml)
        return self.css_doc

    def get_rules(self, loc):
        # xp = f"/n:rules/n:rule/n:loc[text()='{loc}']"
        xp = "/*[local-name()='rules']/*[local-name()='rule']"
        xp += f"/*[local-name()='loc'][text()='{loc}']"
        rule_eles = self.css_doc.xpath(xp)
        if len(rule_eles) == 0:
            if self.do_verbose:
                print(f"jexes can not find a rule for {loc}")
                return None
        # xp = '../n:prop'
        xp = "../*[local-name()='prop']"
        rules = {}
        for rule_ele in rule_eles:
            # prop_eles = self.x.run(rule_ele, xp)
            prop_eles = rule_ele.xpath(xp)
            for prop_ele in prop_eles:
                if 'name' not in prop_ele.attrib:
                    err = docing.show(prop_ele)
                    print(f"jexes: no name for variable {err}",
                          file=sys.stderr)
                    continue
                name = prop_ele.attrib['name']
                if name in rules:
                    if self.do_verbose:
                        print(f"jexes find duplicate rule {name} in {loc}")
                else:
                    rules[name] = ''
                # # normal case
                if 'lit' in prop_ele.attrib:
                    rules[name] += prop_ele.attrib['lit']
                    continue
                # # no @lit, no @var
                if 'var' not in prop_ele.attrib:
                    if self.do_verbose:
                        print("jexes: no lit= and no var= in {name} at {loc}")
                    continue
                # # var case
                var = prop_ele.attrib['var']
                if var not in self.var:
                    if self.do_verbose:
                        print("jexes sees refer error for {name} in {loc}")
                    continue
                rules[name] += self.var[var]
        out = self.format_rules(rules)
        return out

    def format_rules(self, rules):
        out = ''
        for name in rules:
            out += name + ': ' + rules[name] + '; '
        out = out[:-2]
        return out
