"""
Check the requirements.
    TODO list:
        remove plus suffixes in get_installed
"""

import re, subprocess, os
try:
    from pip_dater.util import runtime_output
    from pip_dater.database import DB
except:
    from util import runtime_output
    from database import DB


def change_version(req, name, ver_new):
    # 'flags=re.MULTILINE' is required for ^ to match the begin of line
    return re.sub(f"(?m)^{name}==.+", f"{name}=={ver_new}", req)

def get_installed(cleanup=True, remove_plus=False):
    """Get the list of requirements.
    Optionally removed references to packages installed locally from wheels."""
    req = subprocess.run(['pip', 'freeze'], capture_output=True, text=True).stdout
    if cleanup:
        # local files are marked with '@': fix them
        req = re.sub(r'(.+) @ .+-([0-9.]+)[-\.].*', r'\1==\2', req)
    if remove_plus:
        pass  # TODO: remove plus suffixes
    return req

def check(req, verbose=False, filter_errors=False):
    """Verify a list of requirements."""
    temp = '_check_requirements.txt'
    with open(temp, 'w') as fw:
        fw.write(req)
    err, out = runtime_output(f"pip install --dry-run --ignore-installed -r {temp}",
                              verbose=verbose)
    os.remove(temp)
    if err and filter_errors:
        # keep only the interesting lines of the log
        # bad_lines = re.findall(r'(?ms)^ERROR: (?!ResolutionImpossible).+?\n|'
        #                        r'(?<=The conflict is caused by:\n).+?(?=\n\n)',
        #                        out)
        bad_lines = re.findall(r'(?ms)(?<=The conflict is caused by:\n).+?(?=\n\n)',
                               out)
        out = '\n'.join(bad_lines)
    return err, out

def check_outdated(req_installed, candidate, probably_good=[], verbose=False):
    """Verify a list of requirements against proposed upgrades."""
    db = DB()
    good_candidate = []
    if probably_good:
        ottimismo = [x for x in candidate if x[0] in probably_good]
        req = req_installed
        for name, _, ver_new in ottimismo:
            req = change_version(req, name, ver_new)
        print(f"  Group testing: {probably_good}")
        err, out = check(req, verbose=verbose)
        if err:
            print('    check FAILED.')
        else:
            print('    check passed.')
            good_candidate = probably_good
            candidate = [x for x in candidate if x[0] not in probably_good]
    for name, _, ver_new in candidate:
        print(f"  Testing: {name} {ver_new}")
        fullname = f"{name}=={ver_new}"
        if fullname in db:
            blockers = db[fullname]
            blockers_found = [b for b in blockers if b in req_installed]
            found_in_db = len(blockers_found) > 0
        else:
            found_in_db = False
        if found_in_db:
            err = True
            out = f"    Blockers found in DB: {','.join(blockers_found)}"
        else:
            req = change_version(req_installed, name, ver_new)
            err, out = check(req, verbose=verbose, filter_errors=True)
        if err:
            db_str = ' (blockers found in DB)' if found_in_db else ''
            print(f'    check FAILED{db_str}.')
        else:
            print('    check passed.')
            good_candidate.append(name)
        # update db in case of fail
        if err and fullname not in db:
            blockers = re.findall(r'(\w.+?)(\[.+\])? (.+) depends on ', out)
            bb = [f"{n}=={v}" for n, _, v in blockers]
            db[fullname] = bb
    return good_candidate

def probably_good_db(outdated, req_installed, verbose=False):
    """Verify if the proposed are compatible with the installed using the DB.
    Return a list of candidates."""
    db = DB()
    candidate = set(name for name, _, _ in outdated)
    for name, ver_old, ver_new in outdated:
        print(f"  Testing: {name} {ver_new}")
        fullname = f"{name}=={ver_new}"
        if fullname in db:
            # block the package if at least a blocker is installed
            blockers = db[fullname]
            blockers_found = [b for b in blockers if b in req_installed]
            if blockers_found:
                if verbose:
                    print(f"    Blockers found: {','.join(blockers_found)}")
                candidate.discard(name)
    return sorted(list(candidate))

##########################
if __name__ == "__main__":
    # # test output filter
    # out = """    python-lsp-server[all] 1.14.0 depends on autopep8<2.1.0 and >=2.0.4; extra == "all"'"""
    # blockers = re.findall(r'(\w.+?)(\[.+\])? (.+) depends on ', out)
    # print(blockers)

    # test check_outdated
    print("Testing upgradable packages")
    req_installed = get_installed()
    outdated = [
                # ['scipy', '1.16.3', '1.17.0'],
                # ['black', '25.12.0', '26.1.0'],
                # ['async-lru', '2.0.5', '2.1.0'],
                # ['ruff', '0.14.10', '0.14.13'],
                ['autopep8', '2.0.4', '2.3.2'],
                ['pyflakes', '3.2.0', '3.4.0'],
                ['tinycss2', '1.4.0', '1.5.1']
                ]
    print("Finding 'probably good' upgrades.")
    probably_good = probably_good_db(outdated, req_installed, verbose=True)
    # probably_good = []
    print(probably_good)
    # good_candidate = check_outdated(req_installed, outdated,
    #                                 probably_good=probably_good, verbose=False)
    # print('Good:', good_candidate)

    # # test check
    # print('Checking current installation')
    # req = get_installed()
    # # req = req.replace('autopep8==2.0.4', 'autopep8==2.3.2')
    # # req = req.replace('tinycss2==1.4.0', 'tinycss2==1.5.1')
    # err, out = check(req)
    # if err: print('   ', out)
