#!/usr/bin/env python3
import regex as re
import os
import sys

ROUTE_PATTERN = (
    r'@(?:[[:alpha:]])+\.route\(\"(?<url>[^"]+)"[^)]*\)\s*'
    r'(?:@[[:alpha:]_()., ]+\s*)*def\s+(?<name>[[:alpha:]][[:alnum:]_]*)'
    r'\((?<params>[[:alnum:], ]*)\):')
quote_group = "[\"']"
URL_FOR_PATTERN = (
    r'url_for\({quotes}(?<name>[[:alpha:]][[:alnum:]_]*)'
    '{quotes}'.format(quotes=quote_group))

ROOT_DIR = "."
ENDINGS = [".py", ".html", ".txt"]
MAX_DEPTH = 2


def list_dir(dir, level=0):
    if level >= MAX_DEPTH:
        return
    for file in os.listdir(dir):
        path = os.path.join(dir, file)
        if os.path.isfile(path):
            if file == sys.argv[0]:
                continue
            for ending in ENDINGS:
                if file.endswith(ending):
                    yield path
        elif os.path.isdir(path):
            yield from list_dir(path, level + 1)


class Route:
    def __init__(self, file, name, parameters):
        self.file = file
        self.name = name
        self.parameters = parameters

    def __repr__(self):
        return "Route({file}, {name}, {parameters})".format(
            file=self.file, name=self.name, parameters=self.parameters)

    def get_parameter_set(self):
        return {parameter.name for parameter in self.parameters}


class Parameter:
    def __init__(self, name, type=None):
        self.name = name
        self.type = type

    def __repr__(self):
        return "Parameter({name}, {type})".format(
            name=self.name, type=self.type)

    @staticmethod
    def from_string(text):
        if ":" in text:
            type, name = text.split(":", 1)
            return Parameter(name, type)
        return Parameter(text)


def split_url_parameters(url):
    params = []
    current_param = None
    for char in url:
        if current_param is None:
            if char == "<":
                current_param = ""
        else:
            if char == ">":
                params.append(Parameter.from_string(current_param))
                current_param = None
            else:
                current_param += char
    return params


def split_function_parameters(parameters):
    return list(map(str.strip, parameters.split(",")))


def read_url_for_parameters(content):
    params = []
    bracket_level = 1
    current_param = None
    for char in content:
        if bracket_level == 0:
            if current_param is not None:
                params.append(current_param.split("=")[0].strip())
            return params
        if char == ",":
            if current_param is not None:
                params.append(current_param.split("=")[0].strip())
            current_param = ""
        else:
            if current_param is not None:
                current_param += char
            if char == "(":
                bracket_level += 1
            elif char == ")":
                bracket_level -= 1


class UrlFor:
    def __init__(self, file, name, parameters):
        self.file = file
        self.name = name
        self.parameters = parameters

    def __repr__(self):
        return (
            "UrlFor(file={file}, name={name}, parameters={parameters})".format(
                file=self.file, name=self.name, parameters=self.parameters))


routes = {}
url_fors = []
for file in list_dir(ROOT_DIR):
    with open(file, "r") as infile:
        content = infile.read()
        for match in re.finditer(ROUTE_PATTERN, content):
            name = match.group("name")
            function_parameters = split_function_parameters(
                match.group("params"))
            url_parameters = split_url_parameters(match.group("url"))
            routes[name] = Route(file, name, url_parameters)
        for match in re.finditer(URL_FOR_PATTERN, content):
            name = match.group("name")
            begin, end = match.span()
            parameters = read_url_for_parameters(content[end:])
            url_fors.append(UrlFor(
                file=file, name=name, parameters=parameters))


for url_for in url_fors:
    if url_for.name not in routes:
        print("Missing route '{}' (for url_for in '{}')".format(
            url_for.name, url_for.file))
        continue
    route = routes[url_for.name]
    route_parameters = route.get_parameter_set()
    url_parameters = set(url_for.parameters)
    if len(route_parameters ^ url_parameters) > 0:
        print("Parameters not matching for '{}' in '{}:'".format(
            url_for.name, url_for.file))
        only_route = route_parameters - url_parameters
        only_url = url_parameters - route_parameters
        if len(only_route) > 0:
            print("Only in route: {}".format(only_route))
        if len(only_url) > 0:
            print("Only in url: {}".format(only_url))