"""
In-memory stand-in for the `pynetbox` package, used only by this test harness.

No real NetBox / network access exists this session (workstation, not the
jumphost) -- this fake implements just enough of pynetbox's surface (the
subset netbox/dc-dc-prefixes-import.py actually calls: .status(),
dcim.sites.get/create, ipam.roles.get, ipam.prefixes.get/create, and
Record.update()) to exercise the real script's logic end-to-end without a
live server. It is intentionally NOT a general pynetbox mock -- it models
only the calls this one script makes.
"""

from __future__ import annotations


class Record:
    _next_id = 1

    def __init__(self, **fields):
        self.id = Record._next_id
        Record._next_id += 1
        for k, v in fields.items():
            setattr(self, k, v)
        self._fields = dict(fields)

    def update(self, payload):
        for k, v in payload.items():
            setattr(self, k, v)
        self._fields.update(payload)
        return True


class Collection:
    def __init__(self):
        self._items = []

    def _matches(self, item, filters):
        for k, v in filters.items():
            if getattr(item, k, None) != v:
                return False
        return True

    def get(self, **filters):
        matches = [i for i in self._items if self._matches(i, filters)]
        if not matches:
            return None
        # REAL pynetbox RAISES on a multi-match. This fake used to return matches[0],
        # which meant the harness DIVERGED FROM REALITY AT EXACTLY THE FAILURE POINT:
        # NetBox permits duplicate prefixes (this importer's own docstring anticipates
        # them against vr0-dc0), and a global .get(prefix=...) would blow up mid-loop
        # in production while the tests sailed through. A fake that is kinder than the
        # real thing hides the bug it exists to catch.
        if len(matches) > 1:
            raise ValueError(
                f"get() returned more than one result ({len(matches)}). "
                f"Filters: {filters}. Use .filter() and scope the lookup.")
        return matches[0]

    def filter(self, **filters):
        return [i for i in self._items if self._matches(i, filters)]

    def create(self, **fields):
        rec = Record(**fields)
        self._items.append(rec)
        return rec

    def all(self):
        return list(self._items)


class _Dcim:
    def __init__(self):
        self.sites = Collection()


class _Ipam:
    def __init__(self):
        self.roles = Collection()
        self.prefixes = Collection()
        self.ip_ranges = Collection()
        self.vlans = Collection()
        self.vlan_groups = Collection()


class _HttpSession:
    """Stands in for pynetbox.api().http_session (a requests.Session)."""

    def __init__(self):
        self.headers = {}


class FakeApi:
    """Stands in for pynetbox.api(url, token=...)."""

    def __init__(self, url=None, token=None):
        self.url = url
        self.token = token
        self.dcim = _Dcim()
        self.ipam = _Ipam()
        # Mirrors pynetbox.api's real .http_session (a requests.Session): get_nb()
        # sets the WAF-safe User-Agent on it. A .headers dict is enough to record it.
        self.http_session = _HttpSession()

    def status(self):
        return {"netbox-version": "fake"}

    def preseed_roles(self, slugs):
        for slug in slugs:
            self.ipam.roles.create(slug=slug, name=slug)


def api(url=None, token=None):
    return FakeApi(url=url, token=token)
