#!/usr/bin/env python3
"""
This script is called by OpenVPN when a client has successfully connected
(see learn-address in server.conf) to allow traffic through the firewalls
with the following params passed in via ENV:
- operation
- address
"""

import subprocess

import central_sdi
import grpc
import sys
import central_sdi.sessions
from central_sdi import util
from netops.ip_access import remote


if __name__ == "__main__":
    # will be one of add, update or delete
    # search for "learn-address" in https://openvpn.net/community-resources/reference-manual-for-openvpn-2-4/
    if sys.argv[1] == 'delete':
        exit(0)

    central_sdi.init()
    env = central_sdi.env
    logger = env.logger
    conn = env.connection
    sdi_header = util.log_header(conn.node_id, conn.username)
    client_mac = sys.argv[2]

    # trim suffix, presumably present since openvpn 2.5
    if client_mac and (index := client_mac.find('@')) >= 0:
        client_mac = client_mac[:index]

    if conn.client_mac == client_mac:
        exit(0)
    if conn.client_mac is not None:
        logger.warning(f'{sdi_header} VPN central connection failed - Mismatch client {client_mac} and connection {conn.client_mac} MAC')
        exit(1)
    if env.cache.check_reservation(client_mac):
        if not conn.logger_mute_flag:
            conn.logger_mute_flag = True
            env.connection = conn
            logger.warning(f'{sdi_header} VPN central connection failed - duplicate MAC {client_mac}')
        else:
            logger.warning(f'{sdi_header} VPN central connection failed - reservation error for MAC {client_mac}')
        exit(1)

    with grpc.insecure_channel(f"{conn.node_ip}:{conn.node_port}") as channel:
        try:
            remote.SessionsStub(channel).SetUserMac(remote.SessionsSetUserMacRequest(
                session_id=conn.session_id,
                user_mac=client_mac,
            ))
        except grpc.RpcError as e:
            code = e.code()  # pylint: disable=no-member
            if code == grpc.StatusCode.ALREADY_EXISTS:
                if not conn.logger_mute_flag:
                    conn.logger_mute_flag = True
                    env.connection = conn
                    logger.warning(f'{sdi_header} VPN remote connection failed - duplicate MAC {client_mac}')
                exit(1)
            raise
        remote.SessionsStub(channel).Connect(remote.SessionsConnectRequest(session_id=conn.session_id))

    conn.setup_commands = [
        [
            'ebtables',
            '-I',
            'IpAccessForward',
            '-d',
            client_mac,
            '-i',
            conn.gretap_iface,
            '-j',
            'ACCEPT',
        ],
        [
            'ebtables',
            '-I',
            'IpAccessForward',
            '-s',
            client_mac,
            '-o',
            conn.gretap_iface,
            '-j',
            'ACCEPT',
        ],
    ]
    conn.teardown_commands = [
        [
            'ebtables',
            '-D',
            'IpAccessForward',
            '-d',
            client_mac,
            '-i',
            conn.gretap_iface,
            '-j',
            'ACCEPT',
        ],
        [
            'ebtables',
            '-D',
            'IpAccessForward',
            '-s',
            client_mac,
            '-o',
            conn.gretap_iface,
            '-j',
            'ACCEPT',
        ],
    ]
    env.connection = conn

    for command in conn.setup_commands:
        subprocess.run(command, timeout=60, check=True)

    conn.client_mac = client_mac
    env.connection = conn
    env.cache.reserve_address(client_mac)

    logger.info(f'{sdi_header} VPN client identified by MAC {client_mac}')
