[CmdletBinding()]
param(
    [Parameter(Mandatory=$true, HelpMessage="Name of the application to release")]
    [string]$AppName,
    
    [Parameter(HelpMessage="Remote service name")]
    [string]$ServiceName,
    
    [Parameter(HelpMessage="Remote extraction path")]
    [string]$RemoteExtractPath,
    
    [Parameter(HelpMessage="Build configuration (Debug/Release)")]
    [string]$PublishConfig = 'Release',
    
    [Parameter(HelpMessage="Target runtime")]
    [string]$Runtime = 'win-x64',
    
    [Parameter(HelpMessage="Self-contained deployment")]
    [bool]$SelfContained = $true,
    
    [Parameter(HelpMessage="Target .NET framework version")]
    [string]$TargetFramework = "net8.0",
    
    [Parameter(HelpMessage="Remote machine name or IP")]
    [string]$RemoteMachine = 'macmini2012',
    
    [Parameter(HelpMessage="Remote username")]
    [string]$Username = 'administrator',
    
    [Parameter(HelpMessage="Remote password")]
    [string]$Password = 'Woslaodaha0909',
    
    [Parameter(HelpMessage="Remote zip file path")]
    [string]$RemoteZipPath,
    
    [Parameter(HelpMessage="Local publish subfolder path")]
    [string]$PublishSubPath,
    
    [Parameter(HelpMessage="Project path to publish (optional)")]
    [string]$ProjectPath = "",
    
    [Parameter(HelpMessage="Enable beep notification on completion")]
    [switch]$EnableBeep = $true,
    
    [Parameter(HelpMessage="Beep frequency in Hz")]
    [int]$BeepFrequency = 1000,
    
    [Parameter(HelpMessage="Beep duration in milliseconds")]
    [int]$BeepDuration = 500,
    
    [Parameter(HelpMessage="nssmex port (default: 3002)")]
    [int]$NssmexPort = 3002
)

# Set default values if not provided
if (-not $ServiceName) {
    $ServiceName = $AppName
}

if (-not $RemoteExtractPath) {
    $RemoteExtractPath = "C:\services\$AppName"
}

if (-not $RemoteZipPath) {
    $RemoteZipPath = "C:\services\$AppName.zip"
}

if (-not $PublishSubPath) {
    $PublishSubPath = "bin/$PublishConfig/$TargetFramework/$Runtime/publish"
}

# Validate required parameters
if ([string]::IsNullOrWhiteSpace($AppName)) {
    Write-Error "AppName parameter is required"
    exit 1
}

# Messages
$msgTransferring = "Transferring $AppName zip file to remote machine..."
$msgTransferComplete = "Transfer complete. Now unzipping on remote machine..."
$msgStoppingService = "Stopping service on remote: $ServiceName"
$msgStartingService = "Starting service on remote: $ServiceName"
$msgRemoteUnzipFinished = "Remote unzip process finished."
$msgPublishNotFound = "Publish folder not found: {0}"
$msgServiceStopped = "Service stopped."
$msgServiceStarted = "Service started."
$msgServiceNotFound = "Service not found: {0}"
$msgCheckingExtractPath = "Checking if extract path exists: {0}"
$msgExtractPathNotExist = "Extract path does not exist. Creating..."
$msgZipFound = "Zip file found: {0}"
$msgZipNotFound = "Zip file not found: {0}"
$msgRemovingFiles = "Removing all files and folders in extract path..."
$msgExtractingZip = "Extracting zip file using .NET ZipFile..."
$msgExtractionComplete = "Extraction complete."
$msgPublishing = "Publishing $AppName..."
$msgCreatingZip = "Creating zip file for $AppName..."

# Convert password to secure string
$securePassword = $Password | ConvertTo-SecureString -AsPlainText -Force

# Determine zip file name
$zipFileName = "$AppName.zip"

