# Fabric Windows Installer Script
# Usage: iwr -useb https://raw.githubusercontent.com/danielmiessler/fabric/main/scripts/installer/install.ps1 | iex
# Usage with custom directory: $env:INSTALL_DIR="C:\tools"; iwr -useb https://raw.githubusercontent.com/danielmiessler/fabric/main/scripts/installer/install.ps1 | iex

param(
    [string]$InstallDir = $env:INSTALL_DIR
)

$ErrorActionPreference = "Stop"

# Colors for output (Windows Console colors)
$Colors = @{
    Red    = "Red"
    Green  = "Green"
    Yellow = "Yellow"
    Blue   = "Cyan"
    White  = "White"
}

# Print functions
function Write-Info {
    param([string]$Message)
    Write-Host "[INFO] $Message" -ForegroundColor $Colors.Blue
}

function Write-Success {
    param([string]$Message)
    Write-Host "[SUCCESS] $Message" -ForegroundColor $Colors.Green
}

function Write-Warning {
    param([string]$Message)
    Write-Host "[WARNING] $Message" -ForegroundColor $Colors.Yellow
}

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

# Detect Windows architecture
function Get-Architecture {
    $arch = $env:PROCESSOR_ARCHITECTURE
    $archAMD64 = $env:PROCESSOR_ARCHITEW6432

    # Check for ARM64
    if ($arch -eq "ARM64") {
        return "arm64"
    }

    # Check for x86_64/AMD64
    if ($arch -eq "AMD64" -or $archAMD64 -eq "AMD64") {
        return "x86_64"
    }

    # Check for x86 (32-bit)
    if ($arch -eq "X86") {
        return "i386"
    }

    Write-Error "Unsupported architecture: $arch"
    Write-Error "This installer supports x86_64, i386, and arm64"
    exit 1
}

# Test if running with appropriate permissions for directory
function Test-WritePermission {
    param([string]$Path)

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

        $testFile = Join-Path $Path "fabric_write_test.tmp"
        "test" | Out-File -FilePath $testFile -Force
        Remove-Item $testFile -Force
        return $true
    }
    catch {
        return $false
    }
}

# Download and install Fabric
function Install-Fabric {
    param(
        [string]$Architecture,
        [string]$InstallDirectory
    )

    # Construct download URL
    $filename = "fabric_Windows_$Architecture.zip"
    $downloadUrl = "https://github.com/danielmiessler/fabric/releases/latest/download/$filename"

    Write-Info "Downloading Fabric for Windows $Architecture..."
    Write-Info "URL: $downloadUrl"

    # Create temporary directory
    $tempDir = Join-Path $env:TEMP "fabric_install_$(Get-Random)"
    New-Item -Path $tempDir -ItemType Directory -Force | Out-Null
    $tempFile = Join-Path $tempDir "fabric.zip"

    try {
        # Download the archive
        Write-Info "Downloading archive..."
        Invoke-WebRequest -Uri $downloadUrl -OutFile $tempFile -UseBasicParsing

        Write-Info "Extracting Fabric binary..."

        # Extract the zip file
        Add-Type -AssemblyName System.IO.Compression.FileSystem
        $zip = [System.IO.Compression.ZipFile]::OpenRead($tempFile)

        # Find and extract only fabric.exe
        $fabricEntry = $zip.Entries | Where-Object { $_.Name -eq "fabric.exe" }
        if (!$fabricEntry) {
            Write-Error "fabric.exe not found in the downloaded archive"
            exit 1
        }

        # Create install directory if it doesn't exist
        if (!(Test-Path $InstallDirectory)) {
            Write-Info "Creating install directory: $InstallDirectory"
            New-Item -Path $InstallDirectory -ItemType Directory -Force | Out-Null
        }

        # Extract fabric.exe to install directory
        $fabricPath = Join-Path $InstallDirectory "fabric.exe"
        Write-Info "Installing Fabric to $fabricPath..."

        [System.IO.Compression.ZipFileExtensions]::ExtractToFile($fabricEntry, $fabricPath, $true)
        $zip.Dispose()

        Write-Success "Fabric installed successfully to $fabricPath"
        return $fabricPath
    }
    catch {
        Write-Error "Failed to download or extract Fabric: $($_.Exception.Message)"
        exit 1
    }
    finally {
        # Clean up
        if (Test-Path $tempDir) {
            Remove-Item $tempDir -Recurse -Force -ErrorAction SilentlyContinue
        }
    }
}

