from __future__ import annotations

import importlib
import sys
from types import ModuleType


def _reload_modules() -> None:
    for name in ["cadis", "cadis._api", "cadis._manager", "cadis._sdk"]:
        sys.modules.pop(name, None)


def test_lookup_does_not_auto_install_dataset() -> None:
    class _GlobalLookupImpl:
        @classmethod
        def from_defaults(cls):
            return cls()

        def lookup(self, lat: float, lon: float):
            return {
                "lookup_status": "ok",
                "world_context": {"country": {"iso2": "TW", "name": "Taiwan"}},
            }

    cadis_world = ModuleType("cadis.world")
    cadis_world.GlobalLookup = _GlobalLookupImpl
    sys.modules["cadis.world"] = cadis_world
    cadis_runtime = ModuleType("cadis.runtime")
    cadis_runtime.inspect_dataset = lambda dataset_dir: {"state": {"dataset": {"status": "missing"}}}
    sys.modules["cadis.runtime"] = cadis_runtime

    _reload_modules()
    cadis = importlib.import_module("cadis")

    from pathlib import Path
    import tempfile
    import os

    with tempfile.TemporaryDirectory() as td:
        cache_root = Path(td)
        os.environ["CADIS_CACHE_DIR"] = str(cache_root)
        try:
            payload = cadis.lookup(25.03, 121.56)
        finally:
            os.environ.pop("CADIS_CACHE_DIR", None)

    assert payload["execution"]["lookup_status"] == "failed"
    assert payload["execution"]["resolution_state"] == "remediable_capability_gap"
    assert payload["execution"]["capability_detail"] == "supported_dataset_missing"
    assert payload["state"]["dataset"]["status"] == "missing"
    assert "world_context" not in payload


def test_bootstrap_and_lookup_sdk_flow() -> None:
    class _GlobalLookupImpl:
        @classmethod
        def from_defaults(cls):
            return cls()

        def lookup(self, lat: float, lon: float):
            return {
                "lookup_status": "ok",
                "world_context": {"country": {"iso2": "TW", "name": "Taiwan"}},
            }

    class _Runtime:
        calls = 0

        def __init__(self, *, dataset_dir: str):
            self.dataset_dir = dataset_dir

        def lookup(self, lat: float, lon: float):
            _Runtime.calls += 1
            return {"lookup_status": "ok", "result": {"admin_hierarchy": []}}

    cadis_world = ModuleType("cadis.world")
    cadis_world.GlobalLookup = _GlobalLookupImpl
    sys.modules["cadis.world"] = cadis_world

    cadis_cdn = ModuleType("cadis.cdn")
    cadis_cdn.parse_version_for_sort = lambda raw: (1, 0, 0)
    cadis_cdn.install_dataset = lambda **kwargs: {
        "country_iso2": "TW",
        "dataset_id": "tw.admin",
        "dataset_version": "v1.0.0",
        "dataset_dir": "/tmp/cadis/TW/tw.admin/v1.0.0",
    }
    sys.modules["cadis.cdn"] = cadis_cdn

    cadis_runtime = ModuleType("cadis.runtime")
    cadis_runtime.inspect_dataset = lambda dataset_dir: {"state": {"dataset": {"status": "ready"}}}
    cadis_runtime.bootstrap_dataset = lambda dataset_dir: {"bootstrap_status": "ready"}
    cadis_runtime.CadisRuntime = _Runtime
    sys.modules["cadis.runtime"] = cadis_runtime

    _reload_modules()
    cadis = importlib.import_module("cadis")
    sdk = cadis.CadisSDK()

    bootstrap = sdk.bootstrap("TW")
    assert bootstrap["bootstrap_status"] == "ready"

    payload = sdk.lookup(25.03, 121.56)
    assert payload["execution"]["lookup_status"] == "ok"
    assert payload["execution"]["resolution_state"] == "resolved"
    assert "capability_detail" not in payload["execution"]
    assert _Runtime.calls == 1
    assert "world_context" not in payload


