[CmdletBinding()]
param(
  [string]$Package = "examples/showcase/windows_skia",
  [string]$AppName = "",
  [string]$DistDir = "dist\windows-msvc",
  [switch]$NoBuild,
  [string]$VcpkgRoot = "",
  [string]$WgpuNativeRoot = "",
  [switch]$EnableWebView2,
  [string]$WebView2SdkRoot = "",
  [string]$Version = "0.1.0",
  [string]$BuildNumber = "1"
)

$ErrorActionPreference = "Stop"

function Require-Path {
  param(
    [string]$PathValue,
    [string]$Message
  )

  if (-not (Test-Path -LiteralPath $PathValue)) {
    throw $Message
  }
}

function Assert-ChildPath {
  param(
    [string]$Parent,
    [string]$Child
  )

  $resolvedParent = [System.IO.Path]::GetFullPath($Parent).TrimEnd('\') + '\'
  $resolvedChild = [System.IO.Path]::GetFullPath($Child)
  if (-not $resolvedChild.StartsWith($resolvedParent, [System.StringComparison]::OrdinalIgnoreCase)) {
    throw "Refusing to modify path outside ${resolvedParent}: $resolvedChild"
  }
}

function Convert-PackagePath {
  param([string]$Value)
  return ($Value -replace '/', '\')
}

function Ensure-WgpuNativeRoot {
  param([string]$ExplicitRoot)

  if (-not [string]::IsNullOrWhiteSpace($ExplicitRoot)) {
    $root = (Resolve-Path -LiteralPath $ExplicitRoot).Path
    Require-Path (Join-Path $root "lib\wgpu_native.dll") "Missing WGPU dynamic library: $(Join-Path $root 'lib\wgpu_native.dll')"
    Require-Path (Join-Path $root "wgpu-native-meta\wgpu-native-git-tag") "Missing WGPU release metadata under $root"
    return $root
  }

  $assetName = "wgpu-windows-x86_64-msvc-release.zip"
  $releaseTag = "v27.0.4.0"
  $releaseRepo = "gfx-rs/wgpu-native"
  $toolsRoot = Join-Path $repoRoot ".tools\wgpu-native"
  $extractRoot = Join-Path $toolsRoot ($assetName -replace '\.zip$', '')
  $dllPath = Join-Path $extractRoot "lib\wgpu_native.dll"
  $tagPath = Join-Path $extractRoot "wgpu-native-meta\wgpu-native-git-tag"

  if ((Test-Path -LiteralPath $dllPath) -and (Test-Path -LiteralPath $tagPath)) {
    return (Resolve-Path -LiteralPath $extractRoot).Path
  }

  New-Item -ItemType Directory -Path $toolsRoot -Force | Out-Null
  $zipPath = Join-Path $toolsRoot $assetName
  $url = "https://github.com/$releaseRepo/releases/download/$releaseTag/$assetName"
  Write-Host "==> Downloading $url"
  Invoke-WebRequest -UseBasicParsing -Uri $url -OutFile $zipPath

  if (Test-Path -LiteralPath $extractRoot) {
    Assert-ChildPath $toolsRoot $extractRoot
    Remove-Item -LiteralPath $extractRoot -Recurse -Force
  }
  $tmpExtract = Join-Path $toolsRoot "extract-$([Guid]::NewGuid().ToString('N'))"
  Expand-Archive -Path $zipPath -DestinationPath $tmpExtract -Force
  Move-Item -LiteralPath $tmpExtract -Destination $extractRoot

  Require-Path $dllPath "Missing WGPU dynamic library after extraction: $dllPath"
  Require-Path $tagPath "Missing WGPU release metadata after extraction: $tagPath"
  return (Resolve-Path -LiteralPath $extractRoot).Path
}

function Test-PackageUsesWgpu {
  param([string]$PackagePath)

  $pkgPath = Join-Path $repoRoot (Join-Path (Convert-PackagePath $PackagePath) "moon.pkg")
  Require-Path $pkgPath "Missing MoonBit package manifest: $pkgPath"
  $pkg = Get-Content -LiteralPath $pkgPath -Raw
  $usesWgpuRenderer = $pkg.Contains("wzzc-dev/moui_wgpu_renderer")
  return $usesWgpuRenderer
}

function Find-ZlibRuntimeDll {
  $tripletRoot = $env:MOUI_MSVC_ZLIB_TRIPLET_ROOT
  if ([string]::IsNullOrWhiteSpace($tripletRoot)) {
    throw "MOUI_MSVC_ZLIB_TRIPLET_ROOT is not set; msvc_env.ps1 did not configure zlib."
  }
  $binDir = Join-Path $tripletRoot "bin"
  foreach ($name in @("z.dll", "zlib1.dll", "zlib.dll")) {
    $candidate = Join-Path $binDir $name
    if (Test-Path -LiteralPath $candidate) {
      return $candidate
    }
  }
  $matches = Get-ChildItem -LiteralPath $binDir -Filter "zlib*.dll" -ErrorAction SilentlyContinue
  if ($matches.Count -gt 0) {
    return $matches[0].FullName
  }
  throw "Could not find zlib runtime DLL under $binDir"
}

function Write-Utf8NoBom {
  param(
    [string]$PathValue,
    [string]$Value
  )

  $encoding = New-Object System.Text.UTF8Encoding($false)
  [System.IO.File]::WriteAllText($PathValue, $Value, $encoding)
}

$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path
$repoRoot = (Resolve-Path (Join-Path $scriptDir "..\..")).Path
. (Join-Path $scriptDir "webview2_sdk.ps1")

$packageLeaf = Split-Path -Leaf (Convert-PackagePath $Package)
$packageParent = Split-Path -Leaf (Split-Path -Parent (Convert-PackagePath $Package))
if ([string]::IsNullOrWhiteSpace($AppName)) {
  $AppName = $packageParent
}

$usesWgpu = Test-PackageUsesWgpu $Package
$resolvedWgpuRoot = if ($usesWgpu) { Ensure-WgpuNativeRoot $WgpuNativeRoot } else { "" }
. (Join-Path $scriptDir "msvc_env.ps1") -VcpkgRoot $VcpkgRoot -WgpuNativeRoot $resolvedWgpuRoot
Enable-MsvcC11Atomics
if ($usesWgpu) {
  Enable-MsvcGlobalC11ModeForCOnlyStubs
}
$webView2 = $null
if ($EnableWebView2 -or -not [string]::IsNullOrWhiteSpace($WebView2SdkRoot)) {
  $webView2 = Enable-WebView2BuildEnvironment $WebView2SdkRoot
}

if (-not (Get-Command moon -ErrorAction SilentlyContinue)) {
  throw "MoonBit toolchain is not available in PATH. Install moon and try again."
}
if (-not (Get-Command node -ErrorAction SilentlyContinue)) {
  throw "Node.js is required to validate the package manifest."
}

$packageBuildDir = Join-Path $repoRoot ("_build\native\debug\build\" + (Convert-PackagePath $Package))
$builtExe = Join-Path $packageBuildDir "$packageLeaf.exe"
$appDir = Join-Path $repoRoot (Join-Path $DistDir $AppName)
$appExe = Join-Path $appDir "$AppName.exe"
$manifestPath = Join-Path $appDir "moui-package.json"
$wgpuAppRoot = Join-Path $appDir "wgpu-native"
$runCmdPath = Join-Path $appDir "run.cmd"

Push-Location $repoRoot
try {
  Write-Host "==> repo root: $repoRoot"
  Write-Host "==> package: $Package"
  if ($usesWgpu) {
    Write-Host "==> renderer route: native WGPU experimental"
    Write-Host "==> WGPU native root: $resolvedWgpuRoot"
  } else {
    Write-Host "==> renderer route: native Skia mainline"
  }
  if ($webView2) {
    Write-Host "==> WebView2 bridge: enabled with static loader; Evergreen Runtime required"
  } else {
    Write-Host "==> WebView2 bridge: fallback unavailable build"
  }
  if (-not $NoBuild) {
    & moon build $Package --target native
    if ($LASTEXITCODE -ne 0) {
      throw "moon build failed with exit code $LASTEXITCODE"
    }
  }

  Require-Path $builtExe "Built executable not found: $builtExe"

  if (Test-Path -LiteralPath $appDir) {
    Assert-ChildPath (Join-Path $repoRoot $DistDir) $appDir
    Remove-Item -LiteralPath $appDir -Recurse -Force
  }
  New-Item -ItemType Directory -Path $appDir | Out-Null

  Copy-Item -LiteralPath $builtExe -Destination $appExe
  if ($usesWgpu) {
    Copy-Item -LiteralPath $resolvedWgpuRoot -Destination $wgpuAppRoot -Recurse
  }

  $zlibRuntime = Find-ZlibRuntimeDll
  $zlibRuntimeName = Split-Path -Leaf $zlibRuntime
  Copy-Item -LiteralPath $zlibRuntime -Destination (Join-Path $appDir $zlibRuntimeName)

  if ($usesWgpu) {
    $runCmd = @(
      "@echo off",
      "setlocal",
      'set "MBT_WGPU_NATIVE_ROOT=%~dp0wgpu-native"',
      'set "PATH=%~dp0;%~dp0wgpu-native\lib;%PATH%"',
      '"%~dp0' + $AppName + '.exe" %*',
      "exit /b %ERRORLEVEL%"
    )
  } else {
    $runCmd = @(
      "@echo off",
      "setlocal",
      'set "PATH=%~dp0;%PATH%"',
      '"%~dp0' + $AppName + '.exe" %*',
      "exit /b %ERRORLEVEL%"
    )
  }
  Set-Content -LiteralPath $runCmdPath -Value $runCmd -Encoding ASCII

  $runtimeFiles = @(
    "run.cmd",
    $zlibRuntimeName
  )
  if ($usesWgpu) {
    $runtimeFiles = @(
      "run.cmd",
      "wgpu-native\lib\wgpu_native.dll",
      $zlibRuntimeName
    )
  }
  $manifest = @{
    schemaVersion = 1
    platform = "windows"
    outputKind = "portable-folder"
    appName = $AppName
    moonPackage = $Package
    version = $Version
    buildNumber = $BuildNumber
    executable = (Split-Path -Leaf $appExe)
    bundleName = $AppName
    runtimeFiles = $runtimeFiles
  }
  if ($webView2) {
    $manifest.externalRuntimeDependencies = @("Microsoft Edge WebView2 Evergreen Runtime")
  }
  $manifest = $manifest | ConvertTo-Json -Depth 4
  Write-Utf8NoBom $manifestPath $manifest

  & node (Join-Path $repoRoot "scripts\validate-package-manifest.mjs") $manifestPath --platform windows
  if ($LASTEXITCODE -ne 0) {
    throw "manifest validation failed with exit code $LASTEXITCODE"
  }

  Write-Host "==> Wrote $appDir"
}
finally {
  Pop-Location
}