import sys
import os
import glob

import dekan
import docing
from ishus import Ishus

# from riffer import Riffer


class Beric:
    """basic functions for a report (Bericht)"""

    def __init__(self, erimp, repcode, do_verbose=False):
        self.e = erimp
        self.repcode = repcode
        # # set some basics about the report
        self.set_dirs()
        self.do_verbose = do_verbose
        self.nsmap = self.e.abovo.nsmap
        self.ishus = Ishus(erimp)
        self.last_issuedate = None
        return None

    def set_dirs(self):
        self.fudi = self.e.dirs['reports'] + '/' + self.repcode
        self.dirs = {}
        self.stage = {}
        for given_stage in self.e.abovo.stage:
            # # should be an object but it's a directory for now
            self.stage[given_stage] = self.fudi + '/' + given_stage
            self.dirs[given_stage] = self.fudi + '/' + given_stage
        # # the next loop should be obsolete
        # for learst in self.e.abovo.learsts:
        #    # # should be an object but it's a directory for now
        #    self.dirs[learst] = self.e.dirs['learn'] + '/' + learst + '/' \
        #        + self.repcode
        # # learn report stages
        learn_dir = self.e.abovo.constants['vardir_learn']
        for learest in self.e.abovo.learests:
            # # should be an object but it's a directory for now
            target = self.e.abovo.constants['learest_' + learest]
            self.dirs[learest] = self.fudi + '/' + learn_dir + '/' + target
        var_dir = self.e.abovo.constants['vardir_state']
        self.dirs['state'] = self.fudi + '/' + var_dir
        self.dirs['blatt'] = self.e.dirs['blatt'] + '/reports/' + self.repcode
        self.dirs['opt'] = self.fudi + '/opt'

    # # target - source times
    def delays(self, earlier, later, span=6):
        e = self.e
        recent_issuedates = e.issuedates[0:span]
        repcode = self.repcode
        later_dates = self.e.folda.lasts(self.stage[later])
        earlier_dates = e.folda.firsts(self.stage[earlier])
        if len(later_dates) == 0:
            return []
        delays = {}
        for issuedate in later_dates:
            if issuedate not in recent_issuedates:
                continue
            later_fufi = later_dates[issuedate]
            later_tist = e.f.tist(later_fufi)
            if issuedate not in earlier_dates:
                print(f'no earlier for {later} {issuedate} in {repcode}',
                      file=sys.stderr)
                continue
            earlier_fufi = earlier_dates[issuedate]
            earlier_tist = e.f.tist(earlier_fufi)
            delay = later_tist - earlier_tist
            if delay < 0:
                print(f'{repcode}, {issuedate}: negative delay {delay}',
                      file=sys.stderr)
                delay = None
            delays[issuedate] = delay
        return delays

    def last(self, what, kind=None):
        """also fills the model time"""
        what_dir = self.dirs[what]
        glob_string = what_dir + '/*'
        if kind is not None:
            glob_string = what_dir + '/' + kind
        if not os.path.isdir(what_dir):
            return None
        list_of_fufis = glob.glob(glob_string)
        if len(list_of_fufis) == 0:
            return None
        last_fufi = max(list_of_fufis, key=os.path.getmtime)
        return last_fufi

    def get_last_issuedate(self):
        if self.last_issuedate is not None:
            return self.last_issuedate
        issuedate_fufis = self.e.folda.firsts(self.dirs['sent'])
        issuedates = list(issuedate_fufis)
        if len(issuedates) == 0:
            return None
        last_issuedate = issuedates[-1]
        self.last_issuedate = last_issuedate
        return last_issuedate

    def has_it_labour(self):
        e = self.e
        last_selected = self.last('selected')
        last_selected_issuedate = e.f.issuedate(last_selected)
        last_sent = self.last('sent')
        last_sent_issuedate = e.f.issuedate(last_sent)
        if last_selected_issuedate != last_sent_issuedate:
            return True
        return False

    def is_it_adolescent(self):
        """moved to caler.is_report_mature, with opposite boolean"""
        if 'traspi' not in self.e.conf:
            return False
        count_issues_sent = len(self.e.d.lasts(self.dirs['sent']))
        if count_issues_sent < int(self.e.conf['traspi']):
            return True
        return False

    def adopercent(self):
        """how much adolescent is the report"""
        if 'traspi' not in self.e.conf:
            return 0
        count_issues_sent = len(self.e.d.lasts(self.dirs['sent']))
        traspi = int(self.e.conf['traspi'])
        return traspi - count_issues_sent / traspi

    def is_active(self, do_verbose=False):
        sources = list(self.e.d.lasts(self.dirs['source']).keys())
        sents = list(self.e.d.lasts(self.dirs['sent']).keys())
        if len(sources) == 0:
            ## no source, well prolly now
            if do_verbose:
                print(f"report: {self.repcode} has no source")
            ##
            return True
        if len(sources) == len(sents):
            if do_verbose:
                print(f"report: {self.repcode} has all issues done")
            ## something still pending
            return True
        bremse = self.bremse_level()
        if do_verbose:
            print(f"bremse {bremse}")
        # # the report is not active its bremse_level is zero
        if bremse > 0:
            return False
        # # unless we define something else, a report is always
        # # active
        if 'bremspi' not in self.e.conf:
            return True
        # # an empty report, without source file, is always
        # # active
        if self.is_it_empty():
            return True
        issuedates = self.e.issuedates
        # # this is really just not to have a crash at the next
        # # check. This should only be an issue at the start of
        # # a new implementation
        if len(issuedates) < 2:
            return True
        second_to_last_issuedate = issuedates[1]
        sourc_list = list(self.e.folda.firsts(self.dirs['source']))
        last_sourc = sourc_list[-1]
        # # second_to_last_issuedate may be the current one, as
        # # issuedate contain the next future issuedate because
        # # we need that in the brown period.
        if last_sourc >= second_to_last_issuedate:
            return True
        if bremse == 0:
            return True
        return False

    def bremse_level(self):
        """0: not, 1: laya, 2: camila"""
        e = self.e
        if 'bremspi' not in e.conf:
            return 0
        # repcode = self.repcode
        source_dafu = e.d.dates(self.dirs['source'])
        sent_dafu = e.d.dates(self.dirs['sent'])
        # # no source ? can't be bremsed
        if source_dafu is None or len(source_dafu) == 0:
            return 0
        # # some source but no sent
        # # nothing ever done
        if sent_dafu is None:
            return 0
        if len(sent_dafu) == 0:
            return 1
        early_bremse = 0
        # # how many issues have not been sent
        count_gap_issues = e.d.count_gap_issues(self.dirs['sent'])
        # # infancy of report is the same as it being
        # # less than bremspi old
        if len(sent_dafu) < int(e.conf['bremspi']):
            # early_bremse = 1
            # # if we have any gap issues
            if count_gap_issues > 1:
                bremse_level = 2
                return bremse_level
        if count_gap_issues > int(e.conf['bremspi']):
            bremse_level = 1
            return bremse_level
        return early_bremse

    def issuestate(self, repcode):
        from rixer import Rixer
        from riffer import Riffer
        import lxml.etree as et
        import filer
        e = self.e
        # from recon import Recon
        rixer = Rixer(e)
        riffer = Riffer(e)
        # report = Report(e)
        if repcode is None:
            repcode = self.repcode
        out_fufi = self.dirs['state'] + '/issues.xml'
        issue_eles = {}
        if os.path.isfile(out_fufi):
            old_doc = filer.parse_lax(out_fufi)
            if old_doc is not None:
                for issue_ele in old_doc.xpath('//e:issue',
                                               namespaces=self.nsmap):
                    issue_eles[issue_ele.attrib['date']] = issue_ele
        firsts = self.e.folda.firsts(e.report[repcode].dirs['sent'])
        ns = e.ns['ernad']
        issuedates = reversed(sorted(firsts))
        needs_a_save = False
        issues_ele = et.Element(et.QName(e.ns['ernad'], 'issues'),
                                nsmap=e.ns)
        totals = []
        issuelist = list(issuedates)
        # # create link to the latest issue
        # # this is only registered when it is false
        if len(issuelist) == 0:
            # # important to note that this report has no sent issues
            # # this is used in recon.py
            return None
        issues_ele.attrib['from_pretty'] = issuelist[-1].replace('-', '‒')
        issues_ele.attrib['until_pretty'] = issuelist[0].replace('-', '‒')
        issues_ele.attrib['from'] = issuelist[-1]
        issues_ele.attrib['until'] = issuelist[0]
        for issuedate in issuelist:
            if issuedate in issue_eles:
                issues_ele.append(issue_eles[issuedate])
                totals.append(int(issue_eles[issuedate].attrib['total']))
                continue
            needs_a_save = True
            if self.do_verbose:
                print(f"report adds issuedate {issuedate} to the issuestate")
            issue_ele = et.SubElement(issues_ele, et.QName(ns, 'issue'))
            issuedate_pretty = issuedate.replace('-', '‒')
            # # old way, info duplicated below
            issue_ele.text = issuedate_pretty
            fufi = firsts[issuedate]
            issue_ele.attrib['time'] = riffer.formatted_time(fufi)
            docs = rixer.docs(fufi)
            total = len(docs)
            totals.append(total)
            issue_ele.attrib['total'] = str(total)
            issue_ele.attrib['date'] = issuedate
            # # this the modern way
            issue_ele.attrib['issuedate_pretty'] = issuedate_pretty
        issues_ele.attrib['roughly'] = dekan.roughly(dekan.make(totals))
        et.cleanup_namespaces(issues_ele)
        if self.do_verbose:
            print(docing.show(issues_ele))
        if not needs_a_save:
            return False
        if self.do_verbose:
            print(docing.show(issues_ele))
        if self.do_verbose:
            print(f"report saves {out_fufi}")
        return filer.install_xml(issues_ele, out_fufi)

    def waiting(self):
        # e = self.e
        folda = self.e.folda
        # repcode = self.repcode
        # # this should use self.dir[sent]
        # sents = list(folda.firsts(e.report[repcode].dirs['sent']))
        sents = list(folda.firsts(self.dirs['sent']))
        # sourc = list(folda.firsts(e.report[repcode].dirs['source']))
        sourc = list(folda.firsts(self.dirs['source']))
        waits = []
        for issuedate in sourc:
            if issuedate not in sents:
                waits.append(issuedate)
        return waits

    def next_missing_issuedate(self):
        e = self.e
        repcode = self.repcode
        source_dates = self.e.folda.firsts(e.report[repcode].dirs['source'])
        # # last first
        issuedates = e.issuedates
        last_missing = None
        for issuedate in issuedates:
            if issuedate not in source_dates:
                last_missing = issuedate
                continue
            return last_missing
        return None

    def is_it_empty(self):
        # # an empty report is one that has no source files
        e = self.e
        repcode = self.repcode
        source_dates = self.e.folda.firsts(e.report[repcode].dirs['source'])
        if len(source_dates) > 0:
            return False
        return True

    def is_it_pregnant(self):
        # # a pregnant report is one that has no sent files
        e = self.e
        repcode = self.repcode
        sent_dates = self.e.folda.firsts(e.report[repcode].dirs['sent'])
        if len(sent_dates) > 0:
            return False
        return True

    def is_is_autonomous(self):
        """assumes that ocotl is run on a regular basis"""
        fina = self.e.abovo.constants['eval_grund_name']
        eval_grund_fufi = self.dirs['mocla'] + '/' + fina
        if not os.path.isfile(eval_grund_fufi):
            return True
        return False

    def audit_issues(self):
        sources = list(self.e.d.lasts(self.dirs['source']).keys())
        repcode = self.repcode
        count = 0
        out_string = ''
        missings = []
        while count < len(sources) - 1:
            curen = sources[count]
            futur = sources[count + 1]
            calcd = self.ishus.shift(curen, -1)
            if futur == calcd:
                count += 1
                continue
            out_string += f" {futur} {calcd}" + "\n"
            missings.append(calcd)
            count += 1
        if len(out_string) == 0:
            return True
        sents = list(self.e.d.lasts(self.dirs['sent']).keys())
        last_sent = sents[-1]
        # # an issue was sent after
        if last_sent < missings[-1]:
            # print(f"audit of {self.repcode} fails")
            return False
        msg = f"report: {repcode} misses"
        for issuedate in missings:
            msg += " " + issuedate
        print(msg, file=sys.stderr)
        return missings

    def audit_issuedate(self, issuedate):
        sources = self.e.d.lasts(self.dirs['source'])
        total_sources = len(list(sources.keys()))
        if total_sources == 0:
            return True
        ## if there is only one issue and it is the curret one
        if total_sources == 1:
            if issuedate in sources:
                return True
        previous_issuedate = self.ishus.shift(issuedate, 1)
        if previous_issuedate in sources:
            return True
        return False

    def needs_release(self):
        sources = list(self.e.d.lasts(self.dirs['source']).keys())
        sents = list(self.e.d.lasts(self.dirs['sent']).keys())
        if len(sources) == 0:
            ## no source, well prolly now
            print(f"report: {self.repcode} has no source")
            ##
            return False
        if len(sources) > len(sents):
            ## something still pending
            return False
        last_source = sources[-1]
        if last_source == self.e.issuedates[0]:
            return False
        return last_source

    def last_issues(self, total):
        sents = list(self.e.d.lasts(self.dirs['sent']).keys())
        sents.reverse()
        if len(sents) < total:
            return sents
        return sents[0:total]
