#Requires -Version 5.1
<#
.SYNOPSIS
    mb_clouseau Build Script
    Uncovering clues with MusicBee Clouseau

.DESCRIPTION
    Builds the mb_clouseau MusicBee plugin with all dependencies merged into a single DLL.
    Supports Debug and Release configurations.
    Output is placed in /publish folder.

.PARAMETER Configuration
    Build configuration: Debug or Release (default: Debug)

.PARAMETER Clean
    Clean build outputs before building

.PARAMETER NuGetRestore
    Restore NuGet packages before building

.PARAMETER SkipMerge
    Skip ILRepack merge step (output separate DLLs)

.PARAMETER Verbose
    Enable verbose output

.EXAMPLE
    .\build.ps1
    Builds Debug configuration

.EXAMPLE
    .\build.ps1 -Configuration Release
    Builds Release configuration

.EXAMPLE
    .\build.ps1 -Configuration Release -Clean
    Clean build in Release configuration
#>

[CmdletBinding()]
param(
    [Parameter(Position = 0)]
    [ValidateSet('Debug', 'Release')]
    [string]$Configuration = 'Debug',

    [switch]$Clean,
    [switch]$NuGetRestore,
    [switch]$SkipMerge
)

# ============================================================================
# Configuration
# ============================================================================

$ErrorActionPreference = 'Stop'
$script:ExitCode = 0

# Paths
$ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$ProjectRoot = $ScriptDir
$ProjectFile = Join-Path $ProjectRoot 'mb_clouseau.csproj'
$PluginDir = Join-Path $ProjectRoot 'plugin'
$BuildDir = Join-Path $ProjectRoot 'build'
$BinDir = Join-Path (Join-Path $BuildDir 'bin') $Configuration
$ObjDir = Join-Path $BuildDir 'obj'
$PublishDir = Join-Path $ProjectRoot 'publish'
$ToolsDir = Join-Path $ProjectRoot 'tools'
$ILRepackDir = Join-Path $ToolsDir 'ILRepack'
$ILRepackExe = Join-Path $ILRepackDir 'ILRepack.exe'

# Output
$OutputDll = 'mb_clouseau.dll'
$MergedDll = Join-Path $PublishDir $OutputDll

# Version info
$VersionFile = Join-Path $PluginDir 'AssemblyInfo.cs'
$Version = '0.1.0'

# ============================================================================
# Functions
# ============================================================================

function Write-Header {
    param([string]$Message)
    Write-Host ''
    Write-Host ('=' * 76) -ForegroundColor Cyan
    Write-Host " $Message" -ForegroundColor Cyan
    Write-Host ('=' * 76) -ForegroundColor Cyan
    Write-Host ''
}

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

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

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

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

function Get-MSBuildPath {
    # Try VS 2022
    $vs2022 = "${env:ProgramFiles}\Microsoft Visual Studio\2022\Community\MSBuild\Current\Bin\MSBuild.exe"
    if (Test-Path $vs2022) { return $vs2022 }

    $vs2022Pro = "${env:ProgramFiles}\Microsoft Visual Studio\2022\Professional\MSBuild\Current\Bin\MSBuild.exe"
    if (Test-Path $vs2022Pro) { return $vs2022Pro }

    $vs2022Ent = "${env:ProgramFiles}\Microsoft Visual Studio\2022\Enterprise\MSBuild\Current\Bin\MSBuild.exe"
    if (Test-Path $vs2022Ent) { return $vs2022Ent }

    # Try VS 2019
    $vs2019 = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\2019\Community\MSBuild\Current\Bin\MSBuild.exe"
    if (Test-Path $vs2019) { return $vs2019 }

    # Try dotnet msbuild
    $dotnetMSBuild = (Get-Command dotnet -ErrorAction SilentlyContinue)
    if ($dotnetMSBuild) { return 'dotnet build' }

    throw 'MSBuild not found. Install Visual Studio or .NET SDK.'
}

