#!/usr/bin/python3.6
"""
Query bugzilla for component bugs and search through comments for trigger words. Output results
to a log and for tasks found in the comments set bugzillano field with bugzilla ids.
"""

import sys
import re
import time

from typing import Dict, List, Set
from pathlib import Path

import bugzilla

from retrace.retrace import BUGZILLA_STATUS, RetraceTask
from retrace.config import Config

CONFIG = Config()

if __name__ == "__main__":

    logfile = Path(CONFIG["LogDir"], "bugzilla-query.log")

    with logfile.open("a") as log:
        log.write(time.strftime("[%Y-%m-%d %H:%M:%S] Running bugzilla query\n"))

        # connect to bugzilla
        bz_url = CONFIG["BugzillaURL"]
        bz_creds = CONFIG["BugzillaCredentials"]

        try:
            bzapi = bugzilla.Bugzilla(url=bz_url, cookiefile=None, tokenfile=None)
        except (bugzilla.BugzillaError, ValueError) as e:
            log.write("Exception: {0}".format(e))
            sys.exit(1)

        if not bzapi.logged_in and bz_creds:
            bzapi.readconfig(bz_creds)
            try:
                bzapi.connect()
            except (bugzilla.BugzillaError, ValueError) as e:
                log.write("Exception: {0}".format(e))

        if bzapi.logged_in:
            log.write("Successfuly logged in as {0}.\n".format(bzapi.user))
        else:
            log.write("Not logged in. Continue as anonymous user.\n")

        product_list = CONFIG.get_list("BugzillaProduct")
        component_list = CONFIG.get_list("BugzillaComponent")

        query = bzapi.build_query(
            product=product_list,
            component=component_list,
            include_fields=["id"])

        # I don't want to run regex on each and every comment, at first try fast check
        # if trigger word is in comment and if is, then use slower regex
        trigger_words = CONFIG.get_list("BugzillaTriggerWords")
        regexes = CONFIG.get_list("BugzillaRegExes")

        config_status = CONFIG.get_list("BugzillaStatus")
        query["status"] = list(set(BUGZILLA_STATUS).difference(config_status))

        found_tasks: Dict[str, List[str]] = {}

        bugs = [x.bug_id for x in bzapi.query(query)]

        while bugs:
            query = bzapi.build_query(
                bug_id=bugs[:250],
                include_fields=["id", "comments"])

            cur_bugs = bzapi.query(query)
            bugs = bugs[250:]
            for bug in cur_bugs:
                found_ids: Set[str] = set()
                for comment in bug.comments:
                    for i, trigger_word in enumerate(trigger_words):
                        if trigger_word in comment["text"]:
                            m = re.findall(regexes[i], comment["text"])
                            found_ids.update(m)
                for f in found_ids:
                    if f in found_tasks:
                        found_tasks[f].append(str(bug.bug_id))
                    else:
                        found_tasks[f] = [str(bug.bug_id)]

        try:
            files = sorted(Path(CONFIG["SaveDir"]).iterdir())
        except OSError as ex:
            files = []
            log.write("Error listing task directory: %s\n" % ex)

        # Find all tasks
        existing_tasks = []
        for taskdir in files:
            if not taskdir.is_dir():
                continue

            existing_tasks.append(taskdir.name)

        log.write("------------Existing tasks with found bugzillas-----------\n")
        for taskid in found_tasks:
            if taskid in existing_tasks:
                log.write("Task {0} has following bugzilla(s): {1}.\n".format(taskid, ", ".join(found_tasks[taskid])))
                try:
                    task = RetraceTask(taskid)
                except Exception:
                    continue

                if task.has_bugzillano():
                    current = task.get_bugzillano()
                    assert isinstance(current, list)
                    found = found_tasks[taskid]
                    # only set bugzillano if some found bugzillas aren't in current bugzillano list
                    if not set(found).issubset(current):
                        task.set_bugzillano(current + found)
                else:
                    task.set_bugzillano(found_tasks[taskid])

        log.write("\n\n------------Found tasks in bugzillas that do not exist on local system-----------\n")
        for taskid in found_tasks:
            if taskid not in existing_tasks:
                log.write("Unknown task {0} has following bugzilla(s): {1}.\n"
                          .format(taskid, ", ".join(found_tasks[taskid])))

        log.write("\n\n------------Existing tasks that no bugzilla was found for-----------\n")
        no_bz_tasks = [taskid for taskid in existing_tasks if taskid not in found_tasks.keys()]
        log.write("{0}\n\n".format(", ".join(no_bz_tasks)))
