param(
  [string]$Version = (Get-Date -Format "yyyyMMdd-HHmm")
)

$ErrorActionPreference = "Stop"

$root = Resolve-Path (Join-Path $PSScriptRoot "..")
$releaseRoot = Join-Path $root "public\source-release"
$stageRoot = Join-Path $releaseRoot "stage"
$stageProject = Join-Path $stageRoot "plai-chain-source"
$zipName = "plai-chain-source-$Version.zip"
$zipPath = Join-Path $releaseRoot $zipName
$latestPath = Join-Path $releaseRoot "plai-chain-source-latest.zip"
$manifestPath = Join-Path $releaseRoot "manifest.json"
$historyPath = Join-Path $releaseRoot "release-history.json"

$excludedDirs = @(
  ".git",
  ".agents",
  ".codex",
  "node_modules",
  "data",
  "logs",
  "tmp",
  "temp",
  "public\source-release"
)

$excludedFileNames = @(
  ".env",
  "ledger.json",
  "npm-debug.log",
  "yarn-debug.log",
  "yarn-error.log",
  "pnpm-debug.log",
  "aisfor-chain.code-workspace"
)

$excludedExtensions = @(
  ".bak",
  ".tmp",
  ".log",
  ".pfx",
  ".pem",
  ".key",
  ".crt",
  ".sqlite",
  ".db"
)

function Is-ExcludedPath([string]$fullPath) {
  $relative = Resolve-RelativePath $fullPath
  foreach ($dir in $excludedDirs) {
    if ($relative -eq $dir -or $relative.StartsWith("$dir\")) {
      return $true
    }
  }

  $name = [System.IO.Path]::GetFileName($fullPath)
  if ($name -like ".env.*" -and $name -ne ".env.example") {
    return $true
  }
  if ($excludedFileNames -contains $name) {
    return $true
  }
  if ($name -like "*.zip" -or $name -like "*.7z" -or $name -like "*.tar" -or $name -like "*.tgz" -or $name -like "*.gz") {
    return $true
  }
  if ($name -match "secret|private|password|credential|recovery|wallet-key|admin-session") {
    return $true
  }

  $ext = [System.IO.Path]::GetExtension($fullPath).ToLowerInvariant()
  return $excludedExtensions -contains $ext
}

function Resolve-RelativePath([string]$fullPath) {
  $rootPath = [string]$root
  if (-not $rootPath.EndsWith("\")) {
    $rootPath = "$rootPath\"
  }
  $rootUri = New-Object System.Uri($rootPath)
  $fileUri = New-Object System.Uri($fullPath)
  return [System.Uri]::UnescapeDataString($rootUri.MakeRelativeUri($fileUri).ToString()).Replace("/", "\")
}

New-Item -ItemType Directory -Force -Path $releaseRoot | Out-Null
if (Test-Path $stageRoot) {
  Remove-Item -LiteralPath $stageRoot -Recurse -Force
}
New-Item -ItemType Directory -Force -Path $stageProject | Out-Null

$files = Get-ChildItem -LiteralPath $root -Recurse -File -Force |
  Where-Object { -not (Is-ExcludedPath $_.FullName) }

$fileIndex = @(
  foreach ($file in $files) {
    $relative = Resolve-RelativePath $file.FullName
    [ordered]@{
      path = $relative
      sizeBytes = $file.Length
      modifiedAt = $file.LastWriteTimeUtc.ToString("o")
    }
  }
)

$topDirectories = @(
  $fileIndex |
    ForEach-Object {
      $parts = $_.path.Split("\")
      if ($parts.Length -gt 1) { $parts[0] } else { "(root)" }
    } |
    Sort-Object -Unique
)

foreach ($file in $files) {
  $relative = Resolve-RelativePath $file.FullName
  $destination = Join-Path $stageProject $relative
  $destinationDir = Split-Path $destination -Parent
  New-Item -ItemType Directory -Force -Path $destinationDir | Out-Null
  Copy-Item -LiteralPath $file.FullName -Destination $destination -Force
}

$readmePublic = Join-Path $stageProject "SOURCE_PUBLIC_NOTICE_KO.md"
Copy-Item -LiteralPath (Join-Path $root "docs\SOURCE_PUBLIC_NOTICE_KO.md") -Destination $readmePublic -Force

if (Test-Path $zipPath) {
  Remove-Item -LiteralPath $zipPath -Force
}
Compress-Archive -Path (Join-Path $stageRoot "plai-chain-source") -DestinationPath $zipPath -Force
Copy-Item -LiteralPath $zipPath -Destination $latestPath -Force

$hash = (Get-FileHash -Algorithm SHA256 -LiteralPath $zipPath).Hash.ToLowerInvariant()
$latestHash = (Get-FileHash -Algorithm SHA256 -LiteralPath $latestPath).Hash.ToLowerInvariant()
$fileInfo = Get-Item -LiteralPath $zipPath
$generatedAt = (Get-Date).ToUniversalTime().ToString("o")
$releaseNotes = @(
  "Public source package generated with sensitive data removal rules.",
  "Includes server routes, services, models, views, public assets, contracts, documentation, and examples.",
  "Excludes runtime secrets, wallets/private keys, database files, logs, uploads, dependency folders, and backups.",
  "SHA256 is published for package integrity verification."
)

$releaseEntry = [ordered]@{
  version = $Version
  package = $zipName
  generatedAt = $generatedAt
  sizeBytes = $fileInfo.Length
  sha256 = $hash
  fileCount = $fileIndex.Count
  notes = $releaseNotes
}

$history = @()
if (Test-Path $historyPath) {
  try {
    $historyRaw = (Get-Content -LiteralPath $historyPath -Raw -Encoding UTF8) -replace "^\uFEFF", ""
    $parsedHistory = $historyRaw | ConvertFrom-Json
    if ($parsedHistory) {
      $history = @($parsedHistory)
    }
  } catch {
    $history = @()
  }
}
$history = @($releaseEntry) + @($history | Where-Object { $_.version -ne $Version } | Select-Object -First 11)

$manifest = [ordered]@{
  ok = $true
  project = "PLAI Chain"
  package = $zipName
  latest = "plai-chain-source-latest.zip"
  version = $Version
  generatedAt = $generatedAt
  sizeBytes = $fileInfo.Length
  sha256 = $hash
  latestSha256 = $latestHash
  fileCount = $fileIndex.Count
  directoryCount = $topDirectories.Count
  topDirectories = $topDirectories
  releaseNotes = $releaseNotes
  releases = $history
  fileIndex = @($fileIndex | Select-Object -First 300)
  fileIndexTruncated = $fileIndex.Count -gt 300
  license = @{
    name = "PLAI Chain Public Source Review License"
    summary = "Public review, audit, and transparency use is allowed. Commercial reuse, exchange listing impersonation, private-key extraction attempts, and redistribution with removed notices are not allowed without written permission."
  }
  excluded = @{
    directories = $excludedDirs
    files = $excludedFileNames
    extensions = $excludedExtensions
    patterns = @(".env.* except .env.example", "secret/private/password/credential/recovery/wallet-key/admin-session", "archives")
  }
}

$manifest | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $manifestPath -Encoding UTF8
$history | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $historyPath -Encoding UTF8
Remove-Item -LiteralPath $stageRoot -Recurse -Force

Write-Host "Created $zipPath"
Write-Host "SHA256 $hash"
