#!/usr/bin/env python3
"""
Check internal links in md file in or under hugo directory.
Used by BornAgain CI to ensure that all internal links work in the online documentation.

Copyright Forschungszentrum Julich GmbH 2025.
License:  Public Domain
"""
import glob, os, re, sys


def print_broken_links(first_line_printed, broken_links_count, lin,
                       links_list, src_list):
    '''
    Increase the broken links counter by 1 and return it in the output.
    Print the broken links with the corresponding md files in relative form.
    Change first_line_printed in case it was False to indicate that the header
    is printed and return its value.
    '''
    if not first_line_printed:
        print("Broken link: ")
        first_line_printed = True
    broken_links_count += 1
    print('\n', lin)
    print("In: ")
    for i in range(len(links_list)):
        if links_list[i] == lin:
            print(src_list[i])
    return (first_line_printed, broken_links_count)


def make_path_absolute(current_path, relative_path):
    '''
    This is to transform relative path into an absolute one
    example: current_path is ./ref/sim/class, relative_path: ../../file
    as long as there is ../ in the relative path, it delete them, but
    at the same time it delete one level from the current path
    inside while, in the first iteration: relative path becomes ../file
    and current_path becomes ./ref/sim
    '''
    while relative_path[0:3] == '../':
        relative_path = relative_path[3:len(relative_path)]
        current_path = current_path[0:int(current_path.rfind('/'))]
    return f'{current_path}/{relative_path}'


def check_one_file(mdf):
    '''
    Read a file (mdf).
    Check the searched pattern in a file and send each to test.
    '''
    with open(mdf, 'r', encoding="utf8") as f:
        try:
            ftxt = f.read()
        except Exception as e:
            print('A problem appeard while trying to read the file: ' +
                  mdf)
            print(e)
            exit(1)
        for caught in re.findall(r'\[.*?\]\((.*?)\)', ftxt):
            check_one_link(mdf, caught)

def check_one_link(mdf, caught):
    '''
    Check a one detected pattern (caught) in a file (mdf)
    Checking the format of caught, if it is a string, if it contains special characters,..
    Transform conformal link to a unified form
    Append mdf and link to src_list and to Links_list.
    '''
    try:
        line = str(caught)
    except Exeption as e:
        print('The following error appeard when dealing with file: ')
        print(mdf)
        print('It happened when reading the link as a string')
        print(e)
        exit(1)
    if '.md' in line:
        dotmdlst.append(line)
    if line[0:1] == '/':
        tline = '.' + line
        src_list.append(mdf)
        links_list.append(tline)
    elif line[0:4] == 'http':
        pass  # don't check external links for the time being
    elif line[0] == '#':
        pass  # link target is inside present page - no need for checking
    elif line[0:2] == '{{':
        pass  # TODO, shortcodes to be treated later
    elif line[0:7] == 'mailto:':
        pass
    else:
        pp = mdf[0:int(mdf.rfind('/'))]
        tline = make_path_absolute(pp, line)
        src_list.append(mdf)
        links_list.append(tline)


def check_existence(lin, broken_links_count):
    '''
    Check if the link is broken, print related information if it is the case
    increase broken links count when neccessay
    '''
    first_line_printed = False
    if '#' in lin:
        lin = lin[0:int(lin.rfind('#'))]
    if '.md' in lin:
        dotmdlst.append(lin)
    if not os.path.exists(lin):
        '''
        os.path.exists() does not give a correct answer when checking for a file
        without its extension.
        example when checking ./ref/example where example is an md file, it returns False
        The goal of the following is first separate the path from the name of the file
        pthchk (path check) in the example is to check ./ref
        fdirchk is the name example.
        Then we have to list the name of what pthchk contains without extensions
        if fdirchk match one of them then the link is not broken
        '''
        pthchk = lin[0:int(lin.rfind('/'))]
        fdirchk = lin[int(lin.rfind('/')) + 1:len(lin)]
        direx = os.path.exists(pthchk)
        if not direx:
            (first_line_printed, broken_links_count) = print_broken_links(
                first_line_printed, broken_links_count, lin, links_list,
                src_list)
        elif direx:
            lsta = glob.glob(pthchk + '/*') # list pthchk directory items
            newl = [] # a list to append the same items but without extensions
            for elea in lsta:
                elea = elea[int(elea.rfind('/')) + 1:len(elea)] # here just to get the names without any path part
                if '.' in elea: # if it contains extension
                    elea = elea[0:int(elea.rfind('.'))]
                newl.append(elea.lower())
            if fdirchk not in newl:
                (first_line_printed,
                 broken_links_count) = print_broken_links(
                     first_line_printed, broken_links_count, lin,
                     links_list, src_list)
    return broken_links_count

src_list = []
links_list = []
dotmdlst = []

def check_hugo_links():
    '''
    The main function. it gather first all links in the absolute form,
    then it checks them
    '''
    mdlist = []    
    mdlist = glob.glob('./**/*.md', recursive=True)
    for ele in range(len(mdlist)):
        mdf = str(mdlist[ele])
        check_one_file(mdf)
    if links_list == []:
        print("Links list is empty")
        exit(1)
    disabs = list(set(links_list))
    total_links_count = len(disabs)
    broken_links_count = 0
    for cc in range(len(disabs)):
        link = disabs[cc]
        broken_links_count = check_existence(link, broken_links_count)
    print('--------------------------------------------------')
    print('links containing .md extension are: ', len(dotmdlst))
    print(
        f'{broken_links_count}/{total_links_count} links are broken in total!'
    )


if __name__ == '__main__':
    check_hugo_links()
