"""
network.py — Network and infrastructure functions.
"""

import os
import sys
import urllib.request
import urllib.error

from modules.output import warn, info, good, bad
from modules.utils import require_bin, run


def cfallow():
    """
    Whitelist Cloudflare IPv4 ranges in CSF.
    Uses csf -g to check each range against live iptables rules — catches
    IPs added via cloudflare.allow or any other CSF include file.
    """
    if not require_bin('csf', 'cfallow'):
        return

    cf_ips = _get_cloudflare_ips()
    if not cf_ips:
        return

    print(f"Checking {len(cf_ips)} Cloudflare ranges against live CSF rules...")
    missing = []
    for ip in cf_ips:
        out, _, _ = run(['csf', '-g', ip])
        # csf -g shows 'ACCEPT' in the iptables output if the range is already allowed
        if 'ACCEPT' in out:
            print(f"  OK:      {ip}")
        else:
            print(f"  Missing: {ip}")
            missing.append(ip)

    if not missing:
        good(f"All {len(cf_ips)} Cloudflare ranges are already whitelisted.")
        return

    print(f"\nAdding {len(missing)} missing ranges...")
    for ip in missing:
        run(['csf', '-a', ip, 'Cloudflare'])
        print(f"  Added: {ip}")

    print("Reloading CSF...")
    run(['csf', '-r'])
    good("Done.")


def _get_cloudflare_ips():
    """Fetch Cloudflare IPv4 list and return as a list of strings."""
    try:
        req = urllib.request.Request(
            'https://www.cloudflare.com/ips-v4/',
            headers={'User-Agent': 'curl/7.68.0'}
        )
        with urllib.request.urlopen(req, timeout=10) as resp:
            content = resp.read().decode('utf-8')
        return [line.strip() for line in content.splitlines() if line.strip()]
    except Exception as e:
        warn(f"Could not fetch Cloudflare IPs: {e}")
        return []


def ggnull(ip):
    """Null-route an IP address using the kernel routing table."""
    if not require_bin('route', 'ggnull'):
        return
    _, err, rc = run(['route', 'add', '-host', ip, 'reject'])
    if rc == 0:
        good(f"Null route added for {ip}")
    else:
        bad(f"Failed to add null route for {ip}: {err.strip()}")


def ggpaste(paste_type, filepath=None):
    """
    Upload content to the GreenGeeks paste service.

    Usage:
        ggpaste('text', '/path/to/file')     — upload a file
        ggpaste('text')                       — read from stdin
    """
    if not require_bin('curl', 'ggpaste'):
        return

    auth_header = 'GGAUTH: ggpaste_07082022'
    endpoint = 'https://paste.websitehostserver.net/vps/vps_paste.php'

    if filepath:
        _, err, rc = run(
            ['curl', '--header', auth_header,
             '--form', f't={paste_type}',
             '--form', f'file=@{filepath}',
             endpoint]
        )
    else:
        # Read from stdin
        import subprocess
        data = sys.stdin.read()
        proc = subprocess.run(
            ['curl', '--header', auth_header,
             '--form', f't={paste_type}',
             '--form', 'file=@-',
             endpoint],
            input=data,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE
        )
        print(proc.stdout.decode('utf-8', errors='replace'))
        return

    if rc == 0:
        pass  # curl prints the URL itself
    else:
        warn(f"ggpaste failed: {err.strip()}")


def install_softaculous():
    """Download and run the GreenGeeks Softaculous installation script."""
    if not require_bin('curl', 'install-softaculous'):
        return
    if not require_bin('bash', 'install-softaculous'):
        return

    warn("This will download and execute a remote script. Proceed? (y/N) ", )
    try:
        confirm = input().strip().lower()
    except EOFError:
        confirm = 'n'

    if confirm != 'y':
        info("Cancelled.")
        return

    import subprocess
    curl = subprocess.Popen(
        ['curl', 'https://dl.websitehostserver.net/vps-files/softaculous_install.sh.txt'],
        stdout=subprocess.PIPE, stderr=subprocess.DEVNULL
    )
    bash = subprocess.Popen(
        ['bash'], stdin=curl.stdout
    )
    curl.stdout.close()
    bash.communicate()


def fix_license():
    """Run cpkeyclt to fix cPanel license issues."""
    cpkeyclt = '/usr/local/cpanel/cpkeyclt'
    if not os.path.isfile(cpkeyclt):
        warn(f"cpkeyclt not found at {cpkeyclt}")
        return
    import subprocess
    subprocess.run([cpkeyclt])


def run_summary_script():
    """Download and run the GreenGeeks server migration summary script."""
    if not require_bin('wget', 'summary') and not require_bin('curl', 'summary'):
        warn("Neither wget nor curl found.")
        return
    if not require_bin('php', 'summary'):
        return

    import subprocess
    import tempfile

    with tempfile.NamedTemporaryFile(suffix='.php', delete=False) as tmp:
        tmppath = tmp.name

    try:
        subprocess.run(
            ['wget', '-O', tmppath,
             'https://dl.websitehostserver.net/vps-files/summary.txt'],
            check=True
        )
        subprocess.run(['php', tmppath])
    except Exception as e:
        warn(f"summary script failed: {e}")
    finally:
        if os.path.isfile(tmppath):
            os.unlink(tmppath)


def csf_search(ip=None):
    """Search CSF/iptables rules for an IP address."""
    if not ip:
        try:
            ip = input("  IP address: ").strip()
        except (EOFError, KeyboardInterrupt):
            return
    if not ip:
        warn("No IP provided.")
        return
    if not require_bin('csf', 'CSF'):
        return
    out, _, _ = run(['csf', '-g', ip])
    print(out)


def csf_allow(ip=None):
    """Add an IP to CSF allow list (csf.allow)."""
    if not ip:
        try:
            ip = input("  IP address: ").strip()
        except (EOFError, KeyboardInterrupt):
            return
    if not ip:
        warn("No IP provided.")
        return
    if not require_bin('csf', 'CSF'):
        return
    out, _, rc = run(['csf', '-a', ip, 'VPS Tool'])
    print(out)
    if rc == 0:
        good(f"{ip} added to CSF allow list.")
    else:
        warn(f"csf -a returned exit code {rc}")


def csf_unblock(ip=None):
    """Remove an IP from CSF temp bans and/or deny list."""
    if not ip:
        try:
            ip = input("  IP address: ").strip()
        except (EOFError, KeyboardInterrupt):
            return
    if not ip:
        warn("No IP provided.")
        return
    if not require_bin('csf', 'CSF'):
        return
    # Remove from temp ban list
    out_tr, _, _ = run(['csf', '-tr', ip])
    if out_tr.strip():
        print(out_tr)
    # Remove from permanent deny list
    out_dr, _, _ = run(['csf', '-dr', ip])
    if out_dr.strip():
        print(out_dr)
    if not out_tr.strip() and not out_dr.strip():
        info(f"{ip} was not found in temp bans or deny list.")