try {
    Write-Host $msgPublishing
    
    # Build dotnet publish command
    $publishArgs = @("-c", $PublishConfig, "-r", $Runtime)
    if ($SelfContained) {
        $publishArgs += "--self-contained", "true"
    } else {
        $publishArgs += "--self-contained", "false"
    }
    
    # Add project path if provided
    if ($ProjectPath) {
        $publishArgs += $ProjectPath
    }
    
    # Publish the .NET app
    dotnet publish @publishArgs
    
    if ($LASTEXITCODE -ne 0) {
        Write-Error "dotnet publish failed with exit code $LASTEXITCODE"
        exit 1
    }

    # Zip the publish folder
    if ($ProjectPath) {
        $publishPath = Join-Path -Path $ProjectPath -ChildPath $PublishSubPath
    } else {
        $publishPath = Join-Path -Path (Get-Location) -ChildPath $PublishSubPath
    }
    $zipPath = Join-Path -Path (Get-Location) -ChildPath $zipFileName
    
    if (Test-Path $zipPath) { 
        Remove-Item $zipPath -Force 
    }
    
    if (Test-Path $publishPath) {
        Write-Host $msgCreatingZip
        Compress-Archive -Path "$publishPath\*" -DestinationPath $zipPath -Force

        # Prepare credentials for remote copy
        $cred = New-Object System.Management.Automation.PSCredential($Username, $securePassword)

        # Create a session and copy the zip file
        Write-Host $msgTransferring
        $session = New-PSSession -ComputerName $RemoteMachine -Credential $cred

        # Stop the service before unzipping
        Write-Host $msgStoppingService
        Invoke-Command -Session $session -ScriptBlock {
            param($serviceName, $msgServiceStopped, $msgServiceNotFound, $nssmexPort)
            $serviceStopped = $false
            
            # Try nssmex REST API first
            try {
                $stopUrl = "http://localhost:${nssmexPort}/api/stop/${serviceName}"
                Write-Host "  Trying nssmex API: $stopUrl" -ForegroundColor Gray
                $response = Invoke-RestMethod -Uri $stopUrl -Method Get
                Write-Host "  nssmex: $msgServiceStopped" -ForegroundColor Green
                $serviceStopped = $true
                Start-Sleep -Seconds 2
            } catch {
                Write-Host "  nssmex API not available or failed" -ForegroundColor Yellow
            }
            
            # Also try Windows Services
            if (Get-Service -Name $serviceName -ErrorAction SilentlyContinue) {
                try {
                    Stop-Service -Name $serviceName -Force -ErrorAction Stop
                    Write-Host "  Windows Service: $msgServiceStopped" -ForegroundColor Green
                    $serviceStopped = $true
                    
                    # Wait for service to fully stop
                    $timeout = 30
                    $elapsed = 0
                    while ((Get-Service -Name $serviceName).Status -ne 'Stopped' -and $elapsed -lt $timeout) {
                        Start-Sleep -Seconds 1
                        $elapsed++
                    }
                } catch {
                    Write-Host "  Windows Service stop failed: $_" -ForegroundColor Yellow
                }
            } else {
                Write-Host "  Windows Service not found" -ForegroundColor Yellow
            }
            
            if (-not $serviceStopped) {
                Write-Host "  WARNING: Could not stop service via any method" -ForegroundColor Yellow
            }
        } -ArgumentList $ServiceName, $msgServiceStopped, $msgServiceNotFound, $NssmexPort

        Copy-Item -Path $zipPath -Destination $RemoteZipPath -ToSession $session -Force
        Write-Host $msgTransferComplete
        Remove-PSSession $session

        $session = New-PSSession -ComputerName $RemoteMachine -Credential $cred
        Invoke-Command -Session $session -ScriptBlock {
            param($remoteZipPath, $remoteExtractPath, $msgCheckingExtractPath, $msgExtractPathNotExist, $msgZipFound, $msgZipNotFound, $msgRemovingFiles, $msgExtractingZip, $msgExtractionComplete)
            Write-Host ($msgCheckingExtractPath -f $remoteExtractPath)
            if (!(Test-Path $remoteExtractPath)) {
                Write-Host $msgExtractPathNotExist
                New-Item -Path $remoteExtractPath -ItemType Directory | Out-Null
            }
            if (Test-Path $remoteZipPath) {
                Write-Host ($msgZipFound -f $remoteZipPath)
                Write-Host $msgRemovingFiles
                Get-ChildItem -Path $remoteExtractPath | Remove-Item -Recurse -Force
                Write-Host $msgExtractingZip
                Add-Type -AssemblyName System.IO.Compression.FileSystem
                [System.IO.Compression.ZipFile]::ExtractToDirectory($remoteZipPath, $remoteExtractPath)
                Write-Host $msgExtractionComplete
            } else {
                Write-Host ($msgZipNotFound -f $remoteZipPath)
            }
        } -ArgumentList $RemoteZipPath, $RemoteExtractPath, $msgCheckingExtractPath, $msgExtractPathNotExist, $msgZipFound, $msgZipNotFound, $msgRemovingFiles, $msgExtractingZip, $msgExtractionComplete

        # Start the service after unzipping
        Write-Host $msgStartingService
        Invoke-Command -Session $session -ScriptBlock {
            param($serviceName, $msgServiceStarted, $msgServiceNotFound, $nssmexPort)
            $serviceStarted = $false
            
            # Try nssmex REST API first
            try {
                $startUrl = "http://localhost:${nssmexPort}/api/start/${serviceName}"
                Write-Host "  Trying nssmex API: $startUrl" -ForegroundColor Gray
                $response = Invoke-RestMethod -Uri $startUrl -Method Get
                Write-Host "  nssmex: $msgServiceStarted" -ForegroundColor Green
                $serviceStarted = $true
            } catch {
                Write-Host "  nssmex API not available or failed" -ForegroundColor Yellow
            }
            
            # Also try Windows Services
            if (Get-Service -Name $serviceName -ErrorAction SilentlyContinue) {
                try {
                    Start-Service -Name $serviceName -ErrorAction Stop
                    Write-Host "  Windows Service: $msgServiceStarted" -ForegroundColor Green
                    $serviceStarted = $true
                } catch {
                    Write-Host "  Windows Service start failed: $_" -ForegroundColor Yellow
                }
            } else {
                Write-Host "  Windows Service not found" -ForegroundColor Yellow
            }
            
            if (-not $serviceStarted) {
                Write-Host "  WARNING: Could not start service via any method" -ForegroundColor Yellow
            }
        } -ArgumentList $ServiceName, $msgServiceStarted, $msgServiceNotFound, $NssmexPort
        Write-Host $msgRemoteUnzipFinished
        Remove-PSSession $session

        # Remove the zip file after publishing
        if (Test-Path $zipPath) { 
            Remove-Item $zipPath -Force 
        }

        # Play a beep sound if enabled
        if ($EnableBeep) {
            [console]::beep($BeepFrequency, $BeepDuration)
        }
        
        Write-Host "Successfully deployed $AppName to $RemoteMachine" -ForegroundColor Green
    } else {
        Write-Host ($msgPublishNotFound -f $publishPath) -ForegroundColor Red
        exit 1
    }
} catch {
    Write-Error "An error occurred: $_"
    exit 1
}