#!/usr/bin/python3

#
# Copyright (C) 2025 Nethesis S.r.l.
# SPDX-License-Identifier: GPL-2.0-only
#

import os
import re
import sys
import json
import subprocess
import argparse
from euci import EUci
from nethsec import utils
import logging
import logging.handlers

out_dir = "/etc/ha"

# Syslog logger for info-level
logger = logging.getLogger('ns-ha')
if not logger.handlers:
    handler = logging.handlers.SysLogHandler(address='/dev/log')
    formatter = logging.Formatter('%(name)s: %(levelname)s: %(message)s')
    handler.setFormatter(formatter)
    logger.addHandler(handler)
logger.setLevel(logging.INFO)

def disable_interfaces(file):
    u = EUci()
    with open(os.path.join(out_dir, file), 'r') as f:
        interfaces = json.load(f)
    for interface in interfaces.keys():
        # bring down the interface, do not check with ifstatus because exit code is not reliable
        subprocess.run(["/sbin/ifdown", interface], capture_output=True)
        # Return code of ifdown is not reliable, so we do not check it nor log it
        logger.info("Bringing down interface %s", interface)

def disable_hotspot():
    u = EUci()
    devices = utils.get_all_by_type(u, 'network', 'device')
    for d in devices:
        device = devices[d]
        tags = device.get('ns_tag', [])
        if 'ha' in tags and device.get('ns_macaddr'):
            # Get the original MAC address using ethtool
            result = subprocess.run(["ethtool", "-P", device.get('name')], capture_output=True, text=True)
            for line in result.stdout.splitlines():
                if "Permanent address:" in line:
                    original_mac = line.split("Permanent address:")[1].strip()
                    break
            else:
                original_mac = ""
            if original_mac:
                # Set the original MAC address on the device
                subprocess.run(["/sbin/ip", "link", "set", "dev", device.get('name'), "address", original_mac], check=True)
                logger.info("Restored original mac address on device %s to %s: %s", device.get('name'), original_mac, "success" if proc.returncode == 0 else "fail")

def stop_services():
    # Force stop of services managed by keepalived on backup node:
    # it seems the sh code is not very reliable
    services = set()
    hotplug_dir = "/etc/hotplug.d/keepalived"
    try:
        for fname in sorted(os.listdir(hotplug_dir)):
            path = os.path.join(hotplug_dir, fname)
            if not os.path.isfile(path):
                continue
            try:
                with open(path, 'r') as fh:
                    content = fh.read()
                    # Only consider this file if it contains the marker to stop services on backup
                    if 'set_stop_if_backup' not in content:
                        continue

                    for line in content.splitlines():
                        # match: set_service_name <name>  (allow optional quotes)
                        m = re.search(r'\bset_service_name\s+([\'\"]?)([A-Za-z0-9._-]+)\1', line)
                        if m:
                            services.add(m.group(2))
            except Exception:
                # ignore unreadable files
                continue
    except Exception:
        # directory may not exist or be unreadable
        return

    for svc in services:
        init_script = os.path.join("/etc/init.d", svc)
        if os.path.exists(init_script) and os.path.isfile(init_script):
            subprocess.run([init_script, "stop"], capture_output=True)

if __name__ == "__main__":
	# Parse --force flag to override HA role check
	parser = argparse.ArgumentParser(add_help=False)
	parser.add_argument("--force", action="store_true", help="Run disable actions regardless of HA role")
	args, _ = parser.parse_known_args()

	if not args.force:
		# Execute only on backup node (preserve previous behavior)
		try:
			proc = subprocess.run(["/usr/libexec/rpcd/ns.ha", "call", "status"], capture_output=True, text=True, check=True)
			status = json.loads(proc.stdout)
		except Exception:
			# If we can't determine role, be conservative and exit
			status = {}
		if status.get("role") != "backup":
			sys.exit(0)
	# If --force is provided, skip the role check and proceed
	disable_interfaces('wg_interfaces')
	disable_interfaces('ipsec_interfaces')
	disable_interfaces('wan_interfaces')
	disable_hotspot()
     # Do not apply network changes yet, they will be applied when switching to primary
	if args.force:
		# Tear down everything immediately in case of fault or stop
		stop_services()