"""Fail-closed network isolation for the entire test suite."""

from __future__ import annotations

import socket
from typing import Protocol, cast

import pytest

from osm_lead_source_service.sidecar.migration_config import load_test_migration_settings

_ORIGINAL_SOCKET = socket.socket
_ORIGINAL_CREATE_CONNECTION = socket.create_connection
_ORIGINAL_GETADDRINFO = socket.getaddrinfo
_ORIGINAL_GETHOSTBYNAME = socket.gethostbyname
_ORIGINAL_GETHOSTBYNAME_EX = socket.gethostbyname_ex
_ORIGINAL_GETNAMEINFO = socket.getnameinfo


class _PbfEndpoint(Protocol):
    host: str
    port: int


def _network_forbidden(*args: object, **kwargs: object) -> None:
    del args, kwargs
    raise AssertionError("network access is forbidden unless explicitly test-marked")


def _same_endpoint(address: object, host: str, port: int) -> bool:
    return (
        isinstance(address, tuple)
        and len(address) >= 2
        and address[0] == host
        and address[1] == port
    )


def _install_endpoint_guard(
    monkeypatch: pytest.MonkeyPatch,
    *,
    host: str,
    port: int,
    purpose: str,
) -> None:
    class GuardedSocket(_ORIGINAL_SOCKET):  # type: ignore[misc, valid-type]
        def __init__(
            self,
            family: int = socket.AF_INET,
            type: int = socket.SOCK_STREAM,
            proto: int = 0,
            fileno: int | None = None,
        ) -> None:
            if fileno is None:
                if family not in {socket.AF_INET, socket.AF_INET6}:
                    raise AssertionError(f"{purpose} tests may create only IP sockets")
                if int(type) & int(socket.SOCK_STREAM) != int(socket.SOCK_STREAM):
                    raise AssertionError(f"{purpose} tests may create only TCP sockets")
                super().__init__(family, type, proto)
            else:
                super().__init__(family, type, proto, fileno=fileno)

        def connect(self, address: object) -> object:
            if not _same_endpoint(address, host, port):
                raise AssertionError(f"{purpose} tests may connect only to the configured endpoint")
            return super().connect(address)  # type: ignore[arg-type]

        def connect_ex(self, address: object) -> int:
            if not _same_endpoint(address, host, port):
                raise AssertionError(f"{purpose} tests may connect only to the configured endpoint")
            return super().connect_ex(address)  # type: ignore[arg-type]

        def bind(self, address: object) -> None:
            del address
            raise AssertionError(f"{purpose} tests cannot create additional network listeners")

        def listen(self, backlog: int = 0) -> None:
            del backlog
            raise AssertionError(f"{purpose} tests cannot create additional network listeners")

        def sendto(self, data: object, address: object) -> int:
            del data, address
            raise AssertionError(f"{purpose} tests cannot use connectionless network sends")

    def guarded_create_connection(
        address: object,
        timeout: float | object = socket._GLOBAL_DEFAULT_TIMEOUT,
        source_address: tuple[str, int] | None = None,
    ) -> socket.socket:
        if not _same_endpoint(address, host, port):
            raise AssertionError(f"{purpose} tests may connect only to the configured endpoint")
        return _ORIGINAL_CREATE_CONNECTION(address, timeout, source_address)  # type: ignore[arg-type]

    def guarded_getaddrinfo(
        requested_host: object,
        requested_port: object,
        *args: object,
        **kwargs: object,
    ) -> list[tuple[object, ...]]:
        if requested_host != host or requested_port != port:
            raise AssertionError(f"{purpose} tests may resolve only the configured endpoint")
        return _ORIGINAL_GETADDRINFO(requested_host, requested_port, *args, **kwargs)  # type: ignore[arg-type]

    def guarded_gethostbyname(requested_host: str) -> str:
        if requested_host != host:
            raise AssertionError(f"{purpose} tests may resolve only the configured endpoint")
        return _ORIGINAL_GETHOSTBYNAME(requested_host)

    def guarded_gethostbyname_ex(requested_host: str) -> tuple[str, list[str], list[str]]:
        if requested_host != host:
            raise AssertionError(f"{purpose} tests may resolve only the configured endpoint")
        return _ORIGINAL_GETHOSTBYNAME_EX(requested_host)

    def guarded_getnameinfo(
        sockaddr: tuple[object, ...],
        flags: int,
    ) -> tuple[str, str]:
        if not _same_endpoint(sockaddr, host, port):
            raise AssertionError(f"{purpose} tests may resolve only the configured endpoint")
        return _ORIGINAL_GETNAMEINFO(sockaddr, flags)  # type: ignore[arg-type]

    monkeypatch.setattr(socket, "socket", GuardedSocket)
    monkeypatch.setattr(socket, "create_connection", guarded_create_connection)
    monkeypatch.setattr(socket, "getaddrinfo", guarded_getaddrinfo)
    monkeypatch.setattr(socket, "gethostbyname", guarded_gethostbyname)
    monkeypatch.setattr(socket, "gethostbyname_ex", guarded_gethostbyname_ex)
    monkeypatch.setattr(socket, "getnameinfo", guarded_getnameinfo)


@pytest.fixture(autouse=True)
def forbid_network(monkeypatch: pytest.MonkeyPatch, request: pytest.FixtureRequest) -> None:
    """Allow only exact disposable database or local PBF fixture endpoints."""

    markers = {mark.name for mark in request.node.iter_markers()}
    database_test = {"database", "integration"}.issubset(markers)
    pbf_network_test = {"pbf_network", "integration"}.issubset(markers)
    if database_test and pbf_network_test:
        raise AssertionError("a test cannot combine database and PBF network access")

    if database_test:
        settings = load_test_migration_settings()
        _install_endpoint_guard(
            monkeypatch,
            host=settings.host,
            port=settings.port,
            purpose="database",
        )
        return

    if pbf_network_test:
        endpoint = cast(_PbfEndpoint, request.getfixturevalue("pbf_test_endpoint"))
        _install_endpoint_guard(
            monkeypatch,
            host=endpoint.host,
            port=endpoint.port,
            purpose="PBF network",
        )
        return

    monkeypatch.setattr(socket, "socket", _network_forbidden)
    monkeypatch.setattr(socket, "create_connection", _network_forbidden)
    monkeypatch.setattr(socket, "getaddrinfo", _network_forbidden)
    monkeypatch.setattr(socket, "gethostbyname", _network_forbidden)
    monkeypatch.setattr(socket, "gethostbyname_ex", _network_forbidden)
    monkeypatch.setattr(socket, "getnameinfo", _network_forbidden)
