-- ~/.hammerspoon/NetworkCenter.lua
local NetworkCenter = {}
local Config = require("Config")
local menuBar = hs.menubar.new()

-- State Management
local nasOnline = false
local vpnActive = false
local ovpnIP = "Offline"
local tailscaleIP = "Offline"
local tailnetDomain = "Offline" -- NEW: Added state for domain
local speedTestResult = "Idle"
local lastExternalIP = "Offline"
local ispName = "Fetching..."
local wifiInfo = { 
    ssid = "N/A", speed = 0, gen = "N/A", band = "N/A", 
    channel = "N/A", protocol = "N/A", rssi = 0, noise = 0 
}
local gatewayLatency = "---"

-- Service Monitoring State
local services = {
    { name = "Plex", host = "192.168.1.105", port = 32400 },
    { name = "Komga", host = "192.168.1.101", port = 25600 },
    { name = "Sonarr", host = "192.168.1.101", port = 8989 },
    { name = "Radarr", host = "192.168.1.101", port = 7878 },
    { name = "qBitorrent", host = "192.168.1.101", port = 8080 }
}
local serviceStatus = {}

-- ==========================================
-- 1. DATA RETRIEVAL
-- ==========================================
local function getWifiDetails()
    local details = hs.wifi.interfaceDetails()
    if details then
        wifiInfo.ssid = details.ssid or "Disconnected"
        wifiInfo.speed = details.transmitRate or 0
        wifiInfo.rssi = details.rssi or 0
        wifiInfo.noise = details.noise or 0
        
        local phy = details.activePHYMode
        local phyStr = tostring(phy)
        
        if phy == 6 or phyStr:find("6") or phyStr:find("ax") then 
            wifiInfo.gen, wifiInfo.protocol = "6", "AX"
        elseif phy == 5 or phyStr:find("5") or phyStr:find("ac") then 
            wifiInfo.gen, wifiInfo.protocol = "5", "AC"
        elseif phy == 4 or phyStr:find("4") or phyStr:find("n") then 
            wifiInfo.gen, wifiInfo.protocol = "4", "N"
        else 
            wifiInfo.gen, wifiInfo.protocol = "?", "?"
        end

        if details.wlanChannel and type(details.wlanChannel) == "table" then
            wifiInfo.band = tostring(details.wlanChannel.band):gsub("GHz", "") or "N/A"
            wifiInfo.channel = tostring(details.wlanChannel.number) or "N/A"
        end
    else
        wifiInfo.ssid = hs.wifi.currentNetwork() or "N/A"
    end
end

function NetworkCenter.updateStatus()
    getWifiDetails()

    -- Async NAS Ping
    hs.task.new("/sbin/ping", function(code)
        nasOnline = (code == 0)
        NetworkCenter.refreshUI()
    end, {"-c", "1", "-t", "1", Config.nasIP}):start()

    -- Gateway Latency
    hs.task.new("/sbin/ping", function(code, stdOut)
        if code == 0 and stdOut then
            local ms = stdOut:match("time=(%d+%.?%d*) ms")
            gatewayLatency = ms and (math.floor(tonumber(ms)) .. "ms") or "Error"
        else
            gatewayLatency = "Timeout"
        end
        NetworkCenter.refreshUI()
    end, {"-c", "1", "-t", "1", "192.168.1.1"}):start()

    -- Service Port Checks
    for _, svc in ipairs(services) do
        hs.task.new("/usr/bin/nc", function(code)
            serviceStatus[svc.name] = (code == 0 and "🟢" or "🔴")
            NetworkCenter.refreshUI()
        end, {"-z", "-w", "2", svc.host, tostring(svc.port)}):start()
    end

    -- Tailscale Detection & Domain Retrieval
    local tsPath = "/Applications/Tailscale.app/Contents/MacOS/Tailscale"
    local tsStatus = hs.execute(tsPath .. " status --json 2>/dev/null")
    local foundTSIP = "Offline"
    local foundTSDomain = "Offline"

    if tsStatus and tsStatus ~= "" then
        local tsData = hs.json.decode(tsStatus)
        if tsData and tsData.BackendState == "Running" then
            -- Get the Tailnet name/domain
            foundTSDomain = tsData.MagicDNSSuffix or "N/A"
            
            if tsData.Self and tsData.Self.TailscaleIPs then
                for _, ip in ipairs(tsData.Self.TailscaleIPs) do
                    if ip:match("^100%.") then foundTSIP = ip break end
                end
            end
        end
    end
    tailscaleIP = foundTSIP
    tailnetDomain = foundTSDomain

    -- OpenVPN Detection
    local ovpnCheck = hs.execute("pgrep -x 'OpenVPN Connect'"):gsub("%s+", "")
    local ovpnIPQuery = hs.execute("ifconfig | grep -A 1 'utun' | grep 'inet 10.' | awk '{print $2}'"):gsub("%s+", "")
    
    if ovpnCheck ~= "" and ovpnIPQuery ~= "" then
        vpnActive = true
        ovpnIP = ovpnIPQuery
    else
        vpnActive = false
        ovpnIP = "Offline"
    end
    
    -- External IP & ISP
    hs.task.new("/usr/bin/curl", function(exitCode, stdOut)
        if exitCode == 0 and stdOut then
            local data = hs.json.decode(stdOut)
            if data then
                lastExternalIP = data.ip or "Offline"
                ispName = data.asn_org or data.company_name or "Unknown ISP"
            end
        else
            lastExternalIP = "Offline"
            ispName = "Offline"
        end
        NetworkCenter.refreshUI()
    end, {"-s", "-m", "5", "https://ifconfig.co/json"}):start()
