# PowerShell: "iwr -useb https://raw.githubusercontent.com/sky22333/shell/main/dev/cf-setup.ps1 | iex"
# Path: "C:\ProgramData\cloudflared\"

$ProgressPreference = 'SilentlyContinue'
$ErrorActionPreference = 'Stop'

function Write-ColorMessage {
    param (
        [Parameter(Mandatory=$true)]
        [string]$Message,
        [ValidateSet('Black','DarkBlue','DarkGreen','DarkCyan','DarkRed','DarkMagenta','DarkYellow','Gray','DarkGray','Blue','Green','Cyan','Red','Magenta','Yellow','White')]
        [string]$Color = 'White'
    )
    
    try {
        $originalColor = $null
        if ($Host.UI -and $Host.UI.RawUI -and $Host.UI.RawUI.ForegroundColor) {
            $originalColor = $Host.UI.RawUI.ForegroundColor
            $Host.UI.RawUI.ForegroundColor = $Color
        }
        
        Write-Host $Message
        
        if ($originalColor -ne $null) {
            $Host.UI.RawUI.ForegroundColor = $originalColor
        }
    } catch {

        try {
            Write-Host $Message -ForegroundColor $Color
        } catch {
            Write-Host $Message
        }
    }
}

function Download-File {
    param (
        [string]$Url,
        [string]$OutputPath
    )
    
    try {

        if ($PSVersionTable.PSVersion.Major -ge 3) {

            $webClient = New-Object System.Net.WebClient
            $webClient.Headers.Add("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36")
            $webClient.DownloadFile($Url, $OutputPath)
            $webClient.Dispose()
        } else {

            $webClient = New-Object System.Net.WebClient
            $webClient.DownloadFile($Url, $OutputPath)
            $webClient.Dispose()
        }
        return $true
    } catch {
        Write-ColorMessage "Download failed: $($_.Exception.Message)" Red
        return $false
    }
}

function Test-AdminRights {
    try {
        $currentUser = [Security.Principal.WindowsIdentity]::GetCurrent()
        $principal = New-Object Security.Principal.WindowsPrincipal($currentUser)
        return $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)
    } catch {
        return $false
    }
}

Write-Host "====== CloudFlared Tunnel Setup Tool ======" -ForegroundColor Cyan
Write-Host "Initializing..." -ForegroundColor Yellow

$cloudflaredUrl = "https://github.com/cloudflare/cloudflared/releases/download/2025.8.0/cloudflared-windows-amd64.exe"
$installDir = "$env:ProgramData\cloudflared"
$cloudflaredBin = Join-Path $installDir "cloudflared.exe"
$logPath = Join-Path $installDir "cloudflared.log"
$serviceName = "CloudflaredTunnel"

$psVersion = $PSVersionTable.PSVersion.Major
Write-Host "Detected PowerShell version: $psVersion" -ForegroundColor Green

try {
    if (-not (Test-Path $installDir)) {
        New-Item -ItemType Directory -Path $installDir -Force | Out-Null
        Write-ColorMessage "Created installation directory: $installDir" Green
    }
} catch {
    Write-ColorMessage "Cannot create installation directory, may need administrator privileges" Red
    Write-ColorMessage "Error: $($_.Exception.Message)" Red
    exit 1
}

Write-ColorMessage "`nChecking cloudflared..." Yellow
if (Test-Path $cloudflaredBin) {
    Write-ColorMessage "cloudflared.exe already exists: $cloudflaredBin" Green
    
    try {
        $fileInfo = Get-Item $cloudflaredBin
        $fileSize = [math]::Round($fileInfo.Length / 1MB, 2)
        Write-ColorMessage "File size: ${fileSize} MB" Cyan
    } catch {
    }
} else {
    Write-ColorMessage "Starting download of cloudflared..." Cyan
    Write-ColorMessage "Download URL: $cloudflaredUrl" Gray
    Write-ColorMessage "Save location: $cloudflaredBin" Gray
    
    $downloadSuccess = Download-File -Url $cloudflaredUrl -OutputPath $cloudflaredBin
    
    if ($downloadSuccess) {
        Write-ColorMessage "Download complete!" Green
        try {
            $fileInfo = Get-Item $cloudflaredBin
            $fileSize = [math]::Round($fileInfo.Length / 1MB, 2)
            Write-ColorMessage "File size: ${fileSize} MB" Cyan
        } catch {
        }
    } else {
        Write-ColorMessage "Download failed, please check your network connection or download manually" Red
        Write-ColorMessage "Manual download URL: $cloudflaredUrl" Yellow
        exit 1
    }
}