def test_lookup_reports_invalid_for_corrupted_local_dataset() -> None:
    class _GlobalLookupImpl:
        @classmethod
        def from_defaults(cls):
            return cls()

        def lookup(self, lat: float, lon: float):
            return {
                "lookup_status": "ok",
                "world_context": {"country": {"iso2": "TW", "name": "Taiwan"}},
            }

    cadis_world = ModuleType("cadis.world")
    cadis_world.GlobalLookup = _GlobalLookupImpl
    sys.modules["cadis.world"] = cadis_world

    cadis_runtime = ModuleType("cadis.runtime")
    cadis_runtime.inspect_dataset = lambda dataset_dir: {
        "state": {"dataset": {"status": "invalid"}}
    }
    sys.modules["cadis.runtime"] = cadis_runtime

    cadis_cdn = ModuleType("cadis.cdn")
    cadis_cdn.parse_version_for_sort = lambda raw: (1, 0, 0)
    sys.modules["cadis.cdn"] = cadis_cdn

    _reload_modules()
    cadis = importlib.import_module("cadis")

    from pathlib import Path
    import tempfile

    with tempfile.TemporaryDirectory() as td:
        cache_root = Path(td)
        version_dir = cache_root / "TW" / "tw.admin" / "v1.0.0"
        version_dir.mkdir(parents=True)
        # Ensure cadis uses this temp cache root.
        import os

        os.environ["CADIS_CACHE_DIR"] = str(cache_root)
        try:
            payload = cadis.lookup(25.03, 121.56)
        finally:
            os.environ.pop("CADIS_CACHE_DIR", None)

    assert payload["execution"]["lookup_status"] == "failed"
    assert payload["execution"]["resolution_state"] == "remediable_capability_gap"
    assert payload["execution"]["capability_detail"] == "dataset_invalid"
    assert payload["state"]["dataset"]["status"] == "invalid"
    assert "world_context" not in payload


def test_lookup_world_boundary_uses_state_world_only() -> None:
    class _GlobalLookupImpl:
        @classmethod
        def from_defaults(cls):
            return cls()

        def lookup(self, lat: float, lon: float):
            return {
                "lookup_status": "ok",
                "world_context": {
                    "world_result": {"type": "open_sea", "name": "Open Sea"},
                },
            }

    cadis_world = ModuleType("cadis.world")
    cadis_world.GlobalLookup = _GlobalLookupImpl
    sys.modules["cadis.world"] = cadis_world

    _reload_modules()
    cadis = importlib.import_module("cadis")
    payload = cadis.lookup(0.0, -160.0)

    assert payload["execution"]["lookup_status"] == "failed"
    assert payload["execution"]["resolution_state"] == "terminal_non_country"
    assert payload["execution"]["capability_detail"] == "non_country_world_classification"
    assert payload["state"]["world"]["classification"] == "open_sea"
    assert payload["state"]["world"]["name"] == "Open Sea"
    assert "label" not in payload["state"]["world"]
    assert "world_context" not in payload


def test_bootstrap_passes_pinned_dataset_version_to_install() -> None:
    class _Runtime:
        def __init__(self, *, dataset_dir: str):
            self.dataset_dir = dataset_dir

    install_calls: dict[str, object] = {}

    cadis_cdn = ModuleType("cadis.cdn")
    cadis_cdn.install_dataset = lambda **kwargs: install_calls.update(kwargs) or {
        "country_iso2": "TW",
        "dataset_id": "tw.admin",
        "dataset_version": "v1.0.3",
        "dataset_dir": "/tmp/cadis/TW/tw.admin/v1.0.3",
    }
    cadis_cdn.parse_version_for_sort = lambda raw: (1, 0, 3)
    sys.modules["cadis.cdn"] = cadis_cdn

    cadis_runtime = ModuleType("cadis.runtime")
    cadis_runtime.inspect_dataset = lambda dataset_dir: {"state": {"dataset": {"status": "ready"}}}
    cadis_runtime.bootstrap_dataset = lambda dataset_dir: {"bootstrap_status": "ready"}
    cadis_runtime.CadisRuntime = _Runtime
    sys.modules["cadis.runtime"] = cadis_runtime

    _reload_modules()
    cadis = importlib.import_module("cadis")

    out = cadis.bootstrap("TW", dataset_version="v1.0.3")

    assert out["bootstrap_status"] == "ready"
    assert install_calls["dataset_version"] == "v1.0.3"


