from __future__ import annotations

import pytest

from osm_lead_source_service.errors import PbfDownloadRejected
from osm_lead_source_service.markets import get_english_market
from osm_lead_source_service.pbf.catalog import GeofabrikCatalog


def _feature(
    feature_id: str,
    *,
    parent: str | None,
    name: str,
    iso_alpha2: list[str] | None = None,
    iso_subdivision: list[str] | None = None,
) -> dict[str, object]:
    properties: dict[str, object] = {
        "id": feature_id,
        "name": name,
        "parent": parent,
        "urls": {"pbf": f"https://download.geofabrik.de/test/{feature_id}-latest.osm.pbf"},
    }
    if iso_alpha2 is not None:
        properties["iso3166-1:alpha2"] = iso_alpha2
    if iso_subdivision is not None:
        properties["iso3166-2"] = iso_subdivision
    return {"type": "Feature", "properties": properties}


def _catalog() -> GeofabrikCatalog:
    return GeofabrikCatalog.from_mapping(
        {
            "type": "FeatureCollection",
            "features": [
                _feature("us", parent="north-america", name="United States", iso_alpha2=["US"]),
                _feature("us-west", parent="us", name="US West"),
                _feature("us-east", parent="us", name="US East"),
                _feature(
                    "california",
                    parent="us-west",
                    name="California",
                    iso_subdivision=["US-CA"],
                ),
                _feature(
                    "nevada",
                    parent="us-west",
                    name="Nevada",
                    iso_subdivision=["US-NV"],
                ),
                _feature(
                    "bermuda",
                    parent="us",
                    name="Bermuda",
                    iso_alpha2=["BM"],
                ),
                _feature(
                    "malaysia-singapore-brunei",
                    parent="asia",
                    name="Malaysia, Singapore, and Brunei",
                    iso_alpha2=["MY"],
                ),
            ],
        }
    )


def test_direct_and_override_roots_resolve() -> None:
    catalog = _catalog()
    assert catalog.root_for_market(get_english_market("US")).feature_id == "us"
    assert (
        catalog.root_for_market(get_english_market("SG")).feature_id == "malaysia-singapore-brunei"
    )


def test_leaf_planner_uses_smallest_country_shards() -> None:
    shards = _catalog().plan_market_leaf_shards(get_english_market("US"))

    assert tuple(item.feature.feature_id for item in shards) == (
        "us-east",
        "california",
        "nevada",
    )


def test_large_market_is_split_recursively_and_foreign_children_are_excluded() -> None:
    catalog = _catalog()
    sizes = {
        "us": 5_000,
        "us-west": 2_000,
        "us-east": 400,
        "california": 300,
        "nevada": 200,
        "bermuda": 100,
    }
    shards = catalog.plan_market_shards(
        get_english_market("US"),
        max_shard_bytes=512 * 1024 * 1024,
        size_lookup=lambda feature: sizes[feature.feature_id] * 1024 * 1024,
    )
    assert tuple(item.feature.feature_id for item in shards) == (
        "us-east",
        "california",
        "nevada",
    )
    assert all(item.country_bounds is None for item in shards)


def test_shared_extract_market_keeps_country_bounds() -> None:
    catalog = _catalog()
    shards = catalog.plan_market_shards(
        get_english_market("SG"),
        max_shard_bytes=512 * 1024 * 1024,
        size_lookup=lambda feature: 99,
    )
    assert len(shards) == 1
    assert shards[0].feature.feature_id == "malaysia-singapore-brunei"
    assert shards[0].country_bounds is not None


def test_oversized_unsplittable_market_fails_closed() -> None:
    catalog = GeofabrikCatalog.from_mapping(
        {"features": [_feature("ireland", parent="europe", name="Ireland", iso_alpha2=["IE"])]}
    )
    with pytest.raises(PbfDownloadRejected, match="oversized unsplittable"):
        catalog.plan_market_shards(
            get_english_market("IE"),
            max_shard_bytes=32 * 1024 * 1024,
            size_lookup=lambda feature: 64 * 1024 * 1024,
        )


def test_unapproved_catalog_url_is_rejected() -> None:
    bad = _feature("us", parent="north-america", name="US", iso_alpha2=["US"])
    properties = bad["properties"]
    assert isinstance(properties, dict)
    urls = properties["urls"]
    assert isinstance(urls, dict)
    urls["pbf"] = "https://example.com/us-latest.osm.pbf"
    with pytest.raises(PbfDownloadRejected, match="unapproved PBF URL"):
        GeofabrikCatalog.from_mapping({"features": [bad]})


def test_path_feature_ids_and_hierarchical_parents_are_normalized() -> None:
    catalog = GeofabrikCatalog.from_mapping(
        {
            "features": [
                _feature("us", parent="north-america", name="United States", iso_alpha2=["US"]),
                _feature("us/california", parent="north-america", name="California"),
                _feature("norcal", parent="us/california", name="Northern California"),
                _feature("socal", parent="us/california", name="Southern California"),
            ]
        }
    )

    california = catalog.get("us--california")
    assert california.parent_id == "us"
    assert tuple(item.feature_id for item in catalog.children("us")) == ("us--california",)
    assert tuple(item.feature_id for item in catalog.children("us--california")) == (
        "norcal",
        "socal",
    )
    shards = catalog.plan_market_leaf_shards(get_english_market("US"))
    assert tuple(item.feature.feature_id for item in shards) == ("norcal", "socal")
    assert all("/" not in item.shard_id for item in shards)


def test_hierarchical_parent_without_downloadable_ancestor_fails_closed() -> None:
    with pytest.raises(PbfDownloadRejected, match="no downloadable ancestor"):
        GeofabrikCatalog.from_mapping(
            {"features": [_feature("norcal", parent="us/california", name="Northern California")]}
        )


def test_malformed_hierarchical_parent_is_rejected() -> None:
    with pytest.raises(PbfDownloadRejected, match="parent id has an invalid shape"):
        GeofabrikCatalog.from_mapping(
            {
                "features": [
                    _feature("us", parent="north-america", name="United States", iso_alpha2=["US"]),
                    _feature("norcal", parent="us//california", name="Northern California"),
                ]
            }
        )


def test_normalized_feature_id_collision_is_rejected() -> None:
    with pytest.raises(PbfDownloadRejected, match="normalized feature ids collide"):
        GeofabrikCatalog.from_mapping(
            {
                "features": [
                    _feature("us/california", parent="us", name="California path"),
                    _feature("us--california", parent="us", name="California slug"),
                ]
            }
        )
