from gitevo import GitEvo, ParsedCommit
from gitevo.utils import as_str


extension = '.py'

def metrics(evo: GitEvo):

    @evo.metric('Lines of code (LOC)', show_version_chart=False)
    def loc(commit: ParsedCommit):
        return commit.loc

    @evo.metric('Python files', show_version_chart=False)
    def files(commit: ParsedCommit):
        return len(commit.parsed_files)

    @evo.metric('LOC / Python files', show_version_chart=False)
    def loc_per_file(commit: ParsedCommit):
        parsed_files = len(commit.parsed_files)
        if parsed_files == 0: return 0
        return commit.loc / parsed_files
    
    @evo.metric('production file', show_version_chart=False, group='Production and test files')
    def production_files(commit: ParsedCommit):
        return len([file for file in commit.parsed_files if 'test' not in file.name.lower()])
    
    @evo.metric('test file', show_version_chart=False, group='Production and test files')
    def test_files(commit: ParsedCommit):
        return len([file for file in commit.parsed_files if 'test' in file.name.lower()])
    
    @evo.metric('Data structures', categorical=True)
    def data_structures(commit: ParsedCommit):
        return commit.find_node_types(['dictionary', 'list', 'set', 'tuple'])
    
    @evo.metric('Functions and classes', categorical=True)
    def definitions(commit: ParsedCommit):
        return commit.find_node_types(['class_definition', 'function_definition'])

    @evo.metric('class', group='LOC of functions and classes (mean)')
    def class_loc(commit: ParsedCommit):
        return commit.loc_by_type('class_definition', 'mean')

    @evo.metric('function', group='LOC of functions and classes (mean)')
    def function_loc(commit: ParsedCommit):
        return commit.loc_by_type('function_definition', 'mean')

    @evo.metric('Functions: def vs. async def', categorical=True)
    def sync_async(commit: ParsedCommit):
        function_definitions = commit.find_nodes_by_type(['function_definition'])
        return ['async def' if as_str(func.child(0).text) == 'async' else 'def' for func in function_definitions]

    @evo.metric('Function parameters', categorical=True, version_chart_type='hbar', top_n=5)
    def parameter_types(commit: ParsedCommit):
        function_definitions = commit.find_nodes_by_type(['function_definition'])
        func_def_parameters = [func.child_by_field_name('parameters') for func in function_definitions if func.child_by_field_name('parameters')]
        return [named_param.type for parameters in func_def_parameters for named_param in commit.named_children_for(parameters)]

    @evo.metric('Function return type', categorical=True)
    def return_types(commit: ParsedCommit):
        function_definitions = commit.find_nodes_by_type(['function_definition'])
        return ['yes' if func.child_by_field_name('return_type') else 'no' for func in function_definitions]

    @evo.metric('Functions: return vs. yield', categorical=True)
    def return_yield(commit: ParsedCommit):
        return commit.find_node_types(['return_statement', 'yield'])

    @evo.metric('@dataclass', show_version_chart=False)
    def definitions(commit: ParsedCommit):
        decorated_definitions = commit.find_nodes_by_type(['decorated_definition'])
        decorated_classes = [decorated_definition for decorated_definition in decorated_definitions if decorated_definition.child_by_field_name('definition').type == 'class_definition']
        dataclasses = [decorated_class for decorated_class in decorated_classes if as_str(decorated_class.child(0).text).startswith('@dataclass')]
        return len(dataclasses)

    @evo.metric('Control flows', categorical=True)
    def control_flow(commit: ParsedCommit):
        return commit.find_node_types(['for_statement', 'while_statement', 'if_statement', 'try_statement', 'match_statement', 'with_statement'])

    @evo.metric('Conditionals', categorical=True)
    def conditionals(commit: ParsedCommit):
        return commit.find_node_types(['if_statement', 'conditional_expression'])
    
    @evo.metric('Comprehensions', categorical=True)
    def comprehensions(commit: ParsedCommit):
        return commit.find_node_types(['dictionary_comprehension', 'list_comprehension', 'set_comprehension'])

    @evo.metric('Loops', categorical=True)
    def for_while(commit: ParsedCommit):
        return commit.find_node_types(['for_statement', 'while_statement', 'for_in_clause'])

    @evo.metric('Exception statements', categorical=True)
    def exceptions(commit: ParsedCommit):
        return commit.find_node_types(['try_statement', 'raise_statement'])

    @evo.metric('Import statements', categorical=True)
    def imports(commit: ParsedCommit):
        return commit.find_node_types(['import_statement', 'import_from_statement', 'future_import_statement'])