from hatch.index.errors import ArtifactMetadataError

MULTIPLE_USE_METADATA_FIELDS = {
    "classifier",
    "dynamic",
    "license_file",
    "obsoletes_dist",
    "platform",
    "project_url",
    "provides_dist",
    "provides_extra",
    "requires_dist",
    "requires_external",
    "supported_platform",
}
RENAMED_METADATA_FIELDS = {"classifier": "classifiers", "project_url": "project_urls"}


def get_wheel_form_data(artifact):
    import zipfile

    from packaging.tags import parse_tag

    with zipfile.ZipFile(str(artifact), "r") as zip_archive:
        for path in zip_archive.namelist():
            root = path.split("/", 1)[0]
            if root.endswith(".dist-info"):
                dist_info_dir = root
                break
        else:  # no cov
            message = f"Could not find the `.dist-info` directory in wheel: {artifact}"
            raise ArtifactMetadataError(message)

        try:
            with zip_archive.open(f"{dist_info_dir}/METADATA") as zip_file:
                metadata_file_contents = zip_file.read().decode("utf-8")
        except KeyError:  # no cov
            message = f"Could not find a `METADATA` file in the `{dist_info_dir}` directory"
            raise ArtifactMetadataError(message) from None
        else:
            data = parse_headers(metadata_file_contents)

    data["filetype"] = "bdist_wheel"

    # Examples:
    # cryptography-3.4.7-pp37-pypy37_pp73-manylinux2014_x86_64.whl -> pp37
    # hatchling-1rc1-py2.py3-none-any.whl -> py2.py3
    tag_component = "-".join(artifact.stem.split("-")[-3:])
    data["pyversion"] = ".".join(sorted({tag.interpreter for tag in parse_tag(tag_component)}))

    return data


def get_sdist_form_data(artifact):
    import tarfile

    with tarfile.open(str(artifact), "r:gz") as tar_archive:
        pkg_info_dir_parts = []
        for tar_info in tar_archive:
            if tar_info.isfile():
                pkg_info_dir_parts.append(tar_info.name.split("/", 1)[0])
                break
        else:  # no cov
            message = f"Could not find any files in sdist: {artifact}"
            raise ArtifactMetadataError(message)

        pkg_info_dir_parts.append("PKG-INFO")
        pkg_info_path = "/".join(pkg_info_dir_parts)
        try:
            with tar_archive.extractfile(pkg_info_path) as tar_file:
                metadata_file_contents = tar_file.read().decode("utf-8")
        except KeyError:  # no cov
            message = f"Could not find file: {pkg_info_path}"
            raise ArtifactMetadataError(message) from None
        else:
            data = parse_headers(metadata_file_contents)

    data["filetype"] = "sdist"
    data["pyversion"] = "source"

    return data


def parse_headers(metadata_file_contents):
    import email

    message = email.message_from_string(metadata_file_contents)

    headers = {"description": message.get_payload()}

    for header, value in message.items():
        normalized_header = header.lower().replace("-", "_")
        header_name = RENAMED_METADATA_FIELDS.get(normalized_header, normalized_header)

        if normalized_header in MULTIPLE_USE_METADATA_FIELDS:
            if header_name in headers:
                headers[header_name].append(value)
            else:
                headers[header_name] = [value]
        else:
            headers[header_name] = value

    return headers