function Install-ILRepack {
    if (Test-Path $ILRepackExe) {
        Write-Info "ILRepack found at $ILRepackExe"
        return
    }

    Write-Step 'Installing ILRepack...'

    # Create tools directory
    if (-not (Test-Path $ToolsDir)) {
        New-Item -ItemType Directory -Path $ToolsDir -Force | Out-Null
    }

    # Download ILRepack via NuGet
    $nugetUrl = 'https://dist.nuget.org/win-x86-commandline/latest/nuget.exe'
    $nugetExe = Join-Path $ToolsDir 'nuget.exe'

    if (-not (Test-Path $nugetExe)) {
        Write-Info 'Downloading NuGet...'
        Invoke-WebRequest -Uri $nugetUrl -OutFile $nugetExe -UseBasicParsing
    }

    Write-Info 'Installing ILRepack package...'
    & $nugetExe install ILRepack -OutputDirectory $ToolsDir -NonInteractive -Verbosity quiet

    # Find ILRepack.exe
    $ilrepackPkg = Get-ChildItem -Path $ToolsDir -Filter 'ILRepack*' -Directory | Select-Object -First 1
    if ($ilrepackPkg) {
        $srcExe = Join-Path (Join-Path $ilrepackPkg.FullName 'tools') 'ILRepack.exe'
        if (Test-Path $srcExe) {
            if (-not (Test-Path $ILRepackDir)) {
                New-Item -ItemType Directory -Path $ILRepackDir -Force | Out-Null
            }
            Copy-Item $srcExe $ILRepackExe -Force
            Write-Success 'ILRepack installed'
        }
    }

    if (-not (Test-Path $ILRepackExe)) {
        throw 'Failed to install ILRepack'
    }
}

function Invoke-Clean {
    Write-Step 'Cleaning build outputs...'

    $dirsToClean = @($BuildDir, $PublishDir, (Join-Path $ProjectRoot 'bin'), (Join-Path $ProjectRoot 'obj'))

    foreach ($dir in $dirsToClean) {
        if (Test-Path $dir) {
            Remove-Item -Path $dir -Recurse -Force -ErrorAction SilentlyContinue
            Write-Info "Removed $dir"
        }
    }

    Write-Success 'Clean completed'
}

function Invoke-NuGetRestore {
    Write-Step 'Restoring NuGet packages...'

    $nugetExe = Join-Path $ToolsDir 'nuget.exe'
    if (Test-Path $nugetExe) {
        & $nugetExe restore $ProjectFile -NonInteractive
    }
    else {
        dotnet restore $ProjectFile
    }

    if ($LASTEXITCODE -ne 0) {
        throw 'NuGet restore failed'
    }

    Write-Success 'NuGet restore completed'
}

function Invoke-Build {
    Write-Step "Building $Configuration configuration..."

    $msbuild = Get-MSBuildPath

    # Create output directories
    if (-not (Test-Path $BinDir)) {
        New-Item -ItemType Directory -Path $BinDir -Force | Out-Null
    }

    # Build parameters
    $buildParams = @(
        $ProjectFile
        "/t:Build"
        "/p:Configuration=$Configuration"
        "/p:OutputPath=$BinDir"
        "/p:IntermediateOutputPath=$ObjDir\"
        "/v:minimal"
        "/nologo"
    )

    if ($msbuild -eq 'dotnet build') {
        # Use dotnet build
        $buildParams = @(
            'build'
            $ProjectFile
            "-c"
            $Configuration
            "-o"
            $BinDir
            "--nologo"
        )
        & dotnet @buildParams
    }
    else {
        # Use MSBuild
        & $msbuild @buildParams
    }

    if ($LASTEXITCODE -ne 0) {
        throw "Build failed with exit code $LASTEXITCODE"
    }

    # Verify output
    $outputPath = Join-Path $BinDir $OutputDll
    if (-not (Test-Path $outputPath)) {
        throw "Build output not found: $outputPath"
    }

    $fileInfo = Get-Item $outputPath
    Write-Success "Build completed: $OutputDll ($([math]::Round($fileInfo.Length / 1KB, 1)) KB)"
}