end

local function runSpeedTest()
    speedTestResult = "Testing..."
    NetworkCenter.refreshUI()
    hs.task.new("/usr/bin/networkQuality", function(exitCode, stdOut)
        if exitCode == 0 and stdOut then
            local data = hs.json.decode(stdOut)
            if data then
                local downMbps = math.floor((data.dl_throughput / 1048576) + 0.5)
                local upMbps = math.floor((data.ul_throughput / 1048576) + 0.5)
                speedTestResult = string.format("D:%d / U:%d Mbps", downMbps, upMbps)
            else
                speedTestResult = "Parse Error"
            end
        else
            speedTestResult = "Failed"
        end
        NetworkCenter.refreshUI()
    end, {"-c"}):start()
end

-- ==========================================
-- 2. ACTIONS & STEALTH LOGIC
-- ==========================================

local function fastHideOpenVPN()
    local count = 0
    local hideTimer
    hideTimer = hs.timer.doEvery(0.02, function()
        local app = hs.application.get("OpenVPN Connect")
        if app then 
            app:hide()
            hs.applescript.applescript('tell application "System Events" to set visible of process "OpenVPN Connect" to false')
            local win = app:mainWindow()
            if win then win:setFrame(hs.geometry.rect(5000, 5000, 0, 0), 0) end
        end
        count = count + 1
        if count > 75 then hideTimer:stop() end 
    end)
end

local function watchForVPN()
    local attempts = 0
    local watchTimer
    watchTimer = hs.timer.doEvery(1, function()
        attempts = attempts + 1
        local checkIP = hs.execute("ifconfig | grep -A 1 'utun' | grep 'inet 10.'"):gsub("%s+", "")
        if checkIP ~= "" or attempts > 10 then
            NetworkCenter.updateStatus()
            watchTimer:stop()
        end
    end)
end

local function vpnAction(mode)
    if mode == "connect" then
        hs.alert.show("Connecting VPN...")
        fastHideOpenVPN()
        hs.execute(string.format('"%s" --connect-shortcut=%s', Config.openvpnPath, Config.vpnProfileID))
        watchForVPN()
    else
        hs.alert.show("Disconnecting VPN...")
        hs.execute(string.format('"%s" --disconnect-shortcut', Config.openvpnPath))
        hs.timer.doAfter(0.5, function()
            hs.execute("pkill -9 'OpenVPN Connect'")
            NetworkCenter.updateStatus()
        end)
    end
end

local function tailscaleAction(mode)
    local tsPath = "/Applications/Tailscale.app/Contents/MacOS/Tailscale"
    if mode == "up" then
        hs.alert.show("Starting Tailscale...")
        hs.execute(tsPath .. " up")
    else
        hs.alert.show("Stopping Tailscale...")
        hs.execute(tsPath .. " down")
    end
    hs.timer.doAfter(1, NetworkCenter.updateStatus)
    hs.timer.doAfter(3, NetworkCenter.updateStatus)
end

-- ==========================================
-- 3. THE MENU UI
-- ==========================================