def test_lookup_open_sea_retries_nearby_installed_runtime() -> None:
    class _GlobalLookupImpl:
        @classmethod
        def from_defaults(cls):
            return cls()

        def lookup(self, lat: float, lon: float):
            return {
                "lookup_status": "ok",
                "world_context": {
                    "world_result": {"type": "open_sea", "name": "Adriatic Sea"},
                },
            }

    class _GeometryIndex:
        def has_country_scope_geometry(self) -> bool:
            return True

        def distance_km_to_country_scope(self, pt) -> float:
            return 4.7

    class _Runtime:
        def __init__(self, *, dataset_dir: str):
            self.dataset_dir = dataset_dir
            self._pipeline = type(
                "_Pipeline",
                (),
                {
                    "geometry_index": _GeometryIndex(),
                    "policy": type("_Policy", (), {"offshore_max_distance_km": 20.0})(),
                },
            )()

        def lookup(self, lat: float, lon: float):
            return {
                "lookup_status": "ok",
                "result": {
                    "country": {"name": "Italy"},
                    "admin_hierarchy": [
                        {"name": "Veneto"},
                        {"name": "Citta Metropolitana di Venezia"},
                        {"name": "Venezia"},
                    ],
                },
            }

    cadis_world = ModuleType("cadis.world")
    cadis_world.GlobalLookup = _GlobalLookupImpl
    sys.modules["cadis.world"] = cadis_world

    cadis_runtime = ModuleType("cadis.runtime")
    cadis_runtime.inspect_dataset = lambda dataset_dir: {"state": {"dataset": {"status": "ready"}}}
    cadis_runtime.CadisRuntime = _Runtime
    sys.modules["cadis.runtime"] = cadis_runtime

    cadis_cdn = ModuleType("cadis.cdn")
    cadis_cdn.parse_version_for_sort = lambda raw: (1, 0, 1)
    sys.modules["cadis.cdn"] = cadis_cdn

    _reload_modules()
    cadis = importlib.import_module("cadis")

    from pathlib import Path
    import tempfile
    import os

    with tempfile.TemporaryDirectory() as td:
        cache_root = Path(td)
        (cache_root / "IT" / "it.admin" / "v1.0.1").mkdir(parents=True)
        os.environ["CADIS_CACHE_DIR"] = str(cache_root)
        try:
            payload = cadis.lookup(45.45796112723913, 12.35512655566448)
        finally:
            os.environ.pop("CADIS_CACHE_DIR", None)

    assert payload["execution"]["lookup_status"] == "ok"
    assert payload["execution"]["resolution_state"] == "resolved"
    assert payload["state"]["world"]["classification"] == "country"
    assert payload["state"]["world"]["iso2"] == "IT"
    assert payload["state"]["dataset"]["iso2"] == "IT"
    assert payload["result"]["admin_hierarchy"][-1]["name"] == "Venezia"
    assert "world_context" not in payload