function Invoke-Merge {
    Write-Step 'Merging assemblies with ILRepack...'

    # Ensure ILRepack is available
    Install-ILRepack

    # Create publish directory
    if (-not (Test-Path $PublishDir)) {
        New-Item -ItemType Directory -Path $PublishDir -Force | Out-Null
    }

    # Find all DLLs to merge (except system assemblies)
    $primaryDll = Join-Path $BinDir $OutputDll
    $dllsToMerge = @($primaryDll)

    $excludePatterns = @(
        'System.*',
        'Microsoft.*',
        'mscorlib*',
        'netstandard*',
        'WindowsBase*',
        'PresentationCore*',
        'PresentationFramework*'
    )

    Get-ChildItem -Path $BinDir -Filter '*.dll' | ForEach-Object {
        $include = $true
        foreach ($pattern in $excludePatterns) {
            if ($_.Name -like $pattern) {
                $include = $false
                break
            }
        }
        if ($include -and $_.Name -ne $OutputDll) {
            $dllsToMerge += $_.FullName
        }
    }

    Write-Info "Merging $($dllsToMerge.Count) assemblies..."

    # Build ILRepack command arguments as a list
    $ilrepackArgs = [System.Collections.ArrayList]@()
    [void]$ilrepackArgs.Add("/out:$MergedDll")
    [void]$ilrepackArgs.Add("/target:library")
    [void]$ilrepackArgs.Add("/internalize")
    [void]$ilrepackArgs.Add("/allowdup")

    foreach ($dll in $dllsToMerge) {
        [void]$ilrepackArgs.Add($dll)
    }

    Write-Info "Running: ILRepack.exe $($ilrepackArgs -join ' ')"
    & $ILRepackExe $ilrepackArgs

    if ($LASTEXITCODE -ne 0) {
        throw "ILRepack failed with exit code $LASTEXITCODE"
    }

    # Verify output
    if (-not (Test-Path $MergedDll)) {
        throw "Merged DLL not found: $MergedDll"
    }

    $fileInfo = Get-Item $MergedDll
    Write-Success "Merged DLL: $MergedDll ($([math]::Round($fileInfo.Length / 1KB, 1)) KB)"

    # Copy additional files
    $configFiles = @('NLog.config', 'settings.json')
    foreach ($file in $configFiles) {
        $src = Join-Path $PluginDir $file
        if (Test-Path $src) {
            Copy-Item $src $PublishDir -Force
            Write-Info "Copied $file"
        }
    }
}

function Invoke-CopyWithoutMerge {
    Write-Step 'Copying build output to publish folder...'

    # Create publish directory
    if (-not (Test-Path $PublishDir)) {
        New-Item -ItemType Directory -Path $PublishDir -Force | Out-Null
    }

    # Copy all DLLs
    Copy-Item -Path (Join-Path $BinDir '*.dll') -Destination $PublishDir -Force
    Copy-Item -Path (Join-Path $BinDir '*.pdb') -Destination $PublishDir -Force -ErrorAction SilentlyContinue

    # Copy config files
    $configFiles = @('NLog.config', 'settings.json')
    foreach ($file in $configFiles) {
        $src = Join-Path $PluginDir $file
        if (Test-Path $src) {
            Copy-Item $src $PublishDir -Force
        }
    }

    $dllCount = (Get-ChildItem -Path $PublishDir -Filter '*.dll').Count
    Write-Success "Copied $dllCount DLLs to publish folder"
}

function Show-Summary {
    Write-Header 'Build Summary'

    $publishedDll = Join-Path $PublishDir $OutputDll

    if (Test-Path $publishedDll) {
        $fileInfo = Get-Item $publishedDll
        Write-Host "  Configuration : $Configuration" -ForegroundColor White
        Write-Host "  Output        : $publishedDll" -ForegroundColor White
        Write-Host "  Size          : $([math]::Round($fileInfo.Length / 1KB, 1)) KB" -ForegroundColor White
        Write-Host "  Created       : $($fileInfo.LastWriteTime)" -ForegroundColor White
        Write-Host ''

        # List all published files
        Write-Host '  Published files:' -ForegroundColor Gray
        Get-ChildItem -Path $PublishDir | ForEach-Object {
            $size = if ($_.PSIsContainer) { '<DIR>' } else { "$([math]::Round($_.Length / 1KB, 1)) KB" }
            Write-Host "    $($_.Name.PadRight(30)) $size" -ForegroundColor Gray
        }
    }

    Write-Host ''
    Write-Host '  To install: Copy mb_clouseau.dll to MusicBee\Plugins folder' -ForegroundColor Cyan
    Write-Host ''
}

# ============================================================================
# Main
# ============================================================================

try {
    Write-Header "mb_clouseau Build - $Configuration"

    # Clean if requested
    if ($Clean) {
        Invoke-Clean
    }

    # Restore NuGet packages if requested
    if ($NuGetRestore) {
        Invoke-NuGetRestore
    }

    # Build
    Invoke-Build

    # Merge or copy
    if ($SkipMerge) {
        Invoke-CopyWithoutMerge
    }
    else {
        Invoke-Merge
    }

    # Summary
    Show-Summary

    Write-Success 'Build completed successfully!'
}
catch {
    Write-Failure $_.Exception.Message
    if ($VerbosePreference -eq 'Continue') {
        Write-Host $_.ScriptStackTrace -ForegroundColor DarkGray
    }
    $script:ExitCode = 1

    # Pause on error so user can see the message
    Write-Host ''
    Write-Host 'Press any key to exit...' -ForegroundColor Yellow
    $null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')
}

exit $script:ExitCode