# Check if directory is in PATH
function Test-InPath {
    param([string]$Directory)

    $pathDirs = $env:PATH -split ';'
    return $pathDirs -contains $Directory
}

# Provide PATH setup instructions
function Show-PathInstructions {
    param([string]$InstallDir)

    if (Test-InPath $InstallDir) {
        Write-Success "✅ $InstallDir is already in your PATH"
    }
    else {
        Write-Warning "⚠️  $InstallDir is not in your PATH"
        Write-Info "To use fabric from anywhere, you have a few options:"
        Write-Info ""
        Write-Info "Option 1 - Add to PATH for current user (recommended):"
        Write-Info "  `$currentPath = [Environment]::GetEnvironmentVariable('PATH', 'User')"
        Write-Info "  [Environment]::SetEnvironmentVariable('PATH', `"`$currentPath;$InstallDir`", 'User')"
        Write-Info ""
        Write-Info "Option 2 - Add to PATH for all users (requires admin):"
        Write-Info "  `$currentPath = [Environment]::GetEnvironmentVariable('PATH', 'Machine')"
        Write-Info "  [Environment]::SetEnvironmentVariable('PATH', `"`$currentPath;$InstallDir`", 'Machine')"
        Write-Info ""
        Write-Info "Option 3 - Add to current session only:"
        Write-Info "  `$env:PATH += `";$InstallDir`""
        Write-Info ""
        Write-Info "After updating PATH, restart your terminal or run: refreshenv"
    }
}

# Verify installation
function Test-Installation {
    param([string]$FabricPath)

    if (Test-Path $FabricPath) {
        Write-Info "Verifying installation..."
        try {
            $version = & $FabricPath --version 2>$null
            if ($LASTEXITCODE -eq 0) {
                Write-Success "Fabric $version is working correctly!"
            }
            else {
                Write-Warning "Fabric binary exists but --version failed"
            }
        }
        catch {
            Write-Warning "Fabric binary exists but could not run --version"
        }
    }
    else {
        Write-Error "Fabric binary not found at $FabricPath"
        exit 1
    }
}

# Main installation function
function Main {
    Write-Info "🚀 Starting Fabric installation..."

    # Detect architecture
    $arch = Get-Architecture
    Write-Info "Detected architecture: $arch"

    # Determine install directory
    if (!$InstallDir) {
        $InstallDir = Join-Path $env:USERPROFILE ".local\bin"
    }

    Write-Info "Install directory: $InstallDir"

    # Check permissions
    if (!(Test-WritePermission $InstallDir)) {
        Write-Error "Cannot write to $InstallDir"
        Write-Error "Try running as Administrator or choose a different directory"
        Write-Info "Example with custom directory: `$env:INSTALL_DIR=`"C:\tools`"; iwr -useb ... | iex"
        exit 1
    }

    # Install Fabric
    $fabricPath = Install-Fabric -Architecture $arch -InstallDirectory $InstallDir

    # Verify installation
    Test-Installation -FabricPath $fabricPath

    # Check PATH and provide instructions
    Show-PathInstructions -InstallDir $InstallDir

    Write-Info ""
    Write-Success "🎉 Installation complete!"
    Write-Info ""
    Write-Info "Next steps:"
    Write-Info "  1. Run 'fabric --setup' to configure Fabric"
    Write-Info "  2. Add your API keys and preferences"
    Write-Info "  3. Start using Fabric with 'fabric --help'"
    Write-Info ""
    Write-Info "Documentation: https://github.com/danielmiessler/fabric"
}

# Run main function
Main