from typing import Iterable
from typing import Dict
from typing import Sequence
import hashlib
import mmap


def verify_checksums(sources: Iterable[str], hashes: Dict[str, Sequence[str]]) -> bool:
    """Verify checksums for local files.

    Prints a message whenever there is a mismatch.

    Args:
        sources: An iterable of source strings.
        hashes: A dictionary of hash name -> hashes.
            For each entry, the list of hashes corresponds to `sources`
            in order but may be shorter.

    Returns:
        True if no checksums are mismatched, otherwise False.
    """
    valid = True
    hashlib_warnings = set()
    for i, source in enumerate(sources):
        try:
            source_file, _ = source.split("::")
        except ValueError:
            source_file = source

        for hashname, source_hashes in hashes.items():
            try:
                expected_digest = source_hashes[i]
            except IndexError:
                continue

            if expected_digest.lower() == "skip":
                continue

            try:
                h = hashlib.new(hashname)
            except ValueError:
                # Hopefully unlikely. As of the time of writing, all of HASHES are
                # in hashes.algorithms_guaranteed.
                if hashname not in hashlib_warnings:
                    print(
                        f"Warning: Your version of hashlib doesn't support {hashname}"
                    )
                    hashlib_warnings.add(hashname)
                continue

            try:
                with open(source_file, "rb") as f:
                    # Memory map in case the file is large
                    contents = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
                    h.update(contents)  # type: ignore
            except FileNotFoundError:
                break  # No point trying other hashes

            actual_digest = h.hexdigest()

            if expected_digest != actual_digest:
                print(source_file)
                print(f"\tExpected ({hashname}): {expected_digest}")
                print(f"\tActual   ({hashname}): {actual_digest}")
                valid = False
    return valid