#!/usr/bin/env python3
"""NACEF SIC localhost bridge (DEV/demo helper).

The Ministry SIC Agent (port 10006) only accepts requests whose source is
*localhost* (its SICRequestFilter rejects anything else with HTTP 401). When
Odoo runs inside a Docker container it reaches the host as the bridge gateway
IP (e.g. 172.20.0.1), so the SIC refuses it.

This tiny TCP forwarder runs ON THE HOST. It listens on the Docker gateway
address and forwards each connection to 127.0.0.1:10006 — so the SIC sees the
forwarded connection as localhost and accepts it. Point Odoo's "S-MDF Agent
URL" at this listener (e.g. http://172.20.0.1:10106).

Security: it binds ONLY to the Docker gateway IP (not 0.0.0.0), so exposure is
limited to the Docker network. It effectively lets containers on that network
reach the SIC as localhost — do NOT bind it to a public interface.

In production this bridge is unnecessary: Odoo runs natively on the same host
as the SIC, so localhost already works.

Usage:
    python3 nacef_sic_bridge.py            # 172.20.0.1:10106 -> 127.0.0.1:10006
    LISTEN_HOST=172.20.0.1 LISTEN_PORT=10106 \
    TARGET_HOST=127.0.0.1  TARGET_PORT=10006 python3 nacef_sic_bridge.py
"""
import os
import socket
import threading

LISTEN_HOST = os.environ.get("LISTEN_HOST", "172.20.0.1")
LISTEN_PORT = int(os.environ.get("LISTEN_PORT", "10106"))
TARGET_HOST = os.environ.get("TARGET_HOST", "127.0.0.1")
TARGET_PORT = int(os.environ.get("TARGET_PORT", "10006"))


def _pipe(src, dst):
    try:
        while True:
            data = src.recv(65536)
            if not data:
                break
            dst.sendall(data)
    except OSError:
        pass
    finally:
        for s in (src, dst):
            try:
                s.shutdown(socket.SHUT_RDWR)
            except OSError:
                pass


def _handle(client):
    try:
        upstream = socket.create_connection((TARGET_HOST, TARGET_PORT),
                                            timeout=10)
    except OSError:
        client.close()
        return
    threading.Thread(target=_pipe, args=(client, upstream), daemon=True).start()
    threading.Thread(target=_pipe, args=(upstream, client), daemon=True).start()


def main():
    srv = socket.socket()
    srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    srv.bind((LISTEN_HOST, LISTEN_PORT))
    srv.listen(64)
    print("NACEF SIC bridge: %s:%d -> %s:%d"
          % (LISTEN_HOST, LISTEN_PORT, TARGET_HOST, TARGET_PORT), flush=True)
    while True:
        client, _addr = srv.accept()
        _handle(client)


if __name__ == "__main__":
    main()