def test_lookup_open_sea_stays_terminal_when_no_runtime_is_within_offshore_distance() -> None:
    class _GlobalLookupImpl:
        @classmethod
        def from_defaults(cls):
            return cls()

        def lookup(self, lat: float, lon: float):
            return {
                "lookup_status": "ok",
                "world_context": {
                    "world_result": {"type": "open_sea", "name": "Adriatic Sea"},
                },
            }

    class _GeometryIndex:
        def has_country_scope_geometry(self) -> bool:
            return True

        def distance_km_to_country_scope(self, pt) -> float:
            return 35.0

    class _Runtime:
        def __init__(self, *, dataset_dir: str):
            self._pipeline = type(
                "_Pipeline",
                (),
                {
                    "geometry_index": _GeometryIndex(),
                    "policy": type("_Policy", (), {"offshore_max_distance_km": 20.0})(),
                },
            )()

        def lookup(self, lat: float, lon: float):
            return {"lookup_status": "ok", "result": {"admin_hierarchy": []}}

    cadis_world = ModuleType("cadis.world")
    cadis_world.GlobalLookup = _GlobalLookupImpl
    sys.modules["cadis.world"] = cadis_world

    cadis_runtime = ModuleType("cadis.runtime")
    cadis_runtime.inspect_dataset = lambda dataset_dir: {"state": {"dataset": {"status": "ready"}}}
    cadis_runtime.CadisRuntime = _Runtime
    sys.modules["cadis.runtime"] = cadis_runtime

    cadis_cdn = ModuleType("cadis.cdn")
    cadis_cdn.parse_version_for_sort = lambda raw: (1, 0, 1)
    sys.modules["cadis.cdn"] = cadis_cdn

    _reload_modules()
    cadis = importlib.import_module("cadis")

    from pathlib import Path
    import tempfile
    import os

    with tempfile.TemporaryDirectory() as td:
        cache_root = Path(td)
        (cache_root / "IT" / "it.admin" / "v1.0.1").mkdir(parents=True)
        os.environ["CADIS_CACHE_DIR"] = str(cache_root)
        try:
            payload = cadis.lookup(45.45796112723913, 12.35512655566448)
        finally:
            os.environ.pop("CADIS_CACHE_DIR", None)

    assert payload["execution"]["lookup_status"] == "failed"
    assert payload["execution"]["resolution_state"] == "terminal_non_country"
    assert payload["execution"]["capability_detail"] == "non_country_world_classification"
    assert payload["state"]["world"]["classification"] == "open_sea"
    assert payload["state"]["world"]["name"] == "Adriatic Sea"
    assert "dataset" not in payload["state"]


def test_lookup_blocked_dataset_reports_blocked_by_policy() -> None:
    class _GlobalLookupImpl:
        @classmethod
        def from_defaults(cls):
            return cls()

        def lookup(self, lat: float, lon: float):
            return {
                "lookup_status": "ok",
                "world_context": {"country": {"iso2": "TW", "name": "Taiwan"}},
            }

    cadis_world = ModuleType("cadis.world")
    cadis_world.GlobalLookup = _GlobalLookupImpl
    sys.modules["cadis.world"] = cadis_world

    _reload_modules()
    cadis = importlib.import_module("cadis")

    payload = cadis.lookup(25.03, 121.56, allowed_iso2=["JP"])

    assert payload["execution"]["lookup_status"] == "failed"
    assert payload["execution"]["resolution_state"] == "blocked_by_policy"
    assert payload["execution"]["capability_detail"] == "dataset_blocked_by_policy"
    assert payload["state"]["dataset"]["status"] == "blocked"


def test_lookup_invalid_input_reports_invalid_resolution_state() -> None:
    _reload_modules()
    cadis = importlib.import_module("cadis")

    payload = cadis.lookup(999.0, 121.56)

    assert payload["execution"]["lookup_status"] == "failed"
    assert payload["execution"]["resolution_state"] == "invalid_input"
    assert payload["execution"]["capability_detail"] == "input_invalid"


def test_lookup_runtime_failed_with_ready_dataset_reports_unresolved_country() -> None:
    class _GlobalLookupImpl:
        @classmethod
        def from_defaults(cls):
            return cls()

        def lookup(self, lat: float, lon: float):
            return {
                "lookup_status": "ok",
                "world_context": {"country": {"iso2": "TW", "name": "Taiwan"}},
            }

    class _Runtime:
        def __init__(self, *, dataset_dir: str):
            self.dataset_dir = dataset_dir

        def lookup(self, lat: float, lon: float):
            return {"lookup_status": "failed", "result": None}

    cadis_world = ModuleType("cadis.world")
    cadis_world.GlobalLookup = _GlobalLookupImpl
    sys.modules["cadis.world"] = cadis_world

    cadis_runtime = ModuleType("cadis.runtime")
    cadis_runtime.inspect_dataset = lambda dataset_dir: {"state": {"dataset": {"status": "ready"}}}
    cadis_runtime.CadisRuntime = _Runtime
    sys.modules["cadis.runtime"] = cadis_runtime

    cadis_cdn = ModuleType("cadis.cdn")
    cadis_cdn.parse_version_for_sort = lambda raw: (1, 0, 0)
    sys.modules["cadis.cdn"] = cadis_cdn

    _reload_modules()
    cadis = importlib.import_module("cadis")

    from pathlib import Path
    import tempfile
    import os

    with tempfile.TemporaryDirectory() as td:
        cache_root = Path(td)
        version_dir = cache_root / "TW" / "tw.admin" / "v1.0.0"
        version_dir.mkdir(parents=True)
        os.environ["CADIS_CACHE_DIR"] = str(cache_root)
        try:
            payload = cadis.lookup(25.03, 121.56)
        finally:
            os.environ.pop("CADIS_CACHE_DIR", None)

    assert payload["execution"]["lookup_status"] == "failed"
    assert payload["execution"]["resolution_state"] == "unresolved_country"
    assert payload["execution"]["capability_detail"] == "dataset_ready_unresolved"


