#!/usr/bin/env python3

import argparse
import pathlib
import sys
import jinja2
import warnings
import toml
import re
from packaging.tags import sys_tags

# TODO: remove and see if make bottle fails
PREFER_WHL = ['pan-os-python']
CUSTOM_WHL_TAGS = ['py2.py3-none-any']

tags = sorted(set([str(tag) for tag in sys_tags()] + CUSTOM_WHL_TAGS))
tag_exps = [re.compile(r"-{}.whl$".format(tag), re.I) for tag in tags]


class Resource:
    def __init__(self, name, version, url, sha256):
        self.name = name
        self.version = version
        self.url = url
        self.sha256 = sha256

    def __str__(self):
        return (
            f'resource "{self.name}" do\n'
            f'  # requirement specifier: {self.version}\n'
            f'  url "{self.url}"\n'
            f'  sha256 "{self.sha256}"\n'
            'end'
        )


class WhlResource:
    def __init__(self, url):
        # self.specifier = specifier
        # self.name = re.split('[=<>!~]', self.specifier)[0]
        self.url = url.strip()
        # self.sha256 = sha256

    def __str__(self):
        return (
            f'system libexec/"bin/pip", "install", "-v", "--no-deps", "--ignore-installed", "{self.url}"'
        )


# def parse_resources(filename):
#     with open(filename) as f:
#         data = [i.split() for i in [line.strip() for line in f] if i]
#     resources = [Resource(*i) for i in data if i[1].endswith('.tar.gz') and not i[0].startswith("setuptools")]
#     whl_resources = [WhlResource(*i) for i in data if i[1].endswith('.whl') and not i[0].startswith("setuptools")]
#     return resources, whl_resources

def parse_resources(pdm_list):
    with open('pdm.lock') as f:
        td = toml.load(f)
        lock_packages = td.get('metadata', {}).get('files', {})
        # PDM lock file format has changed since above line was originally working.
        if not lock_packages:
            # This below code maps the python data in similar manner (as above)
            # so rest of the code further down should still work.
            package_data = td.get('package', [])
            for entry in package_data:
                package, version = entry.get('name'), entry.get('version')
                lock_packages[f"{package} {version}"] = entry.get('files', [])

    # cache_dirs = [x for x in  if x.is_dir()]
    # cached_packages = [x.name.split('-')[0] for x in cache_path.iterdir() if x.is_dir()]
    # print(cached_packages)

    with open(pdm_list) as f:
        pdm_packages = [x.lower().replace('_', '-').replace('.', '-') for x in f.read().splitlines() if x.strip()]

    resources = []
    whl_resources = []
    for key, hashes in lock_packages.items():
        name = key.split(' ')[0].lower()
        stripped_name = name.replace('_', '-').replace('.', '-')
        if stripped_name not in pdm_packages:
            continue
        if stripped_name.startswith("setuptools"):
            pdm_packages.remove(stripped_name)
            continue
        version = key.split(' ')[1]

        # Grab whls, if they are found first.
        skip_whl_found = False
        for pair in hashes:
            url = pair.get('url', '').strip()
            matching_tags = [x.search(url) for x in tag_exps]
            if any(matching_tags):
                whl_resources.append(WhlResource(url))
                pdm_packages.remove(stripped_name)
                skip_whl_found = True
                break

        if skip_whl_found:
            continue

        # Else grab .tar.gz.
        for pair in hashes:
            url = pair.get('url', '').strip()
            sha256 = pair['hash'][7:]
            if '.tar.gz' in url and name not in PREFER_WHL:
                resources.append(Resource(name, version, url, sha256))
                pdm_packages.remove(stripped_name)
                break
        else:
            if len(hashes) == 1:
                pair = hashes[0]
                url = pair['url']
                sha256 = pair['hash'][7:]

                matching_tags = [x.search(url) for x in tag_exps]
                # if "none-any" not in url or ".whl" not in url:
                if not any(matching_tags):
                    raise Exception('No compatible .whl or .tar.gz found')
            else:
                # May be below section is redundant now with prefer "whl" logic added (v25.2.22)
                for num, pair in enumerate(hashes):
                    if "macosx" in pair['url'] or "none-any" in pair['url']:
                        print(f"{num}. {pair['url'].split('/')[-1]}")
                selection = input("Select whl number to install: ")
                url = hashes[int(selection)]['url']
                sha256 = hashes[int(selection)]['hash'][7:]

            if url and sha256:
                whl_resources.append(WhlResource(url))
            else:
                raise Exception(f'not url and sha256 for {name}')
            pdm_packages.remove(stripped_name)

    if pdm_packages:
        raise Exception(f"Left over packages!\n{pdm_packages}")

    return resources, whl_resources


def update_formula(template, resources, whl_resources, formula, version, sha256):
    env = jinja2.Environment(loader=jinja2.FileSystemLoader(str(template.parent)))
    template = env.get_template(str(template.name))
    with open(formula, "w") as f:
        print(
            template.render(
                version=version, sha256=sha256, resources=(
                    str(i) for i in resources
                ),
                whl_resources=(
                    str(i) for i in reversed(whl_resources)
                ),
            ),
            file=f
        )


def build_cli_parser():
    parser = argparse.ArgumentParser(description="Update dependencies in nscli's Homebrew Formula")
    parser.add_argument("template", help="formula to read", type=pathlib.Path)
    parser.add_argument("pdm_list", help="pdm list", type=pathlib.Path)
    parser.add_argument("formula", help="formula to write")
    parser.add_argument("version", help="nscli's version")
    parser.add_argument("sha256", help="the sha256 sum of the new tarball")
    return parser


def main(args=sys.argv):
    parser = build_cli_parser()
    args = parser.parse_args(args[1:])
    template, pdm_list, formula, version, sha256 = args.template, args.pdm_list, args.formula, args.version, args.sha256
    resources, whl_resources = parse_resources(pdm_list)
    for i in whl_resources:
        if i.url.endswith(".whl") and not i.url.endswith("none-any.whl"):
            warnings.warn(
                "Warning: platform-dependent wheel found for {}."
                "".format(i.url.split('/')[-1])
            )
    return update_formula(template, resources, whl_resources, formula, version, sha256)


if __name__ == '__main__':
    sys.exit(main(sys.argv))
