#
#  Copyright 2026 The InfiniFlow Authors. All Rights Reserved.
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use this file except in compliance with the License.
#  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.
#

param(
    [string]$Version = "latest",
    [string]$InstallDir = "$env:LOCALAPPDATA\Programs\RAGFlow",
    [string]$GitHubRepo = "infiniflow/ragflow",
    [string]$CliName = "ragflow-cli"
)

$ErrorActionPreference = "Stop"

function Write-Info {
    param([string]$Message)
    Write-Host "[INFO] $Message" -ForegroundColor Green
}

function Write-Warn {
    param([string]$Message)
    Write-Host "[WARN] $Message" -ForegroundColor Yellow
}

function Write-ErrorMessage {
    param([string]$Message)
    Write-Host "[ERROR] $Message" -ForegroundColor Red
}

function Get-Platform {
    $os = "windows"
    $arch = "amd64"

    if ($env:PROCESSOR_ARCHITECTURE -eq "ARM64" -or $env:PROCESSOR_ARCHITEW6432 -eq "ARM64") {
        $arch = "arm64"
    }
    elseif ([Environment]::Is64BitOperatingSystem) {
        $arch = "amd64"
    }
    else {
        Write-ErrorMessage "Unsupported Windows architecture: 386"
        exit 1
    }

    Write-Info "Detected platform: ${os}/${arch}"

    return @{
        OS = $os
        Arch = $arch
    }
}

function Get-ReleaseVersion {
    param(
        [string]$RequestedVersion,
        [string]$Repository
    )

    if ($RequestedVersion -ne "latest") {
        Write-Info "Using specified version: $RequestedVersion"
        return $RequestedVersion
    }

    Write-Info "Fetching latest release information"

    $releaseUrl = "https://api.github.com/repos/${Repository}/releases/latest"
    $release = Invoke-RestMethod -Uri $releaseUrl -TimeoutSec 20
    $latestVersion = $release.tag_name

    if ([string]::IsNullOrWhiteSpace($latestVersion)) {
        Write-ErrorMessage "Could not determine latest version"
        exit 1
    }

    Write-Info "Latest version: $latestVersion"
    return $latestVersion
}

function Get-DownloadInfo {
    param(
        [string]$ResolvedVersion,
        [string]$OS,
        [string]$Arch,
        [string]$Repository,
        [string]$BinaryName
    )

    $fileName = "${BinaryName}-${ResolvedVersion}-${OS}-${Arch}.exe"
    $baseUrl = "https://github.com/${Repository}/releases/download/${ResolvedVersion}"

    return @{
        FileName = $fileName
        BinaryUrl = "${baseUrl}/${fileName}"
        ChecksumUrl = "${baseUrl}/SHA256SUMS"
    }
}

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

    Write-Info "Downloading $Url"
    $ProgressPreference = "SilentlyContinue"
    Invoke-WebRequest -Uri $Url -OutFile $OutputPath -TimeoutSec 120
}

function Test-Checksum {
    param(
        [string]$BinaryPath,
        [string]$ChecksumPath,
        [string]$FileName
    )

    $checksumLine = Get-Content $ChecksumPath | Where-Object {
        $parts = $_ -split "\s+"
        $parts.Count -ge 2 -and $parts[1] -eq $FileName
    } | Select-Object -First 1

    if ([string]::IsNullOrWhiteSpace($checksumLine)) {
        Write-ErrorMessage "No checksum found for $FileName in SHA256SUMS"
        exit 1
    }

    $expected = ($checksumLine -split "\s+")[0].ToLowerInvariant()
    $actual = (Get-FileHash -Algorithm SHA256 -Path $BinaryPath).Hash.ToLowerInvariant()

    if ($actual -ne $expected) {
        Write-ErrorMessage "Checksum verification failed for $FileName"
        Write-ErrorMessage "Expected: $expected"
        Write-ErrorMessage "Actual:   $actual"
        exit 1
    }

    Write-Info "Checksum verified"
}

function Install-CLI {
    param(
        [string]$TempFile,
        [string]$TargetDir,
        [string]$BinaryName
    )

    if (-not (Test-Path $TargetDir)) {
        Write-Info "Creating directory: $TargetDir"
        New-Item -ItemType Directory -Path $TargetDir -Force | Out-Null
    }

    $targetFile = Join-Path $TargetDir "${BinaryName}.exe"
    Write-Info "Installing CLI to $targetFile"

    Stop-Process -Name $BinaryName -Force -ErrorAction SilentlyContinue
    Copy-Item -Path $TempFile -Destination $targetFile -Force

    $userPath = [Environment]::GetEnvironmentVariable("Path", "User")
    $pathParts = @()
    if (-not [string]::IsNullOrWhiteSpace($userPath)) {
        $pathParts = $userPath -split ";"
    }

    if ($pathParts -notcontains $TargetDir) {
        Write-Info "Adding $TargetDir to user PATH"
        $newPath = if ([string]::IsNullOrWhiteSpace($userPath)) { $TargetDir } else { "$userPath;$TargetDir" }
        [Environment]::SetEnvironmentVariable("Path", $newPath, "User")
        Write-Warn "Restart your terminal for PATH changes to take effect"
    }

    Write-Info "CLI installed successfully at $targetFile"
    return $targetFile
}

function Test-Installation {
    param([string]$CliPath)

    if (-not (Test-Path $CliPath)) {
        Write-Warn "CLI not found at expected location: $CliPath"
        return
    }

    try {
        & $CliPath --version
        if ($LASTEXITCODE -eq 0) {
            Write-Info "Installation verified successfully"
            return
        }
    }
    catch {
        Write-Warn "Could not execute version check: $_"
    }

    Write-Warn "Could not verify CLI execution, but the binary was installed"
}

function Main {
    $platform = Get-Platform
    $resolvedVersion = Get-ReleaseVersion -RequestedVersion $Version -Repository $GitHubRepo
    $downloadInfo = Get-DownloadInfo -ResolvedVersion $resolvedVersion -OS $platform.OS -Arch $platform.Arch -Repository $GitHubRepo -BinaryName $CliName

    Write-Info "Download URL: $($downloadInfo.BinaryUrl)"

    $tempBinary = [System.IO.Path]::GetTempFileName()
    $tempSums = [System.IO.Path]::GetTempFileName()

    try {
        Download-File -Url $downloadInfo.BinaryUrl -OutputPath $tempBinary
        Download-File -Url $downloadInfo.ChecksumUrl -OutputPath $tempSums
        Test-Checksum -BinaryPath $tempBinary -ChecksumPath $tempSums -FileName $downloadInfo.FileName

        $cliPath = Install-CLI -TempFile $tempBinary -TargetDir $InstallDir -BinaryName $CliName
        Test-Installation -CliPath $cliPath
        Write-Info "Installation complete"
    }
    finally {
        Remove-Item $tempBinary -Force -ErrorAction SilentlyContinue
        Remove-Item $tempSums -Force -ErrorAction SilentlyContinue
    }
}

Main