def test_lookup_missing_unsupported_country_reports_unsupported_country_detail() -> None:
    class _GlobalLookupImpl:
        @classmethod
        def from_defaults(cls):
            return cls()

        def lookup(self, lat: float, lon: float):
            return {
                "lookup_status": "ok",
                "world_context": {"country": {"iso2": "PH", "name": "Philippines"}},
            }

    cadis_world = ModuleType("cadis.world")
    cadis_world.GlobalLookup = _GlobalLookupImpl
    sys.modules["cadis.world"] = cadis_world

    cadis_runtime = ModuleType("cadis.runtime")
    cadis_runtime.inspect_dataset = lambda dataset_dir: {"state": {"dataset": {"status": "missing"}}}
    sys.modules["cadis.runtime"] = cadis_runtime

    _reload_modules()
    cadis = importlib.import_module("cadis")

    from pathlib import Path
    import tempfile
    import os

    with tempfile.TemporaryDirectory() as td:
        cache_root = Path(td)
        os.environ["CADIS_CACHE_DIR"] = str(cache_root)
        try:
            payload = cadis.lookup(16.85, 121.15)
        finally:
            os.environ.pop("CADIS_CACHE_DIR", None)

    assert payload["execution"]["lookup_status"] == "failed"
    assert payload["execution"]["resolution_state"] == "remediable_capability_gap"
    assert payload["execution"]["capability_detail"] == "unsupported_country"


def test_reinstall_uses_force_reinstall_path() -> None:
    class _GlobalLookupImpl:
        @classmethod
        def from_defaults(cls):
            return cls()

        def lookup(self, lat: float, lon: float):
            return {
                "lookup_status": "ok",
                "world_context": {"country": {"iso2": "TW", "name": "Taiwan"}},
            }

    cadis_world = ModuleType("cadis.world")
    cadis_world.GlobalLookup = _GlobalLookupImpl
    sys.modules["cadis.world"] = cadis_world

    install_args: dict[str, object] = {}
    cadis_cdn = ModuleType("cadis.cdn")
    cadis_cdn.parse_version_for_sort = lambda raw: (1, 0, 0)

    def _install_dataset(**kwargs):
        install_args.update(kwargs)
        return {
            "country_iso2": "TW",
            "dataset_id": "tw.admin",
            "dataset_version": "v1.0.0",
            "dataset_dir": "/tmp/cadis/TW/tw.admin/v1.0.0",
        }

    cadis_cdn.install_dataset = _install_dataset
    sys.modules["cadis.cdn"] = cadis_cdn

    cadis_runtime = ModuleType("cadis.runtime")
    cadis_runtime.inspect_dataset = lambda dataset_dir: {"state": {"dataset": {"status": "ready"}}}
    cadis_runtime.bootstrap_dataset = lambda dataset_dir: {"bootstrap_status": "ready"}
    cadis_runtime.CadisRuntime = lambda **kwargs: object()
    sys.modules["cadis.runtime"] = cadis_runtime

    _reload_modules()
    cadis = importlib.import_module("cadis")
    out = cadis.reinstall("TW")

    assert out["bootstrap_status"] == "ready"
    assert install_args.get("force_reinstall") is True
