#!/usr/bin/env python3

import argparse
import hashlib
import json
from pathlib import Path

import osmium


SOURCE_SHA256 = "0662b67825091986d45b8df070d1df43fa8048548e8defe680ec8df61fe1038c"
SOURCE_BYTE_SIZE = 298_424_907
EXTRACT_BOUNDS = (11.99, 52.18, 12.80, 52.79)
OUTPUT_FORMAT = "pbf,add_metadata=false,pbf_compression=zlib,pbf_compression_level=9"


def file_sha256(file_path: Path) -> str:
    digest = hashlib.sha256()
    with file_path.open("rb") as source:
        while chunk := source.read(1024 * 1024):
            digest.update(chunk)
    return digest.hexdigest()


def require_source(file_path: Path) -> None:
    if not file_path.is_file() or file_path.stat().st_size != SOURCE_BYTE_SIZE:
        raise RuntimeError("Havel PBF source byte size differs from the pinned Geofabrik snapshot")
    if file_sha256(file_path) != SOURCE_SHA256:
        raise RuntimeError("Havel PBF source SHA-256 differs from the pinned Geofabrik snapshot")


def main() -> None:
    parser = argparse.ArgumentParser(description="Build the fixed Havel qualification PBF without network access")
    parser.add_argument("source", type=Path)
    parser.add_argument("output", type=Path)
    arguments = parser.parse_args()
    source = arguments.source.resolve(strict=True)
    output = arguments.output.resolve(strict=False)
    if output.exists():
        raise RuntimeError("Havel PBF output already exists")
    if output.parent.resolve(strict=True) == source.parent:
        raise RuntimeError("Havel PBF output must be separated from the immutable upstream snapshot")
    require_source(source)
    minimum_longitude, minimum_latitude, maximum_longitude, maximum_latitude = EXTRACT_BOUNDS
    selected_node_count = 0
    thread_pool = osmium.io.ThreadPool(1, 1)
    output_file = osmium.io.File(output, OUTPUT_FORMAT)
    processor = osmium.FileProcessor(source, osmium.osm.NODE, thread_pool=thread_pool)
    with osmium.ForwardReferenceWriter(
        output_file,
        source,
        back_references=True,
        remove_tags=False,
        forward_relation_depth=0,
        backward_relation_depth=1,
        thread_pool=thread_pool,
    ) as writer:
        for node in processor:
            if (minimum_longitude <= node.location.lon <= maximum_longitude
                    and minimum_latitude <= node.location.lat <= maximum_latitude):
                writer.add_node(node)
                selected_node_count += 1
    require_source(source)
    if selected_node_count == 0 or not output.is_file() or output.stat().st_size == 0:
        raise RuntimeError("Havel PBF extraction produced no bounded data")
    print(json.dumps({
        "bounds": {
            "minimumLongitude": minimum_longitude,
            "minimumLatitude": minimum_latitude,
            "maximumLongitude": maximum_longitude,
            "maximumLatitude": maximum_latitude,
        },
        "extractor": "pyosmium/4.3.1-forward-reference-node-bbox/1",
        "outputByteSize": output.stat().st_size,
        "outputSha256": file_sha256(output),
        "selectedNodeCount": selected_node_count,
        "sourceByteSize": SOURCE_BYTE_SIZE,
        "sourceSha256": SOURCE_SHA256,
    }, ensure_ascii=True, separators=(",", ":"), sort_keys=True))


if __name__ == "__main__":
    main()