Write-ColorMessage "`nChecking existing services..." Yellow
try {
    $serviceExists = Get-Service -Name $serviceName -ErrorAction SilentlyContinue
    if ($serviceExists) {
        Write-ColorMessage "Detected existing cloudflared service: $serviceName" Yellow
        Write-ColorMessage "Service status: $($serviceExists.Status)" Cyan
        
        do {
            $uninstall = Read-Host "Do you want to uninstall the old service? (y/n)"
        } while ($uninstall -notin @('y','Y','n','N','yes','no'))
        
        if ($uninstall -in @('y','Y','yes')) {
            Write-ColorMessage "Uninstalling old service..." Cyan
            try {
                Stop-Service -Name $serviceName -Force -ErrorAction SilentlyContinue
                Start-Sleep -Seconds 2
                
                $scResult = & "$env:SystemRoot\System32\sc.exe" delete $serviceName
                
                if (Test-Path $logPath) {
                    Remove-Item -Path $logPath -Force -ErrorAction SilentlyContinue
                }
                
                Write-ColorMessage "Service uninstallation complete" Green
            } catch {
                Write-ColorMessage "Error uninstalling service: $($_.Exception.Message)" Red
            }
        } else {
            Write-ColorMessage "Keeping existing service, only updating run address" Yellow
        }
    }
} catch {
    Write-ColorMessage "Error checking service: $($_.Exception.Message)" Red
}

Write-ColorMessage "`nPlease select run mode:" Yellow
Write-Host "1) Temporary run (foreground with trycloudflare domain display)"
Write-Host "2) Background run (register as system service)"

do {
    $mode = Read-Host "Please enter 1 or 2 ?"
} while ($mode -notin @('1','2'))

do {
    $localAddr = Read-Host "Please enter local service address (e.g.: 127.0.0.1:8080)"
} while ([string]::IsNullOrWhiteSpace($localAddr))

if ($mode -eq "1") {
    Write-ColorMessage "`nRunning cloudflared in temporary mode..." Cyan
    Write-ColorMessage "Starting cloudflared process..." Yellow
    Write-ColorMessage "Local service address: $localAddr" Green
    
    try {
        Write-ColorMessage "Running cloudflared directly with output to console..." Yellow
        Write-ColorMessage "Press Ctrl+C to stop the tunnel" Yellow
        
        & $cloudflaredBin tunnel --url $localAddr
        
    } catch {
        Write-ColorMessage "Error starting process: $($_.Exception.Message)" Red
    }
    
} elseif ($mode -eq "2") {

    Write-ColorMessage "`nRegistering as system service and running in background..." Cyan
    
    if (-not (Test-AdminRights)) {
        Write-ColorMessage "Warning: Administrator privileges may be required to create system services" Yellow
        Write-ColorMessage "If this fails, please run this script as administrator" Yellow
    }
    
    try {

        $serviceCommand = "`"$cloudflaredBin`" tunnel --url $localAddr --logfile `"$logPath`""
        
        $scResult = & "$env:SystemRoot\System32\sc.exe" create $serviceName binPath= $serviceCommand start= auto
        
        if ($LASTEXITCODE -eq 0) {
            Write-ColorMessage "Service created successfully" Green
        } else {
            Write-ColorMessage "Service creation may have failed, exit code: $LASTEXITCODE" Yellow
        }
        
        Start-Sleep -Seconds 2
        
        Write-ColorMessage "Starting service..." Yellow
        Start-Service -Name $serviceName -ErrorAction Stop
        Write-ColorMessage "Service started successfully, waiting for log output..." Green
        
        $domain = $null
        for ($i = 0; $i -lt 60; $i++) {
            Start-Sleep -Seconds 1
            
            if (Test-Path $logPath) {
                try {
                    $logContent = Get-Content $logPath -Raw -ErrorAction SilentlyContinue
                    if ($logContent -and $logContent -match 'https://[a-zA-Z0-9-]+\.trycloudflare\.com') {
                        $domain = $matches[0]
                        Write-ColorMessage "`n=== Service Running Successfully ===" Green
                        Write-ColorMessage "Public access URL: $domain" Green
                        Write-ColorMessage "Local service address: $localAddr" Cyan
                        Write-ColorMessage "Log file location: $logPath" Gray
                        break
                    }
                } catch {

                }
            }
            
            if ($i % 3 -eq 0) {
                Write-Host "." -NoNewline
            }
        }
        
        Write-Host ""
        
        if (-not $domain) {
            Write-ColorMessage "No access domain detected, please check the log manually: $logPath" Yellow
            Write-ColorMessage "The service may need more time to establish connection" Cyan
            
            try {
                $serviceStatus = Get-Service -Name $serviceName
                Write-ColorMessage "Service status: $($serviceStatus.Status)" Cyan
            } catch {
                Write-ColorMessage "Unable to get service status" Red
            }
        }
        
        Write-ColorMessage "`nService management commands:" Yellow
        Write-ColorMessage "Stop service: Stop-Service -Name $serviceName" Gray
        Write-ColorMessage "Start service: Start-Service -Name $serviceName" Gray
        Write-ColorMessage "Delete service: sc.exe delete $serviceName" Gray
        
    } catch {
        Write-ColorMessage "Failed to create or start service" Red
        Write-ColorMessage "Error: $($_.Exception.Message)" Red
        Write-ColorMessage "Please make sure you have administrator privileges" Yellow
        
        try {
            & "$env:SystemRoot\System32\sc.exe" delete $serviceName 2>$null
        } catch {
        }
    }
    
} else {
    Write-ColorMessage "Invalid option, please enter 1 or 2" Red
    exit 1
}

Write-ColorMessage "`nScript execution complete" Green
