import collections
import json
import lua_to_json
import pylua
import re


__TEMPLATE = u'''\
    function string.starts(String,Start)
        return string.sub(String,1,string.len(Start))==Start
    end

    local function extract_backends()
        {data}
        local res = {{}};

        for key, value in pairs(_G) do
            if key and string.starts(key, 'backends_') then
                res[key] = key
            end;
        end;
        return res;
    end

    return require('json').encode(extract_backends())
'''


def get_shared(node, shared_nodes):
    if isinstance(node, dict):
        if 'shared' in node:
            shared_node = node['shared']
            if 'uuid' in shared_node and len(shared_node) > 1:
                uuid = shared_node.pop('uuid')
                node.pop('shared')
                assert uuid not in shared_nodes
                shared_nodes[uuid] = shared_node
                assert not node.viewkeys() & shared_node.viewkeys()
                node.update(shared_node)

        for v in node.itervalues():
            get_shared(v, shared_nodes)
    elif isinstance(node, list):
        for v in node:
            get_shared(v, shared_nodes)


def replace_shared(node, shared_nodes):
    if isinstance(node, dict):
        if 'shared' in node:
            empty_shared_node = node['shared']
            if empty_shared_node.keys() == ['uuid']:
                uuid = empty_shared_node['uuid']
                node.pop('shared')
                shared_node = shared_nodes[uuid]
                assert not node.viewkeys() & shared_node.viewkeys()
                node.update(shared_node)

        for v in node.itervalues():
            replace_shared(v, shared_nodes)
    elif isinstance(node, list):
        for v in node:
            replace_shared(v, shared_nodes)


def extract_shared(tree):
    shared_nodes = {}
    get_shared(tree, shared_nodes)
    replace_shared(tree, shared_nodes)


class ModulesBackendsGetter:
    def __init__(self, tree):
        """ Gets backends for services from json_val  """
        self.backends = collections.defaultdict(set)
        self.patterns = {}
        self.find_backends(tree)

        for name, backends in self.backends.iteritems():
            self.backends[name] = list(backends)

    def add_pattern(self, k, v):
        if isinstance(v, dict) and 'pattern' in v:
            self.patterns[k] = v['pattern']

    def find_backends(self, node):
        if isinstance(node, dict):
            for k, v in node.iteritems():
                self.add_pattern(k, v)
                if k == 'backends':
                    assert isinstance(v, list)
                    self.backends[k] |= set([v])
                else:
                    self.find_backends(v)

        elif isinstance(node, list):
            for v in node:
                self.find_backends(v)


def get_modules_backends(config, dump_tree=False, fill_backends=False):
    def repl(matchobj):
        match = matchobj.group(0)
        return match[len('genBalancer('):-2] + ';'

    all_backends_code = __TEMPLATE.format(data=config)
    all_backends = json.loads(pylua.eval_raw(all_backends_code.encode('utf-8')))
    for backend_name, _ in all_backends.iteritems():
        config = config.replace(backend_name + ';', '"' + backend_name + '";')
    config = re.sub(r'genBalancer\({.*?}\);', repl, config, flags=re.DOTALL)
    json_data = lua_to_json.loads(config.encode('utf-8'))
    extract_shared(json_data)
    result = {}
    if fill_backends:
        data = ModulesBackendsGetter(json_data)
        result['backends'] = data.backends
        result['patterns'] = data.patterns
    if dump_tree:
        result['tree'] = json_data
    return result
