#!/usr/bin/env python3
"""
patch-ncm.py — Inject P5G Marvis + P5G Radio pages into the NCM React bundle.

Reads from the clean .bak, applies all patches, writes the result.
Safe to re-run (idempotent).
"""

import os
import socket
import sys


JS  = "/etc/athonet/ems-frontend/advanced/assets/index-Cw8Irsq8.js"
BAK = JS + ".bak"

with open(BAK, "r", encoding="utf-8") as f:
    src = f.read()

changed = False


def env_flag(name: str) -> str | None:
    value = os.getenv(name)
    if value is None:
        return None
    return value.strip().lower()


def is_enabled(name: str, default_host: str, default_port: int) -> bool:
    flag = env_flag(name)
    if flag in {"1", "true", "yes", "on"}:
        return True
    if flag in {"0", "false", "no", "off"}:
        return False
    host = os.getenv(f"{name}_HOST", default_host)
    port = int(os.getenv(f"{name}_PORT", str(default_port)))
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.settimeout(0.5)
        return sock.connect_ex((host, port)) == 0


ENABLE_MARVIS = is_enabled("P5G_MARVIS_ENABLED", "127.0.0.1", 8100)
ENABLE_RADIO = is_enabled("P5G_RADIO_ENABLED", "127.0.0.1", 4000)

if not ENABLE_MARVIS and not ENABLE_RADIO:
    print("ERROR: neither P5G Marvis nor P5G Radio appears reachable; refusing to patch")
    sys.exit(1)

# ── 1. Sidebar nav entry ───────────────────────────────────────────────────
SIDEBAR_OLD = '!_o(ue.UPF,t)}]}]}'
sidebar_items = []
if ENABLE_MARVIS:
    sidebar_items.append(
        '{value:"/marvis",label:"P5G Marvis",'
        'icon:a.jsx(ge.Magic,{}),disabled:false,'
        'subItems:['
        '{value:"/marvis/overview",label:"P5G Marvis Insights",disabled:false},'
        '{value:"/marvis/actions",label:"P5G Marvis Actions",disabled:false},'
        '{value:"/marvis/minis",label:"P5G Marvis Minis",disabled:false},'
        '{value:"/marvis/ai",label:"P5G Marvis AI",disabled:false}'
        ']}'
    )
if ENABLE_RADIO:
    sidebar_items.append(
        '{value:"/radio",label:"P5G Radio",'
        'icon:a.jsx(ge.Magic,{}),disabled:false}'
    )
SIDEBAR_NEW = '!_o(ue.UPF,t)}]},' + ",".join(sidebar_items) + ']}'

if SIDEBAR_OLD in src:
    src = src.replace(SIDEBAR_OLD, SIDEBAR_NEW, 1)
    print("Applied: sidebar entry")
    changed = True
elif any(label in src for label in ["P5G Marvis", "P5G Radio"]):
    print("Skipped: sidebar entry already present")
else:
    print("ERROR: sidebar anchor not found"); exit(1)

# ── 2. React Router route with iframe element ──────────────────────────────
# Insert between G4t (UPF route object) and rht (Platform route object)
# in the router children array — this is the unique anchor in the route config
ROUTE_ANCHOR = 'G4t,rht'

# Wrap in Or({fullHeight:!0}) exactly like every other NF route — this gives the
# NCM AppBar + left-navigation sidebar via G0, and a content area that properly
# fills the remaining height.  The iframe then uses height:"100%" to fill it.
MARVIS_ROUTE = ('{path:"marvis",'
                'element:a.jsx(Or,{fullHeight:!0}),'
                'handle:vr({labelIntl:"P5G Marvis",icon:a.jsx(ge.Magic,{})}),'
                'children:['
                '{index:!0,element:a.jsx(ur,{to:"overview",replace:!0})},'
                '{path:"overview",element:a.jsx("iframe",{src:"/core/marvis/overview",style:{display:"block",width:"100%",height:"100%",border:"none"},title:"P5G Marvis Insights"})},'
                '{path:"actions",element:a.jsx("iframe",{src:"/core/marvis/actions",style:{display:"block",width:"100%",height:"100%",border:"none"},title:"P5G Marvis Actions"})},'
                '{path:"minis",element:a.jsx("iframe",{src:"/core/marvis/minis",style:{display:"block",width:"100%",height:"100%",border:"none"},title:"P5G Marvis Minis"})},'
                '{path:"ai",element:a.jsx("iframe",{src:"/core/marvis/",style:{display:"block",width:"100%",height:"100%",border:"none"},title:"P5G Marvis AI"})}'
                ']}')  # closed marvis route

RADIO_ROUTE = ('{path:"radio",'
               'element:a.jsx(Or,{fullHeight:!0}),'
               'handle:vr({labelIntl:"P5G Radio",icon:a.jsx(ge.Magic,{})}),'
               'children:['
               '{index:!0,element:a.jsx("iframe",{src:"/core/radio/",'
               'style:{display:"block",width:"100%",height:"100%",border:"none"},'
               'title:"P5G Radio"})}'
               ']}')
ROUTE_MARKER = 'path:"marvis"'

RADIO_MARKER  = 'path:"radio"'

route_parts = []
if ENABLE_MARVIS:
    route_parts.append(MARVIS_ROUTE)
if ENABLE_RADIO:
    route_parts.append(RADIO_ROUTE)

if ROUTE_ANCHOR in src and ROUTE_MARKER not in src and RADIO_MARKER not in src:
    src = src.replace(ROUTE_ANCHOR, 'G4t,' + ",".join(route_parts) + ',rht', 1)
    print("Applied: injected iframe routes")
    changed = True
elif ROUTE_MARKER in src or RADIO_MARKER in src:
    print("Skipped: route entry already present")
else:
    print("WARNING: route anchor G4t,rht not found — iframe routes not injected")

# ── Validate ───────────────────────────────────────────────────────────────
# ── 3. Register /marvis in the BW permissions registry ────────────────────
# BW is the route→permissions map. If a path is missing, the app throws
# "Permissions for route /marvis are not managed". Tt=()=>!0 means no
# specific permission required (same as profile and siteLoader routes).
PERMS_OLD = '...QUe}'
perm_entries = []
if ENABLE_MARVIS:
    perm_entries.extend([
        '"/marvis":Tt',
        '"/marvis/overview":Tt',
        '"/marvis/actions":Tt',
        '"/marvis/minis":Tt',
        '"/marvis/ai":Tt',
    ])
if ENABLE_RADIO:
    perm_entries.append('"/radio":Tt')
PERMS_NEW = '...QUe,' + ",".join(perm_entries) + '}'

if PERMS_OLD in src and PERMS_NEW not in src:
    src = src.replace(PERMS_OLD, PERMS_NEW, 1)
    print("Applied: permissions entry")
    changed = True
elif all(entry in src for entry in perm_entries):
    print("Skipped: permissions entry already present")
else:
    print("WARNING: BW permissions anchor not found — permissions not registered")

if ENABLE_MARVIS:
    assert src.count('P5G Marvis') >= 1, "P5G Marvis not found after patch"
if ENABLE_RADIO:
    assert src.count('P5G Radio') >= 1, "P5G Radio not found after patch"

if not changed:
    print("Nothing changed — already fully patched")
    exit(0)

with open(JS, "w", encoding="utf-8") as f:
    f.write(src)

print(
    f"Done — P5G Marvis: {src.count('P5G Marvis')} occurrences, "
    f"P5G Radio: {src.count('P5G Radio')} occurrences"
)
