# Shared utilities for Windows scripts
# Provides: colors, progress indicators, interactive detection

Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'

# Detect if running in interactive shell
function Test-Interactive
{
    # Check if both input and output are from terminal
    # In non-interactive environments (CI/CD), this will be false
    $isInteractive = [Environment]::UserInteractive -and
                     (-not [Console]::IsInputRedirected) -and
                     (-not [Console]::IsOutputRedirected)
    return $isInteractive
}

# Global spinner state
$script:SpinnerJob = $null
$script:SpinnerFrames = @('⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏')

# Logging functions
function Write-Info
{
    param([string]$Message)

    if (Test-Interactive)
    {
        Write-Host $Message -ForegroundColor Blue
    }
    else
    {
        Write-Output $Message
    }
}

function Write-Success
{
    param([string]$Message)

    if (Test-Interactive)
    {
        Write-Host $Message -ForegroundColor Green
    }
    else
    {
        Write-Output $Message
    }
}

function Write-Warning-Custom
{
    param([string]$Message)

    if (Test-Interactive)
    {
        Write-Host $Message -ForegroundColor Yellow
    }
    else
    {
        Write-Output "WARNING: $Message"
    }
}

function Write-Error-Custom
{
    param([string]$Message)

    if (Test-Interactive)
    {
        Write-Host $Message -ForegroundColor Red
    }
    else
    {
        Write-Output "ERROR: $Message" *>&1
    }
}

function Write-Step
{
    param([string]$Message)

    if (Test-Interactive)
    {
        Write-Host $Message -ForegroundColor Magenta
    }
    else
    {
        Write-Output $Message
    }
}

function Write-Result
{
    param([string]$Message)

    if (Test-Interactive)
    {
        Write-Host $Message -ForegroundColor Cyan
    }
    else
    {
        Write-Output $Message
    }
}

# Spinner functions (only for interactive shells)
function Start-Spinner
{
    param([string]$Message = "Processing...")

    if (-not (Test-Interactive))
    {
        Write-Output $Message
        return
    }

    $script:SpinnerJob = Start-Job -ScriptBlock {
        param($Frames, $Msg)

        $i = 0
        while ($true)
        {
            $frame = $Frames[$i % $Frames.Length]
            [Console]::SetCursorPosition(0, [Console]::CursorTop)
            Write-Host -NoNewline "$frame $Msg"
            Start-Sleep -Milliseconds 100
            $i++
        }
    } -ArgumentList $script:SpinnerFrames, $Message
}

function Stop-Spinner
{
    if ($script:SpinnerJob -ne $null)
    {
        Stop-Job -Job $script:SpinnerJob -ErrorAction SilentlyContinue
        Remove-Job -Job $script:SpinnerJob -Force -ErrorAction SilentlyContinue
        $script:SpinnerJob = $null

        if (Test-Interactive)
        {
            # Clear the spinner line
            [Console]::SetCursorPosition(0, [Console]::CursorTop)
            Write-Host (' ' * ([Console]::WindowWidth - 1)) -NoNewline
            [Console]::SetCursorPosition(0, [Console]::CursorTop)
        }
    }
}

function Update-Spinner
{
    param([string]$Message)

    if (-not (Test-Interactive))
    {
        Write-Output $Message
        return
    }

    if ($script:SpinnerJob -ne $null)
    {
        Stop-Spinner
        Start-Spinner -Message $Message
    }
}

# Execute command with progress indicator
function Invoke-WithProgress
{
    param(
        [string]$Message,
        [scriptblock]$ScriptBlock
    )

    if (Test-Interactive)
    {
        Start-Spinner -Message $Message

        try
        {
            $output = & $ScriptBlock 2>&1
            $exitCode = $LASTEXITCODE

            Stop-Spinner

            if ($exitCode -eq 0 -or $null -eq $exitCode)
            {
                Write-Success "✓ $Message - Done!"
            }
            else
            {
                Write-Error-Custom "✗ $Message - Failed!"
                if ($output)
                {
                    Write-Output $output
                }
            }

            return $exitCode
        }
        catch
        {
            Stop-Spinner
            Write-Error-Custom "✗ $Message - Failed!"
            throw
        }
    }
    else
    {
        # Non-interactive: just show message and run
        Write-Output "→ $Message"

        try
        {
            & $ScriptBlock
            $exitCode = $LASTEXITCODE

            if ($exitCode -eq 0 -or $null -eq $exitCode)
            {
                Write-Output "✓ $Message - Done!"
            }
            else
            {
                Write-Output "✗ $Message - Failed!"
            }

            return $exitCode
        }
        catch
        {
            Write-Output "✗ $Message - Failed!"
            throw
        }
    }
}

# Check system requirements
function Test-SystemRequirements
{
    param(
        [int]$MinCpu = 4,
        [int]$MinRamGB = 6,
        [int]$MinDiskGB = 20
    )

    Write-Step "Checking system requirements..."

    # Check CPU cores
    $cpuCount = [Environment]::ProcessorCount
    if ($cpuCount -lt $MinCpu)
    {
        Write-Error-Custom "Insufficient CPU cores: $cpuCount (minimum: $MinCpu)"
        return $false
    }

    # Check RAM
    $totalMemBytes = (Get-CimInstance Win32_ComputerSystem).TotalPhysicalMemory
    $totalMemGB = [Math]::Round($totalMemBytes / 1GB, 2)
    if ($totalMemGB -lt $MinRamGB)
    {
        Write-Error-Custom "Insufficient RAM: ${totalMemGB}GB (minimum: ${MinRamGB}GB)"
        return $false
    }

    # Check disk space
    $currentDrive = Split-Path -Qualifier $PWD
    $driveLetter = $currentDrive.Substring(0, 1)
    $freeGB = [Math]::Round((Get-PSDrive -Name $driveLetter).Free / 1GB, 2)
    if ($freeGB -lt $MinDiskGB)
    {
        Write-Error-Custom "Insufficient disk space: ${freeGB}GB (minimum: ${MinDiskGB}GB)"
        return $false
    }

    Write-Success "System requirements met: CPU cores=$cpuCount, RAM=${totalMemGB}GB, FreeDisk=${freeGB}GB"
    return $true
}

# Confirm action
function Confirm-Action
{
    param([string]$Prompt = "Are you sure?")

    if (-not (Test-Interactive))
    {
        # In non-interactive mode, assume yes
        return $true
    }

    $response = Read-Host "$Prompt [y/N]"
    return $response -match '^[yY]([eE][sS])?$'
}

# Ensure spinner cleanup on exit
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
    Stop-Spinner
}
