"""Blokir internet seluruh PC lewat Windows Firewall (butuh hak Administrator).

Cara kerja: kebijakan outbound firewall diubah jadi "block", lalu hanya diizinkan:
server monitoring (IP hasil resolve), DNS, DHCP, dan program agent itu sendiri.
Saat dibuka, kebijakan dikembalikan ke default Windows (allowoutbound).
"""
import socket
import subprocess
import sys
from urllib.parse import urlparse

RULE_PREFIX = "PCMON_"
CREATE_NO_WINDOW = 0x08000000


def server_host(server_url):
    return urlparse(server_url).hostname or ""


def resolve_ips(host):
    """Semua alamat IPv4 milik host (kosong bila gagal)."""
    if not host:
        return []
    try:
        infos = socket.getaddrinfo(host, None, socket.AF_INET)
    except OSError:
        return []
    return sorted({i[4][0] for i in infos})


def build_block_commands(ips, agent_exe=None):
    rule = "netsh advfirewall firewall add rule name=%s%s dir=out action=allow profile=any %s"
    cmds = [list_cmd for list_cmd in build_cleanup_commands()]
    if ips:
        cmds.append(
            ("netsh advfirewall firewall add rule name=%sALLOW_SERVER dir=out action=allow "
             "profile=any remoteip=%s") % (RULE_PREFIX, ",".join(ips))
        )
    cmds.append(rule % (RULE_PREFIX, "ALLOW_DNS_UDP", "protocol=UDP remoteport=53"))
    cmds.append(rule % (RULE_PREFIX, "ALLOW_DNS_TCP", "protocol=TCP remoteport=53"))
    cmds.append(rule % (RULE_PREFIX, "ALLOW_DHCP", "protocol=UDP remoteport=67"))
    if agent_exe:
        cmds.append(rule % (RULE_PREFIX, "ALLOW_AGENT", 'program="%s"' % agent_exe))
    cmds.append("netsh advfirewall set allprofiles state on")
    cmds.append("netsh advfirewall set allprofiles firewallpolicy blockinbound,blockoutbound")
    return cmds


def build_cleanup_commands():
    return [
        "netsh advfirewall firewall delete rule name=%s%s" % (RULE_PREFIX, n)
        for n in ("ALLOW_SERVER", "ALLOW_DNS_UDP", "ALLOW_DNS_TCP", "ALLOW_DHCP", "ALLOW_AGENT")
    ]


def build_unblock_commands():
    return ["netsh advfirewall set allprofiles firewallpolicy blockinbound,allowoutbound"] + build_cleanup_commands()


class NetBlocker:
    def __init__(self, server_url, dry_run=False):
        self.host = server_host(server_url)
        self.dry_run = dry_run
        self.applied = None   # None=belum pernah, False=terbuka, True=terblokir
        self.applied_ips = ()
        self.history = []     # semua perintah yang pernah dijalankan (untuk test/log)
        self.agent_exe = sys.executable if getattr(sys, "frozen", False) else None

    def _run(self, commands):
        for cmd in commands:
            self.history.append(cmd)
            if self.dry_run:
                continue
            subprocess.run(cmd, shell=False, capture_output=True,
                           creationflags=CREATE_NO_WINDOW if sys.platform == "win32" else 0)

    def apply(self, blocked):
        """Terapkan status yang diinginkan. Hanya menjalankan perintah bila ada perubahan."""
        blocked = bool(blocked)
        if blocked:
            ips = tuple(resolve_ips(self.host)) or self.applied_ips
            if self.applied is True and ips == self.applied_ips:
                return False
            self._run(build_block_commands(list(ips), self.agent_exe))
            self.applied, self.applied_ips = True, ips
            return True
        if self.applied is False:
            return False
        self._run(build_unblock_commands())
        self.applied = False
        return True

    def reset(self):
        self._run(build_unblock_commands())
        self.applied = False
