#Requires -RunAsAdministrator
<#
.SYNOPSIS
    Paqet/GFK Windows Client - Bypass Firewall Restrictions

.DESCRIPTION
    This script helps you connect to your server through firewalls that block normal connections.
    It supports two backends:

    PAQET (Recommended for most users)
    ─────────────────────────────────
    • Simple all-in-one solution with built-in SOCKS5 proxy
    • Uses KCP protocol over raw sockets to bypass DPI
    • Works on: Windows (with Npcap)
    • Configuration: Just needs server IP, port, and encryption key
    • Proxy: 127.0.0.1:1080 (SOCKS5)

    GFW-KNOCKER (For heavily restricted networks)
    ─────────────────────────────────────────────
    • Uses "violated TCP" packets + QUIC tunnel to evade deep packet inspection
    • More complex but better at evading sophisticated firewalls (like GFW)
    • Works on: Windows (with Npcap + Python)
    • Requires: Xray running on server port 443
    • Proxy: 127.0.0.1:14000 (forwards to server's Xray SOCKS5)

    CAN I RUN BOTH?
    ───────────────
    Yes! Both can run simultaneously on different ports:
    • Paqet SOCKS5: 127.0.0.1:1080
    • GFK tunnel:   127.0.0.1:14000
    This lets you have a backup if one method gets blocked.

.NOTES
    Requirements:
    • Administrator privileges (for raw socket access)
    • Npcap (https://npcap.com) - auto-installed if missing
    • Python 3.10+ (GFK only) - auto-installed if missing
#>

param(
    [string]$ServerAddr,
    [string]$Key,
    [string]$Action = "menu",  # menu, run, install, config, stop, status
    [string]$Backend = ""      # paqet, gfk (auto-detect if not specified)
)

$ErrorActionPreference = "Stop"

# Directories and pinned versions (for stability - update after testing new releases)
$InstallDir = "C:\paqet"
$PaqetExe = "$InstallDir\paqet_windows_amd64.exe"
$PaqetVersionPinned = "v1.0.0-alpha.17"   # Fallback if GitHub API unreachable
$GfkDir = "$InstallDir\gfk"
$ConfigFile = "$InstallDir\config.yaml"
$SettingsFile = "$InstallDir\settings.conf"

# Npcap (pinned version)
$NpcapVersion = "1.80"
$NpcapUrl = "https://npcap.com/dist/npcap-$NpcapVersion.exe"
$NpcapInstaller = "$env:TEMP\npcap-$NpcapVersion.exe"

# GFK scripts - bundled locally for faster setup (only works when running from downloaded repo)
# When running via "irm | iex", $MyInvocation.MyCommand.Path is null
$ScriptDir = if ($MyInvocation.MyCommand.Path) { Split-Path -Parent $MyInvocation.MyCommand.Path } else { $null }
$GfkLocalDir = if ($ScriptDir) { "$ScriptDir\..\gfk\client" } else { $null }
$GfkFiles = @("mainclient.py", "quic_client.py", "vio_client.py")  # parameters.py is generated

# Colors
function Write-Info { Write-Host "[INFO] $args" -ForegroundColor Cyan }
function Write-Success { Write-Host "[OK] $args" -ForegroundColor Green }
function Write-Warn { Write-Host "[WARN] $args" -ForegroundColor Yellow }
function Write-Err { Write-Host "[ERROR] $args" -ForegroundColor Red }

# Fetch latest paqet version from GitHub, fall back to pinned
function Get-LatestPaqetVersion {
    try {
        $response = Invoke-RestMethod -Uri "https://api.github.com/repos/hanselime/paqet/releases/latest" -TimeoutSec 10
        if ($response.tag_name -match '^v?\d+\.\d+\.\d+') {
            return $response.tag_name
        }
    } catch {}
    return $PaqetVersionPinned
}
$PaqetVersion = Get-LatestPaqetVersion

# Input validation (security: prevent config injection)
function Test-ValidIP {
    param([string]$IP)
    return $IP -match '^(\d{1,3}\.){3}\d{1,3}$'
}

function Test-ValidMAC {
    param([string]$MAC)
    return $MAC -match '^([0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}$'
}

function Test-SafeString {
    param([string]$s)
    # Block characters that could break Python string literals
    if ($s.Contains('"') -or $s.Contains("'") -or $s.Contains('\') -or $s.Contains([char]10) -or $s.Contains([char]13)) {
        return $false
    }
    return $true
}

#═══════════════════════════════════════════════════════════════════════
# Prerequisite Checks
#═══════════════════════════════════════════════════════════════════════

function Test-Admin {
    $identity = [Security.Principal.WindowsIdentity]::GetCurrent()
    $principal = [Security.Principal.WindowsPrincipal]$identity
    return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
}

function Test-Npcap {
    $npcapPath = "C:\Windows\System32\Npcap"
    $wpcapDll = "C:\Windows\System32\wpcap.dll"
    return (Test-Path $npcapPath) -or (Test-Path $wpcapDll)
}

function Test-Python {
    try {
        $version = & python --version 2>&1
        return $version -match "Python 3\."
    } catch {
        return $false
    }
}

function Install-NpcapIfMissing {
    if (Test-Npcap) { return $true }

    Write-Host ""
    Write-Host "===============================================" -ForegroundColor Red
    Write-Host "  NPCAP REQUIRED" -ForegroundColor Red
    Write-Host "===============================================" -ForegroundColor Red
    Write-Host ""
    Write-Host "  Npcap is required for raw socket access."
    Write-Host ""
    Write-Host "  IMPORTANT: During installation, check:" -ForegroundColor Yellow
    Write-Host "  [x] Install Npcap in WinPcap API-compatible Mode" -ForegroundColor Yellow
    Write-Host ""

    $choice = Read-Host "  Download and install Npcap now? [Y/n]"
    if ($choice -match "^[Nn]") {
        Write-Warn "Please install Npcap from https://npcap.com"
        return $false
    }

    Write-Info "Downloading Npcap $NpcapVersion..."
    try {
        Invoke-WebRequest -Uri $NpcapUrl -OutFile $NpcapInstaller -UseBasicParsing
        Write-Success "Downloaded"
    } catch {
        Write-Err "Download failed. Please install manually from https://npcap.com"
        Start-Process "https://npcap.com/#download"
        return $false
    }

    Write-Info "Launching Npcap installer..."
    Write-Host "  Check: [x] WinPcap API-compatible Mode" -ForegroundColor Yellow
    Start-Process -FilePath $NpcapInstaller -Wait | Out-Null
    Remove-Item $NpcapInstaller -Force -ErrorAction SilentlyContinue

    Start-Sleep -Seconds 2
    if (Test-Npcap) {
        Write-Success "Npcap installed!"
        return $true
    } else {
        Write-Err "Npcap installation failed or cancelled"
        return $false
    }
}

function Install-PythonIfMissing {
    if (Test-Python) { return $true }

    Write-Host ""
    Write-Host "===============================================" -ForegroundColor Red
    Write-Host "  PYTHON 3 REQUIRED" -ForegroundColor Red
    Write-Host "===============================================" -ForegroundColor Red
    Write-Host ""
    Write-Host "  GFW-knocker requires Python 3.x"
    Write-Host ""
    Write-Host "  Please install Python from:" -ForegroundColor Yellow
    Write-Host "  https://www.python.org/downloads/" -ForegroundColor Yellow
    Write-Host ""
    Write-Host "  IMPORTANT: Check 'Add Python to PATH' during install!" -ForegroundColor Yellow
    Write-Host ""

    $choice = Read-Host "  Open Python download page? [Y/n]"
    if ($choice -notmatch "^[Nn]") {
        Start-Process "https://www.python.org/downloads/"
    }

    Read-Host "  Press Enter after installing Python"

    if (Test-Python) {
        Write-Success "Python detected!"
        return $true
    } else {
        Write-Err "Python not found. Please restart PowerShell after installing."
        return $false
    }
}

function Install-PythonPackages {
    Write-Info "Installing Python packages (scapy, aioquic)..."
    try {
        & python -m pip install --quiet --upgrade pip 2>&1 | Out-Null
        & python -m pip install --quiet scapy aioquic 2>&1 | Out-Null
        Write-Success "Python packages installed"
        return $true
    } catch {
        Write-Err "Failed to install Python packages: $_"
        Write-Info "Try manually: pip install scapy aioquic"
        return $false
    }
}

#═══════════════════════════════════════════════════════════════════════
# Network Detection
#═══════════════════════════════════════════════════════════════════════

function Get-NetworkInfo {
    $adapter = Get-NetAdapter | Where-Object {
        $_.Status -eq "Up" -and
        $_.InterfaceDescription -notmatch "Virtual|VirtualBox|VMware|Hyper-V|Loopback"
    } | Select-Object -First 1

    if (-not $adapter) {
        $adapter = Get-NetAdapter | Where-Object { $_.Status -eq "Up" } | Select-Object -First 1
    }

    if (-not $adapter) {
        Write-Err "No active network adapter found"
        return $null
    }

    $ifIndex = $adapter.ifIndex
    $ipConfig = Get-NetIPAddress -InterfaceIndex $ifIndex -AddressFamily IPv4 |
                Where-Object { $_.PrefixOrigin -ne "WellKnown" } | Select-Object -First 1
    $gateway = Get-NetRoute -InterfaceIndex $ifIndex -DestinationPrefix "0.0.0.0/0" -ErrorAction SilentlyContinue |
               Select-Object -First 1

    if (-not $ipConfig) {
        Write-Err "No IPv4 address found on $($adapter.Name)"
        return $null
    }

    $gatewayIP = if ($gateway) { $gateway.NextHop } else { $null }
    $gatewayMAC = $null

    if ($gatewayIP) {
        $null = Test-Connection -ComputerName $gatewayIP -Count 1 -ErrorAction SilentlyContinue
        $arpEntry = Get-NetNeighbor -IPAddress $gatewayIP -ErrorAction SilentlyContinue | Select-Object -First 1
        if ($arpEntry -and $arpEntry.LinkLayerAddress) {
            $gatewayMAC = $arpEntry.LinkLayerAddress -replace "-", ":"
        }
    }

    return @{
        Name = $adapter.Name
        Guid = $adapter.InterfaceGuid
        IP = $ipConfig.IPAddress
        GatewayIP = $gatewayIP
        GatewayMAC = $gatewayMAC
    }
}

#═══════════════════════════════════════════════════════════════════════
# Backend Detection
#═══════════════════════════════════════════════════════════════════════

function Get-InstalledBackend {
    if (Test-Path $SettingsFile) {
        $content = Get-Content $SettingsFile -ErrorAction SilentlyContinue
        foreach ($line in $content) {
            if ($line -match '^BACKEND="?(\w+)"?') {
                return $Matches[1]
            }
        }
    }
    if (Test-Path $PaqetExe) { return "paqet" }
    if (Test-Path "$GfkDir\mainclient.py") { return "gfk" }
    return $null
}

function Save-Settings {
    param([string]$Backend, [string]$ServerAddr = "", [string]$SocksPort = "1080")

    $settings = @"
BACKEND="$Backend"
SERVER_ADDR="$ServerAddr"
SOCKS_PORT="$SocksPort"
"@
    if (-not (Test-Path $InstallDir)) {
        New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
    }
    [System.IO.File]::WriteAllText($SettingsFile, $settings)
}

#═══════════════════════════════════════════════════════════════════════
# Paqet Functions
#═══════════════════════════════════════════════════════════════════════

function Install-Paqet {
    Write-Host ""
    Write-Host "  Installing PAQET" -ForegroundColor Green
    Write-Host "  ────────────────" -ForegroundColor Green
    Write-Host "  Paqet is an all-in-one proxy solution with built-in SOCKS5."
    Write-Host "  It uses KCP protocol over raw sockets to bypass firewalls."
    Write-Host ""
    Write-Host "  What will be installed:" -ForegroundColor Yellow
    Write-Host "    1. Npcap (for raw socket access)"
    Write-Host "    2. Paqet binary"
    Write-Host ""
    Write-Host "  After setup, configure with your server's IP:port and key."
    Write-Host "  Your proxy will be: 127.0.0.1:1080 (SOCKS5)"
    Write-Host ""

    if (-not (Install-NpcapIfMissing)) {
        Write-Err "Cannot continue without Npcap"
        return $false
    }

    if (-not (Test-Path $InstallDir)) {
        New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null
    }

    if (Test-Path $PaqetExe) {
        Write-Info "paqet already installed"
        return $true
    }

    $zipUrl = "https://github.com/hanselime/paqet/releases/download/$PaqetVersion/paqet-windows-amd64-$PaqetVersion.zip"
    $zipFile = "$env:TEMP\paqet.zip"

    Write-Info "Downloading paqet $PaqetVersion..."
    try {
        Invoke-WebRequest -Uri $zipUrl -OutFile $zipFile
    } catch {
        Write-Err "Download failed: $_"
        return $false
    }

    Write-Info "Extracting..."
    Expand-Archive -Path $zipFile -DestinationPath $InstallDir -Force
    Remove-Item $zipFile -Force

    Write-Success "paqet installed to $InstallDir"
    Save-Settings -Backend "paqet"
    return $true
}

function New-PaqetConfig {
    param(
        [Parameter(Mandatory)][string]$Server,
        [Parameter(Mandatory)][string]$SecretKey,
        [string]$TcpLocalFlag = "PA",
        [string]$TcpRemoteFlag = "PA"
    )

    # Validate TCP flags (uppercase letters F,S,R,P,A,U,E,C, optionally comma-separated)
    if ($TcpLocalFlag -cnotmatch '^[FSRPAUEC]+(,[FSRPAUEC]+)*$') {
        Write-Warn "Invalid TCP local flag. Using default: PA"
        $TcpLocalFlag = "PA"
    }
    if ($TcpRemoteFlag -cnotmatch '^[FSRPAUEC]+(,[FSRPAUEC]+)*$') {
        Write-Warn "Invalid TCP remote flag. Using default: PA"
        $TcpRemoteFlag = "PA"
    }

    Write-Info "Detecting network..."
    $net = Get-NetworkInfo
    if (-not $net) { return $false }

    Write-Info "  Adapter:     $($net.Name)"
    Write-Info "  Local IP:    $($net.IP)"
    Write-Info "  Gateway MAC: $($net.GatewayMAC)"

    if (-not $net.GatewayMAC) {
        $net.GatewayMAC = Read-Host "  Enter gateway MAC (aa:bb:cc:dd:ee:ff)"
    }

    # Convert comma-separated flags to YAML array format: PA,A -> ["PA", "A"]
    $localFlagArray = ($TcpLocalFlag -split ',') | ForEach-Object { "`"$_`"" }
    $remoteFlagArray = ($TcpRemoteFlag -split ',') | ForEach-Object { "`"$_`"" }
    $localFlagYaml = "[" + ($localFlagArray -join ", ") + "]"
    $remoteFlagYaml = "[" + ($remoteFlagArray -join ", ") + "]"

    $guidEscaped = "\\Device\\NPF_$($net.Guid)"
    $config = @"
role: "client"

log:
  level: "info"

socks5:
  - listen: "127.0.0.1:1080"

network:
  interface: "$($net.Name)"
  guid: "$guidEscaped"
  ipv4:
    addr: "$($net.IP):0"
    router_mac: "$($net.GatewayMAC)"
  tcp:
    local_flag: $localFlagYaml
    remote_flag: $remoteFlagYaml

server:
  addr: "$Server"

transport:
  protocol: "kcp"
  kcp:
    mode: "fast"
    key: "$SecretKey"
"@

    # Ensure install directory exists
    if (-not (Test-Path $InstallDir)) {
        Write-Err "Paqet is not installed. Please install paqet first (option 1)."
        return $false
    }

    [System.IO.File]::WriteAllText($ConfigFile, $config)
    Save-Settings -Backend "paqet" -ServerAddr $Server
    Write-Success "Configuration saved"
    return $true
}

function Start-Paqet {
    if (-not (Test-Npcap)) {
        if (-not (Install-NpcapIfMissing)) { return }
    }

    if (-not (Test-Path $PaqetExe)) {
        Write-Err "paqet not installed"
        return
    }

    if (-not (Test-Path $ConfigFile)) {
        Write-Err "Config not found. Configure first."
        return
    }

    Write-Host ""
    Write-Host "  Starting PAQET" -ForegroundColor Green
    Write-Host "  ──────────────"
    Write-Host "  Paqet will connect to your server using KCP over raw sockets."
    Write-Host ""
    Write-Host "  Your SOCKS5 proxy will be: 127.0.0.1:1080"
    Write-Host "  Configure your browser to use this proxy."
    Write-Host ""
    Write-Info "Starting paqet..."
    Write-Info "SOCKS5 proxy: 127.0.0.1:1080"
    Write-Info "Press Ctrl+C to stop"
    Write-Host ""

    & $PaqetExe run -c $ConfigFile
}

#═══════════════════════════════════════════════════════════════════════
# GFW-knocker Functions
#═══════════════════════════════════════════════════════════════════════

function Install-Gfk {
    Write-Host ""
    Write-Host "  Installing GFW-KNOCKER" -ForegroundColor Yellow
    Write-Host "  ──────────────────────" -ForegroundColor Yellow
    Write-Host "  GFK is an advanced anti-censorship tool designed for heavy DPI."
    Write-Host "  It uses 'violated TCP' packets + QUIC tunneling to evade detection."
    Write-Host ""
    Write-Host "  What will be installed:" -ForegroundColor Yellow
    Write-Host "    1. Npcap (for raw socket access)"
    Write-Host "    2. Python 3.10+ (for QUIC protocol)"
    Write-Host "    3. Python packages: scapy, aioquic"
    Write-Host "    4. GFK client scripts"
    Write-Host ""
    Write-Host "  IMPORTANT: Your server must have Xray running on port 443." -ForegroundColor Cyan
    Write-Host "  GFK is just a tunnel - Xray provides the actual SOCKS5 proxy."
    Write-Host ""
    Write-Host "  After setup, your proxy will be: 127.0.0.1:14000 (SOCKS5)"
    Write-Host ""

    # Check prerequisites
    if (-not (Install-NpcapIfMissing)) { return $false }
    if (-not (Install-PythonIfMissing)) { return $false }
    if (-not (Install-PythonPackages)) { return $false }

    # Create directories
    if (-not (Test-Path $GfkDir)) {
        New-Item -ItemType Directory -Path $GfkDir -Force | Out-Null
    }

    # Copy bundled GFK scripts or download from Gitea
    Write-Info "Setting up GFW-knocker scripts..."
    $GfkGitHubBase = "https://git.samnet.dev/SamNet-dev/paqctl/raw/branch/main/gfk/client"
    foreach ($file in $GfkFiles) {
        $dest = "$GfkDir\$file"
        $src = if ($GfkLocalDir) { "$GfkLocalDir\$file" } else { $null }

        if ($src -and (Test-Path $src)) {
            # Copy from local bundled files (faster)
            Copy-Item -Path $src -Destination $dest -Force
            Write-Info "  Copied $file"
        } else {
            # Download from Gitea (for one-liner installation)
            Write-Info "  Downloading $file..."
            try {
                Invoke-WebRequest -Uri "$GfkGitHubBase/$file" -OutFile $dest -UseBasicParsing
                Write-Info "  Downloaded $file"
            } catch {
                Write-Err "Failed to download $file from Gitea"
                return $false
            }
        }
    }

    Write-Success "GFW-knocker installed to $GfkDir"
    Save-Settings -Backend "gfk"
    return $true
}

function New-GfkConfig {
    param(
        [Parameter(Mandatory)][string]$ServerIP,
        [Parameter(Mandatory)][string]$AuthCode,
        [string]$SocksPort = "1080",
        [string]$TcpFlags = "AP"
    )

    # Validate inputs (security: prevent config injection)
    if (-not (Test-ValidIP $ServerIP)) {
        Write-Err "Invalid server IP format"
        return $false
    }
    if (-not (Test-SafeString $AuthCode)) {
        Write-Err "Invalid auth code format"
        return $false
    }
    # Validate TCP flags (uppercase letters only: F,S,R,P,A,U,E,C)
    if ($TcpFlags -cnotmatch '^[FSRPAUEC]+$') {
        Write-Warn "Invalid TCP flags. Using default: AP"
        $TcpFlags = "AP"
    }

    Write-Info "Detecting network..."
    $net = Get-NetworkInfo
    if (-not $net) { return $false }

    Write-Info "  Adapter:  $($net.Name)"
    Write-Info "  Local IP: $($net.IP)"
    Write-Info "  Gateway:  $($net.GatewayMAC)"

    if (-not $net.GatewayMAC) {
        $net.GatewayMAC = Read-Host "  Enter gateway MAC (aa:bb:cc:dd:ee:ff)"
    }

    # Validate detected network values
    if (-not (Test-ValidIP $net.IP)) {
        Write-Err "Invalid local IP detected"
        return $false
    }
    if ($net.GatewayMAC -and -not (Test-ValidMAC $net.GatewayMAC)) {
        Write-Err "Invalid gateway MAC format"
        return $false
    }

    # Create parameters.py for GFK (matching expected variable names)
    $params = @"
# GFW-knocker client configuration (auto-generated)
from scapy.all import conf

# Network interface for scapy (Windows Npcap)
conf.iface = r"\Device\NPF_$($net.Guid)"
my_ip = "$($net.IP)"
gateway_mac = "$($net.GatewayMAC)"

# Server settings
vps_ip = "$ServerIP"
xray_server_ip = "127.0.0.1"

# Port mappings (local_port: remote_port)
tcp_port_mapping = {14000: 443}
udp_port_mapping = {}

# VIO (raw socket) ports
vio_tcp_server_port = 45000
vio_tcp_client_port = 40000
vio_udp_server_port = 35000
vio_udp_client_port = 30000

# QUIC tunnel ports
quic_server_port = 25000
quic_client_port = 20000
quic_local_ip = "127.0.0.1"

# QUIC settings
quic_verify_cert = False
quic_idle_timeout = 86400
udp_timeout = 300
quic_mtu = 1420
quic_max_data = 1073741824
quic_max_stream_data = 1073741824
quic_auth_code = "$AuthCode"
quic_certificate = "cert.pem"
quic_private_key = "key.pem"

# TCP flags for violated packets (default: AP = ACK+PSH)
tcp_flags = "$TcpFlags"

# SOCKS proxy
socks_port = $SocksPort
"@

    # Ensure GFK directory exists
    if (-not (Test-Path $GfkDir)) {
        Write-Err "GFK is not installed. Please install GFK first (option 2)."
        return $false
    }

    [System.IO.File]::WriteAllText("$GfkDir\parameters.py", $params)
    Save-Settings -Backend "gfk" -ServerAddr $ServerIP -SocksPort $SocksPort
    Write-Success "GFK configuration saved"
    return $true
}

function Start-Gfk {
    if (-not (Test-Npcap)) {
        if (-not (Install-NpcapIfMissing)) { return }
    }

    if (-not (Test-Python)) {
        Write-Err "Python not found"
        return
    }

    if (-not (Test-Path "$GfkDir\mainclient.py")) {
        Write-Err "GFK not installed"
        return
    }

    if (-not (Test-Path "$GfkDir\parameters.py")) {
        Write-Err "GFK not configured"
        return
    }

    Write-Host ""
    Write-Host "  Starting GFW-KNOCKER" -ForegroundColor Yellow
    Write-Host "  ────────────────────"
    Write-Host "  This will start:"
    Write-Host "    1. VIO client (raw socket handler)"
    Write-Host "    2. QUIC client (tunnel to server)"
    Write-Host ""
    Write-Host "  Your SOCKS5 proxy will be: 127.0.0.1:14000"
    Write-Host "  Configure your browser to use this proxy."
    Write-Host ""
    Write-Info "Starting GFW-knocker client..."
    Write-Info "This will start the raw socket client + Python SOCKS5 proxy"
    Write-Info "Press Ctrl+C to stop"
    Write-Host ""

    # Start GFK client
    Push-Location $GfkDir
    try {
        & python mainclient.py
    } finally {
        Pop-Location
    }
}

function Stop-GfkClient {
    # Get-Process doesn't have CommandLine property - use CIM instead
    $procs = Get-CimInstance Win32_Process -Filter "Name LIKE 'python%'" -ErrorAction SilentlyContinue |
             Where-Object { $_.CommandLine -match "mainclient|gfk" }
    if ($procs) {
        $procs | ForEach-Object {
            Stop-Process -Id $_.ProcessId -Force -ErrorAction SilentlyContinue
        }
        Write-Success "GFK client stopped"
    } else {
        Write-Info "GFK client not running"
    }
}

#═══════════════════════════════════════════════════════════════════════
# Common Functions
#═══════════════════════════════════════════════════════════════════════

function Stop-Client {
    # Stop paqet
    $paqetProc = Get-Process -Name "paqet_windows_amd64" -ErrorAction SilentlyContinue
    if ($paqetProc) {
        Stop-Process -Name "paqet_windows_amd64" -Force
        Write-Success "paqet stopped"
    }

    # Stop GFK
    Stop-GfkClient
}

function Get-ClientStatus {
    Write-Host "`n=== Client Status ===" -ForegroundColor Cyan

    $backend = Get-InstalledBackend
    Write-Host "Backend: $(if ($backend) { $backend } else { 'Not installed' })"

    # Npcap
    if (Test-Npcap) {
        Write-Success "Npcap: Installed"
    } else {
        Write-Err "Npcap: NOT installed"
    }

    # Python (for GFK)
    if ($backend -eq "gfk" -or -not $backend) {
        if (Test-Python) {
            Write-Success "Python: Installed"
        } else {
            Write-Warn "Python: Not found (needed for GFK)"
        }
    }

    # Paqet
    if (Test-Path $PaqetExe) {
        Write-Success "Paqet binary: Found"
    }

    # GFK
    if (Test-Path "$GfkDir\mainclient.py") {
        Write-Success "GFK scripts: Found"
    }

    # Config
    if (Test-Path $ConfigFile) {
        Write-Success "Paqet config: Found"
    }
    if (Test-Path "$GfkDir\parameters.py") {
        Write-Success "GFK config: Found"
    }

    # Running processes
    $paqetRunning = Get-Process -Name "paqet_windows_amd64" -ErrorAction SilentlyContinue
    if ($paqetRunning) {
        Write-Success "Paqet: RUNNING (PID: $($paqetRunning.Id))"
        Write-Info "  SOCKS5 proxy: 127.0.0.1:1080"
    }

    Write-Host ""
}

#═══════════════════════════════════════════════════════════════════════
# Update Function
#═══════════════════════════════════════════════════════════════════════

function Get-InstalledPaqetVersion {
    # Check settings file first for tracked version
    if (Test-Path $SettingsFile) {
        $content = Get-Content $SettingsFile -ErrorAction SilentlyContinue
        foreach ($line in $content) {
            if ($line -match '^PAQET_VERSION="?([^"]+)"?') {
                return $Matches[1]
            }
        }
    }
    # Fall back to pinned version if paqet is installed
    if (Test-Path $PaqetExe) {
        return $PaqetVersion
    }
    return $null
}

function Save-PaqetVersion {
    param([string]$Version)

    if (-not (Test-Path $SettingsFile)) {
        return
    }

    $content = Get-Content $SettingsFile -Raw -ErrorAction SilentlyContinue
    if ($content -match 'PAQET_VERSION=') {
        # Update existing
        $content = $content -replace 'PAQET_VERSION="[^"]*"', "PAQET_VERSION=`"$Version`""
    } else {
        # Add new line
        $content = $content.TrimEnd() + "`nPAQET_VERSION=`"$Version`""
    }
    [System.IO.File]::WriteAllText($SettingsFile, $content)
}

function Update-Paqet {
    Write-Host ""
    Write-Host "  CHECKING FOR UPDATES" -ForegroundColor Cyan
    Write-Host "  ────────────────────" -ForegroundColor Cyan
    Write-Host ""

    # Check if paqet is installed
    if (-not (Test-Path $PaqetExe)) {
        Write-Warn "Paqet is not installed. Use option 1 to install first."
        return $false
    }

    # Get installed version
    $installedVersion = Get-InstalledPaqetVersion
    if (-not $installedVersion) {
        $installedVersion = $PaqetVersion
    }

    # Query GitHub API for latest release
    Write-Info "Querying GitHub for latest release..."
    try {
        $apiUrl = "https://api.github.com/repos/hanselime/paqet/releases/latest"
        $response = Invoke-RestMethod -Uri $apiUrl -TimeoutSec 30
        $latestVersion = $response.tag_name
    } catch {
        Write-Err "Failed to check for updates: $_"
        return $false
    }

    # Show version info
    Write-Host ""
    Write-Host "  Installed version:  $installedVersion" -ForegroundColor White
    Write-Host "  Latest version:     $latestVersion" -ForegroundColor White
    Write-Host ""

    # Compare versions
    if ($installedVersion -eq $latestVersion) {
        Write-Success "You are already on the latest version!"
        return $true
    }

    # Confirm update
    Write-Host "  A new version is available!" -ForegroundColor Yellow
    $confirm = Read-Host "  Update to $latestVersion? [y/N]"
    if ($confirm -notmatch "^[Yy]") {
        Write-Info "Update cancelled"
        return $false
    }

    # Stop running paqet first
    $paqetProc = Get-Process -Name "paqet_windows_amd64" -ErrorAction SilentlyContinue
    if ($paqetProc) {
        Write-Info "Stopping paqet..."
        Stop-Process -Name "paqet_windows_amd64" -Force
        Start-Sleep -Seconds 2
    }

    # Download new version
    $zipUrl = "https://github.com/hanselime/paqet/releases/download/$latestVersion/paqet-windows-amd64-$latestVersion.zip"
    $zipFile = "$env:TEMP\paqet-update.zip"
    $extractDir = "$env:TEMP\paqet-update"

    Write-Info "Downloading paqet $latestVersion..."
    try {
        Invoke-WebRequest -Uri $zipUrl -OutFile $zipFile -TimeoutSec 120
    } catch {
        Write-Err "Download failed: $_"
        return $false
    }

    # Validate download
    if (-not (Test-Path $zipFile) -or (Get-Item $zipFile).Length -lt 1000) {
        Write-Err "Downloaded file is invalid or too small"
        return $false
    }

    # Extract
    Write-Info "Extracting..."
    try {
        if (Test-Path $extractDir) { Remove-Item $extractDir -Recurse -Force }
        Expand-Archive -Path $zipFile -DestinationPath $extractDir -Force
    } catch {
        Write-Err "Extraction failed: $_"
        Remove-Item $zipFile -Force -ErrorAction SilentlyContinue
        return $false
    }

    # Find the binary
    $newBinary = Get-ChildItem -Path $extractDir -Filter "paqet_windows_amd64.exe" -Recurse | Select-Object -First 1
    if (-not $newBinary) {
        Write-Err "Could not find paqet binary in archive"
        Remove-Item $zipFile -Force -ErrorAction SilentlyContinue
        Remove-Item $extractDir -Recurse -Force -ErrorAction SilentlyContinue
        return $false
    }

    # Backup old binary
    $backupPath = "$InstallDir\paqet_windows_amd64.exe.bak"
    try {
        Copy-Item $PaqetExe $backupPath -Force
        Write-Info "Backed up old binary"
    } catch {
        Write-Warn "Could not backup old binary: $_"
    }

    # Install new binary
    try {
        Copy-Item $newBinary.FullName $PaqetExe -Force
    } catch {
        Write-Err "Failed to install new binary: $_"
        # Try to restore backup
        if (Test-Path $backupPath) {
            Copy-Item $backupPath $PaqetExe -Force -ErrorAction SilentlyContinue
        }
        return $false
    }

    # Save version to settings
    Save-PaqetVersion -Version $latestVersion

    # Cleanup
    Remove-Item $zipFile -Force -ErrorAction SilentlyContinue
    Remove-Item $extractDir -Recurse -Force -ErrorAction SilentlyContinue

    Write-Host ""
    Write-Success "Updated to $latestVersion!"
    Write-Host ""
    Write-Info "Restart the client to use the new version"
    Write-Host ""

    return $true
}

#═══════════════════════════════════════════════════════════════════════
# Interactive Menu
#═══════════════════════════════════════════════════════════════════════

function Show-Menu {
    param([string]$InitBackend = "")

    # Use passed backend parameter, or detect if not specified
    $backend = if ($InitBackend) { $InitBackend } else { Get-InstalledBackend }

    while ($true) {
        Write-Host ""
        Write-Host "===============================================" -ForegroundColor Cyan
        Write-Host "  PAQET/GFK CLIENT MANAGER" -ForegroundColor Cyan
        Write-Host "===============================================" -ForegroundColor Cyan
        Write-Host ""
        if ($backend) {
            Write-Host "  Active backend: " -NoNewline
            Write-Host "$backend" -ForegroundColor Green
            if ($backend -eq "paqet") {
                Write-Host "  Proxy: 127.0.0.1:1080 (SOCKS5)" -ForegroundColor DarkGray
            } else {
                Write-Host "  Proxy: 127.0.0.1:14000 (SOCKS5 via tunnel)" -ForegroundColor DarkGray
            }
        } else {
            Write-Host "  No backend installed yet" -ForegroundColor Yellow
        }
        Write-Host ""
        Write-Host "  1. Install paqet        (simple, all-in-one SOCKS5)"
        Write-Host "  2. Install GFW-knocker  (advanced, for heavy DPI)"
        Write-Host "  3. Configure connection"
        Write-Host "  4. Start client"
        Write-Host "  5. Stop client"
        Write-Host "  6. Show status"
        Write-Host "  7. Update paqet"
        Write-Host "  8. About (how it works)"
        Write-Host "  0. Exit"
        Write-Host ""

        $choice = Read-Host "  Select option"

        switch ($choice) {
            "1" {
                if (Install-Paqet) { $backend = "paqet" }
            }
            "2" {
                if (Install-Gfk) { $backend = "gfk" }
            }
            "3" {
                if (-not $backend) {
                    Write-Warn "Install a backend first (option 1 or 2)"
                    continue
                }

                if ($backend -eq "paqet") {
                    Write-Host ""
                    Write-Host "  PAQET CONFIGURATION" -ForegroundColor Green
                    Write-Host "  Get these values from your server admin or 'paqctl info' on server"
                    Write-Host ""
                    $server = Read-Host "  Server address (e.g., 1.2.3.4:8443)"
                    $key = Read-Host "  Encryption key (16+ chars)"

                    # Advanced options (hidden by default - just press Enter)
                    Write-Host ""
                    Write-Host "  Advanced options (press Enter for defaults - recommended):" -ForegroundColor DarkGray
                    Write-Host "    TCP flags must match your server config. Only change if server admin says so." -ForegroundColor DarkGray
                    Write-Host "    Valid flags: S A P R F U E C  |  Multiple: PA,A" -ForegroundColor DarkGray
                    $tcpLocal = Read-Host "  TCP local flag [PA]"
                    $tcpRemote = Read-Host "  TCP remote flag [PA]"
                    if (-not $tcpLocal) { $tcpLocal = "PA" }
                    if (-not $tcpRemote) { $tcpRemote = "PA" }

                    if ($server -and $key) {
                        if (New-PaqetConfig -Server $server -SecretKey $key -TcpLocalFlag $tcpLocal -TcpRemoteFlag $tcpRemote) {
                            Write-Host ""
                            Write-Host "  Your SOCKS5 proxy: 127.0.0.1:1080" -ForegroundColor Green
                        }
                    }
                } else {
                    Write-Host ""
                    Write-Host "  GFK CONFIGURATION" -ForegroundColor Yellow
                    Write-Host "  Get these values from your server admin or 'paqctl info' on server"
                    Write-Host ""
                    $server = Read-Host "  Server IP (e.g., 1.2.3.4)"
                    $auth = Read-Host "  Auth code (from server setup)"

                    # Advanced options (hidden by default - just press Enter)
                    Write-Host ""
                    Write-Host "  Advanced options (press Enter for defaults - recommended):" -ForegroundColor DarkGray
                    Write-Host "    TCP flags must match your server config. Only change if server admin says so." -ForegroundColor DarkGray
                    Write-Host "    Valid flags: S A P R F U E C" -ForegroundColor DarkGray
                    $tcpFlags = Read-Host "  TCP flags [AP]"
                    if (-not $tcpFlags) { $tcpFlags = "AP" }

                    if ($server -and $auth) {
                        if (New-GfkConfig -ServerIP $server -AuthCode $auth -SocksPort "14000" -TcpFlags $tcpFlags) {
                            Write-Host ""
                            Write-Host "  Your SOCKS5 proxy: 127.0.0.1:14000" -ForegroundColor Green
                        }
                    }
                }
            }
            "4" {
                if (-not $backend) {
                    Write-Warn "Install a backend first"
                    continue
                }
                if ($backend -eq "paqet") {
                    Start-Paqet
                } else {
                    Start-Gfk
                }
            }
            "5" { Stop-Client }
            "6" { Get-ClientStatus }
            "7" { Update-Paqet }
            "8" { Show-About }
            "0" { return }
            default { Write-Warn "Invalid option" }
        }
    }
}

function Show-About {
    Write-Host ""
    Write-Host "===============================================" -ForegroundColor Cyan
    Write-Host "  HOW IT WORKS" -ForegroundColor Cyan
    Write-Host "===============================================" -ForegroundColor Cyan
    Write-Host ""
    Write-Host "  This tool helps bypass firewall restrictions"
    Write-Host "  by disguising your traffic. You have TWO options:"
    Write-Host ""
    Write-Host "  --- PAQET - Simple and Fast ---" -ForegroundColor Green
    Write-Host "  How: Uses KCP protocol over raw sockets"
    Write-Host "  Proxy: 127.0.0.1:1080 (SOCKS5)"
    Write-Host "  Best for: Most situations, easy setup"
    Write-Host ""
    Write-Host "  --- GFW-KNOCKER - Advanced Anti-DPI ---" -ForegroundColor Yellow
    Write-Host "  How: Violated TCP packets + QUIC tunnel"
    Write-Host "  Proxy: 127.0.0.1:14000 (SOCKS5 via Xray)"
    Write-Host "  Best for: When paqet is blocked, heavy censorship"
    Write-Host ""
    Write-Host "  --- CAN I RUN BOTH? ---" -ForegroundColor Magenta
    Write-Host "  YES! They use different ports:"
    Write-Host "    - Paqet: 127.0.0.1:1080"
    Write-Host "    - GFK:   127.0.0.1:14000"
    Write-Host "  Install both as backup - if one gets blocked, use the other!"
    Write-Host ""
    Write-Host "  Press Enter to continue..." -ForegroundColor DarkGray
    Read-Host | Out-Null
}

#═══════════════════════════════════════════════════════════════════════
# Main Entry Point
#═══════════════════════════════════════════════════════════════════════

if (-not (Test-Admin)) {
    Write-Err "Administrator privileges required"
    Write-Info "Right-click PowerShell -> Run as Administrator"
    exit 1
}

# Auto-detect backend if not specified
if (-not $Backend) {
    $Backend = Get-InstalledBackend
}

switch ($Action.ToLower()) {
    "install" {
        if ($Backend -eq "gfk") {
            Install-Gfk
        } else {
            Install-Paqet
        }
    }
    "config" {
        if ($Backend -eq "gfk") {
            if (-not $ServerAddr -or -not $Key) {
                Write-Err "Usage: -Action config -ServerAddr [ip] -Key [authcode]"
                exit 1
            }
            New-GfkConfig -ServerIP $ServerAddr -AuthCode $Key
        } else {
            if (-not $ServerAddr -or -not $Key) {
                Write-Err "Usage: -Action config -ServerAddr [ip:port] -Key [key]"
                exit 1
            }
            New-PaqetConfig -Server $ServerAddr -SecretKey $Key
        }
    }
    "run" {
        if ($ServerAddr -and $Key) {
            if ($Backend -eq "gfk") {
                Install-Gfk
                New-GfkConfig -ServerIP $ServerAddr -AuthCode $Key
                Start-Gfk
            } else {
                Install-Paqet
                New-PaqetConfig -Server $ServerAddr -SecretKey $Key
                Start-Paqet
            }
        } else {
            if ($Backend -eq "gfk") {
                Start-Gfk
            } else {
                Start-Paqet
            }
        }
    }
    "start" {
        if ($Backend -eq "gfk") { Start-Gfk } else { Start-Paqet }
    }
    "stop" { Stop-Client }
    "status" { Get-ClientStatus }
    "menu" { Show-Menu -InitBackend $Backend }
    default { Show-Menu -InitBackend $Backend }
}
