import sys
import types
import pathlib
import pytest

# Ensure the backend directory is on sys.path so we can import the geoip module directly
BACKEND_DIR = pathlib.Path(__file__).resolve().parents[1]  # backend/ folder
if str(BACKEND_DIR) not in sys.path:
    sys.path.insert(0, str(BACKEND_DIR))

import geoip as geoip


@pytest.fixture(autouse=True)
def reset_geoip_reader():
    # Ensure each test starts with a clean cache
    geoip._geoip_reader = None
    yield
    geoip._geoip_reader = None


def test_get_reader_import_error(monkeypatch):
    import builtins
    real_import = getattr(builtins, "__import__")

    def fake_import(name, globals=None, locals=None, fromlist=(), level=0):
        if name == "geoip2.database":
            raise ImportError("simulate missing geoip2")
        return real_import(name, globals, locals, fromlist, level)

    monkeypatch.setattr(builtins, "__import__", fake_import)

    geoip._geoip_reader = None
    assert geoip._get_reader() is None


def test_get_reader_db_missing(monkeypatch):
    # Provide a fake geoip2 module, but force the database file to be considered missing
    fake_db_module = types.ModuleType("geoip2.database")
    class FakeReader:
        def __init__(self, path):
            self.path = path
    fake_db_module.Reader = FakeReader

    fake_geoip2 = types.ModuleType("geoip2")
    fake_geoip2.database = fake_db_module

    sys.modules["geoip2"] = fake_geoip2
    sys.modules["geoip2.database"] = fake_db_module

    # Ensure path existence check returns False
    monkeypatch.setattr(geoip.os.path, "exists", lambda p: False)

    geoip._geoip_reader = None
    assert geoip._get_reader() is None

    # Clean up injected modules
    del sys.modules["geoip2"]
    del sys.modules["geoip2.database"]


def test_get_reader_loads_and_caches(monkeypatch):
    fake_db_module = types.ModuleType("geoip2.database")
    class FakeReader:
        def __init__(self, path):
            self.path = path
    fake_db_module.Reader = FakeReader

    fake_geoip2 = types.ModuleType("geoip2")
    fake_geoip2.database = fake_db_module

    sys.modules["geoip2"] = fake_geoip2
    sys.modules["geoip2.database"] = fake_db_module

    # Simulate that the database file exists
    monkeypatch.setattr(geoip.os.path, "exists", lambda p: True)

    geoip._geoip_reader = None
    r1 = geoip._get_reader()
    assert isinstance(r1, FakeReader)
    # Second call should return the same cached instance
    r2 = geoip._get_reader()
    assert r1 is r2
    # Clean up injected modules
    del sys.modules["geoip2"]
    del sys.modules["geoip2.database"]


@pytest.mark.parametrize("ip", [None, "", "127.0.0.1", "localhost", "::1"])
def test_get_ip_location_none_inputs(ip):
    assert geoip.get_ip_location(ip) is None


def test_get_ip_location_reader_none(monkeypatch):
    # When there is no reader (no database), return None
    monkeypatch.setattr(geoip, "_get_reader", lambda: None)
    assert geoip.get_ip_location("1.2.3.4") is None


def test_get_ip_location_successful_lookup(monkeypatch):
    from types import SimpleNamespace

    country = SimpleNamespace(name="United States")
    region = SimpleNamespace(name="California")
    resp = SimpleNamespace(
        country=country,
        subdivisions=SimpleNamespace(most_specific=region),
        city=SimpleNamespace(name="Mountain View"),
    )

    class FakeReader:
        def city(self, ip):
            return resp

    monkeypatch.setattr(geoip, "_get_reader", lambda: FakeReader())
    loc = geoip.get_ip_location("1.2.3.4")
    assert loc == {
        "country": "United States",
        "region": "California",
        "city": "Mountain View",
        "display": "United States California Mountain View",
    }


def test_get_ip_location_reader_exception(monkeypatch):
    class FakeReader:
        def city(self, ip):
            raise Exception("boom")

    monkeypatch.setattr(geoip, "_get_reader", lambda: FakeReader())
    assert geoip.get_ip_location("1.2.3.4") is None


def test_get_ip_location_no_location_parts(monkeypatch):
    from types import SimpleNamespace
    resp = SimpleNamespace(country=SimpleNamespace(name=None), subdivisions=None, city=None)

    class FakeReader:
        def city(self, ip):
            return resp

    monkeypatch.setattr(geoip, "_get_reader", lambda: FakeReader())
    assert geoip.get_ip_location("1.2.3.4") is None


def test_get_ip_location_text_valid(monkeypatch):
    from types import SimpleNamespace
    country = SimpleNamespace(name="United States")
    region = SimpleNamespace(name="California")
    resp = SimpleNamespace(
        country=country,
        subdivisions=SimpleNamespace(most_specific=region),
        city=SimpleNamespace(name="Mountain View"),
    )

    class FakeReader:
        def city(self, ip):
            return resp

    monkeypatch.setattr(geoip, "_get_reader", lambda: FakeReader())
    assert geoip.get_ip_location_text("1.2.3.4") == "United States California Mountain View"


def test_get_ip_location_text_none_when_no_location(monkeypatch):
    # Force get_ip_location to return None
    monkeypatch.setattr(geoip, "get_ip_location", lambda ip: None)
    assert geoip.get_ip_location_text("1.2.3.4") == ""