function NetworkCenter.refreshUI()
    if not menuBar then return end
    
    local isHome = (wifiInfo.ssid == Config.homeSSID)
    local locationStr = isHome and "HOME" or "REMOTE"
    local topIcon = isHome and "🏠" or "🌐"
    
    local tsActive = (tailscaleIP ~= "Offline")
    if vpnActive and tsActive then
        topIcon = topIcon .. "🛡️" 
    elseif vpnActive then
        topIcon = topIcon .. "🔒" 
    elseif tsActive then
        topIcon = topIcon .. "📡" 
    end
    
    menuBar:setTitle(topIcon .. " " .. math.floor(wifiInfo.speed) .. "Mb")

    local shareSubMenu = {}
    for i, share in ipairs(Config.shares) do
        local isMounted = hs.fs.attributes("/Volumes/" .. share) ~= nil
        local sIcon = isMounted and "✅" or "⚪️"
        local titleText = sIcon .. " " .. share
        if isMounted then
            local capCmd = "df -h '/Volumes/" .. share .. "' | tail -1 | awk '{gsub(\"i\", \"b\", $2); print \"(\" $2 \")\"}'"
            titleText = titleText .. " " .. hs.execute(capCmd):gsub("\n", "")
        end
        table.insert(shareSubMenu, { 
            title = titleText, 
            fn = function()
                if isMounted then hs.execute("diskutil unmount /Volumes/" .. share)
                else hs.applescript.applescript(string.format('mount volume "smb://%s/%s"', Config.nasIP, share)) end
                hs.timer.doAfter(3, NetworkCenter.updateStatus)
            end
        })
        if isMounted then
            local barCmd = "df -h '/Volumes/" .. share .. "' | tail -1 | awk '{p=int($5); bar=\"\"; for(i=1;i<=10;i++){if(i<=p/10) bar=bar \"■\"; else bar=bar \"□\"} gsub(\"i\", \"b\", $3); print \"        [\" bar \"] \" p \"% (\" $3 \" Used)\"}'"
            table.insert(shareSubMenu, { title = hs.execute(barCmd):gsub("\n", ""), disabled = true })
            table.insert(shareSubMenu, { title = "        📂 Browse Folder", fn = function() hs.execute("open /Volumes/" .. share) end })
            table.insert(shareSubMenu, { title = "-" })
        end
    end

    local labSubMenu = {}
    local labIcons = ""
    for i, svc in ipairs(services) do
        local status = serviceStatus[svc.name] or "⚪️"
        labIcons = labIcons .. status
        table.insert(labSubMenu, {
            title = status .. " " .. svc.name,
            fn = function() hs.execute(string.format("open http://%s:%s", svc.host, svc.port)) end
        })
    end

    local ovpnHeader = "🔐 OPENVPN CONTROLS" .. (vpnActive and " (🟢 ACTIVE)" or "")
    local tsHeader = "🚀 TAILSCALE MESH" .. (tsActive and " (🟢 ACTIVE)" or "")

    local menu = {
        { title = "📍 LOCATION STATUS" },
        { title = "  ├─ Profile: " .. locationStr, disabled = true },
        { title = "  ├─ Gateway: " .. gatewayLatency, disabled = true },
        { title = "  ├─ Channel: " .. wifiInfo.channel .. " (" .. wifiInfo.band .. " GHz)", disabled = true },
        { title = "  ├─ Signal: " .. wifiInfo.rssi .. " dBm / Noise: " .. wifiInfo.noise .. " dBm", disabled = true },
        { title = "  └─ WiFi " .. wifiInfo.gen .. " (" .. wifiInfo.protocol .. ") @ " .. wifiInfo.speed .. " Mbps", disabled = true },
        { title = "-" },
        { title = "🚀 LAB SERVICES " .. labIcons, menu = labSubMenu },
        { title = "-" },
        { title = "🛡️ VPN & MESH NET" },
        { title = "  ├─ OpenVPN: " .. ovpnIP, disabled = true },
        { title = "  ├─ Tailscale IP: " .. tailscaleIP, disabled = true },
        { title = "  ├─ Tailnet: " .. tailnetDomain, disabled = true }, -- NEW: Display Domain
        { title = "  ├─ ISP: " .. ispName, disabled = true },
        { title = "  └─ Public IP: " .. lastExternalIP, disabled = true },
        { title = "-" },
        { title = "📦 STORAGE & SHARES", menu = shareSubMenu },
        { title = "  ├─ NAS IP: " .. Config.nasIP, disabled = true },
        { title = "  └─ NAS Status: " .. (nasOnline and "🟢 Online" or "🔴 Offline"), disabled = true },
        { title = "-" },
        { title = "⚡️ SPEED TEST: " .. speedTestResult, fn = runSpeedTest },
        { title = "-" },
        { title = ovpnHeader },
        { title = "  ├─ 🔒 Connect tunnel", fn = function() vpnAction("connect") end },
        { title = "  └─ 🔓 Disconnect & Quit", fn = function() vpnAction("disconnect") end },
        { title = "-" },
        { title = tsHeader },
        { title = "  ├─ 🟢 Start Service", fn = function() tailscaleAction("up") end },
        { title = "  └─ 🔴 Stop Service", fn = function() tailscaleAction("down") end },
        { title = "-" },
        { title = "🔄 Refresh Status", fn = NetworkCenter.updateStatus }
    }

    menuBar:setMenu(menu)
end

NetworkCenter.updateStatus()
hs.timer.doEvery(30, NetworkCenter.updateStatus) 
hs.alert.show("Network Center Platinum Loaded")

return NetworkCenter