#!/usr/bin/env python3
"""
User-space allowlist TCP proxy for the 111 Ollama fallback.

Purpose:
  - Keep the real Ollama server bound to 127.0.0.1:11434.
  - Expose 192.168.0.111:11434 only to approved momo-pro clients.
  - Reject noisy LAN / VM clients without requiring sudo/pfctl.
"""

from __future__ import annotations

import asyncio
import ipaddress
import logging
import os
import signal
import sys
import time


LISTEN_HOST = os.getenv("OLLAMA111_PROXY_LISTEN_HOST", "192.168.0.111")
LISTEN_PORT = int(os.getenv("OLLAMA111_PROXY_LISTEN_PORT", "11434"))
TARGET_HOST = os.getenv("OLLAMA111_PROXY_TARGET_HOST", "127.0.0.1")
TARGET_PORT = int(os.getenv("OLLAMA111_PROXY_TARGET_PORT", "11434"))
ALLOWED_CIDRS = tuple(
    item.strip()
    for item in os.getenv(
        "OLLAMA111_PROXY_ALLOWED_CIDRS",
        "127.0.0.1/32,192.168.0.111/32,192.168.0.188/32",
    ).split(",")
    if item.strip()
)


def _allowed_networks() -> tuple[ipaddress._BaseNetwork, ...]:
    return tuple(ipaddress.ip_network(item, strict=False) for item in ALLOWED_CIDRS)


ALLOWED_NETWORKS = _allowed_networks()
REJECT_LOG_DEDUP_SEC = float(os.getenv("OLLAMA111_PROXY_REJECT_LOG_DEDUP_SEC", "60"))
_LAST_REJECT_LOG: dict[str, float] = {}


def _is_allowed(peer_ip: str) -> bool:
    try:
        ip = ipaddress.ip_address(peer_ip)
    except ValueError:
        return False
    return any(ip in network for network in ALLOWED_NETWORKS)


async def _pipe(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
    try:
        while True:
            data = await reader.read(65536)
            if not data:
                break
            writer.write(data)
            await writer.drain()
    except (ConnectionError, asyncio.CancelledError):
        pass
    finally:
        try:
            writer.close()
            await writer.wait_closed()
        except Exception:
            pass


async def _handle_client(reader: asyncio.StreamReader, writer: asyncio.StreamWriter) -> None:
    peer = writer.get_extra_info("peername")
    peer_ip = peer[0] if peer else "unknown"
    if not _is_allowed(peer_ip):
        now = time.monotonic()
        last_log = _LAST_REJECT_LOG.get(peer_ip, 0.0)
        if now - last_log >= REJECT_LOG_DEDUP_SEC:
            logging.warning("reject peer=%s allowed=%s", peer_ip, ",".join(ALLOWED_CIDRS))
            _LAST_REJECT_LOG[peer_ip] = now
        writer.close()
        await writer.wait_closed()
        return

    try:
        target_reader, target_writer = await asyncio.open_connection(TARGET_HOST, TARGET_PORT)
    except Exception as exc:
        logging.error("target connect failed peer=%s target=%s:%s err=%r", peer_ip, TARGET_HOST, TARGET_PORT, exc)
        writer.close()
        await writer.wait_closed()
        return

    logging.info("proxy peer=%s -> %s:%s", peer_ip, TARGET_HOST, TARGET_PORT)
    await asyncio.gather(
        _pipe(reader, target_writer),
        _pipe(target_reader, writer),
    )


async def _main() -> None:
    logging.basicConfig(
        level=os.getenv("OLLAMA111_PROXY_LOG_LEVEL", "INFO"),
        format="%(asctime)s %(levelname)s %(message)s",
        stream=sys.stdout,
    )
    server = await asyncio.start_server(_handle_client, LISTEN_HOST, LISTEN_PORT)
    sockets = ", ".join(str(sock.getsockname()) for sock in (server.sockets or []))
    logging.info(
        "ollama111 allow proxy listening=%s target=%s:%s allowed=%s",
        sockets,
        TARGET_HOST,
        TARGET_PORT,
        ",".join(ALLOWED_CIDRS),
    )

    stop = asyncio.Event()
    loop = asyncio.get_running_loop()
    for sig in (signal.SIGINT, signal.SIGTERM):
        loop.add_signal_handler(sig, stop.set)

    async with server:
        await stop.wait()
        server.close()
        await server.wait_closed()


if __name__ == "__main__":
    asyncio.run(_main())
