# -*- coding: utf-8 -*-
import os
import re
import sys
import json
import yaml
import random
import logging
import requests
import itertools


SRE_OP = {
    re.sre_parse.LITERAL: lambda r, x: [str(i) + unicode(chr(x)) for i in r],
    re.sre_parse.SUBPATTERN: lambda r, x: [''.join(i) for i in itertools.product(r, sre_subpattern(x))],
    re.sre_parse.BRANCH: lambda r, x: [''.join(i) for i in itertools.product(r, sre_branch(x))],
    re.sre_parse.MAX_REPEAT: lambda r, x: [''.join(i) for i in itertools.product(r, sre_repeate(x))],
    re.sre_parse.MIN_REPEAT: lambda r, x: [''.join(i) for i in itertools.product(r, sre_repeate(x))],
    re.sre_parse.ANY: lambda r, x: [str(i) + '!STOP!' for i in r],
    re.sre_parse.IN: lambda r, x: [''.join(i) for i in itertools.product(r, sre_in(x))],
    re.sre_parse.RANGE: lambda r, x: [str(i) + '!STOP!' for i in r],
    re.sre_parse.CATEGORY: lambda r, x: [''.join(i) for i in itertools.product(r, sre_category(x))],
}


def sre_gen_category(x):
    if x == re.sre_parse.CATEGORY_SPACE:
        return re.sre_parse.parse("[ \t\n\r\f\v]")
    elif x == re.sre_parse.CATEGORY_DIGIT:
        return re.sre_parse.parse("[0-9]")
    elif x == re.sre_parse.CATEGORY_WORD:
        return re.sre_parse.parse("[a-zA-Z0-9_]")
    elif x == re.sre_parse.CATEGORY_NOT_SPACE:
        return re.sre_parse.parse("[^ \t\n\r\f\v]")
    elif x == re.sre_parse.CATEGORY_NOT_DIGIT:
        return re.sre_parse.parse("[^0-9]")
    elif x == re.sre_parse.CATEGORY_NOT_WORD:
        return re.sre_parse.parse("[^a-zA-Z0-9_]")
    raise Exception("Unknown category " + str(x))


def sre_category(x):
    return ''.join(sre_recur(sre_gen_category(x)))


def sre_in(x):
    if x[0][0] != re.sre_parse.NEGATE:
        return sre_recur([random.choice(x), ])
    else:
        # NOT(^) is not implemented because of lack
        # of regexps of this type in prod config
        raise Exception("Not Implemeted")
    return ['']


def sre_branch(x):
    data = []
    for branch in x[1]:
        data.extend(sre_recur(branch))
    return data


def sre_subpattern(x):
    return sre_recur(x[-1])


def sre_repeate(x):
    return sre_recur(x[-1])


def sre_recur(pattern):
    results = ['']
    for op, data in pattern:
        if op in SRE_OP:
            results = SRE_OP[op](results, data)
        else:
            raise Exception(
                "Invalid operation: {0} with data {1}".format(str(op), str(data)))
    return results


def parse_regexp(reg):
    data = []
    for x in sre_recur(re.sre_parse.parse(reg.decode('utf-8'), flags=re.U)):
        data.append(''.join(x))
    return data


def find_first_report(conf):
    for module in conf:
        if "report" in module:
            uuid = module["report"].get("uuid", None)

            if uuid is None or "matcher_map" not in module["report"]:
                return uuid

            matcher = {}
            for pattern in module["report"]["matcher_map"]:
                name = "{0}_{1}".format(uuid, pattern)

                matcher[name] = module["report"]["matcher_map"][pattern]["match_fsm"]["path"]
            return matcher
    return None


def process_config(conf):
    if "regexp_path_section" not in conf:
        logging.error("    No root regexp_path_section section")
        return {}

    if "pattern" not in conf["regexp_path_section"]:
        logging.error("    No pattern param in regexp_path_section section")
        return {}

    pattern = conf["regexp_path_section"]["pattern"]
    report = find_first_report(conf["regexp_path_section"].get("modules", []))

    if report is None:
        logging.error("    No report module in upper tree")
        return {}

    mapper = {}

    if isinstance(report, dict):
        for i in report:
            path = parse_regexp(report[i])[0]
            stop_pos = path.find("!STOP!")
            if stop_pos == -1:
                stop_pos = len(path)
            k = path[0:stop_pos]
            if k in mapper:
                raise Exception("{0} already in map".format(k))
            mapper[k] = i
    elif isinstance(report, str):
        path = parse_regexp(pattern)[0]
        stop_pos = path.find("!STOP!")
        if stop_pos == -1:
            stop_pos = len(path)
        k = path[0:stop_pos]
        if k in mapper:
            raise Exception("{0} already in map".format(k))
        mapper[k] = report

    return mapper


def process_file(fn):
    with open(fn) as fd:
        f = fd.readlines()

        breaker = 0
        for i in xrange(0, len(f)):
            if "---" in f[i]:
                breaker = i + 1
                break

        conf = yaml.safe_load("\n".join(f[breaker:]))
        return process_config(conf)
    return {}


def post_wiki(mapping, token):
    payload = "#|"
    for k in sorted(mapping):
        payload += "|| {0} | ((https://yasm.yandex-team.ru/template/panel/yastatic_pannel/uuid={1} yasm)) ||".format(
            k, mapping[k])
    payload += "|#"

    r = requests.post(
        "https://wiki-api.yandex-team.ru/_api/frontend/L7/yastatic/panel/",
        data=json.dumps(
            {"title": "Панель графиков yastatic по проектам", "body": payload}),
        headers={"Authorization": "OAuth {0}".format(token), "Content-Type": "application/json"})

    if r.status_code != 200:
        logging.error("Error: {0}".format(r.text))


def process(fn, token):
    yaml.SafeLoader.add_constructor(
        u'!f', lambda l, n: str(l.construct_scalar(n)))
    mapper = {}
    for path, _, files in os.walk(fn):
        logging.info("Scanning {0}".format(path))

        for f in files:
            if f.startswith("yastatic_net") and (f.endswith(".yaml") or f.endswith(".yml")):
                logging.info("  Processing {0}".format(f))
                mapper.update(process_file(os.path.join(path, f)))

    post_wiki(mapper, token)


if __name__ == "__main__":
    if len(sys.argv) == 1 or len(sys.argv) > 2:
        logging.error(
            "Usage:\n\tpython {0} <path to yastatic upstreams dir>".format(sys.argv[0]))
    else:
        if not os.path.exists(sys.argv[1]):
            raise Exception("Could not find directory with upstreams")
        process(sys.argv[1], os.environ['WIKI'])
