# Pack production upload bundle (上传到服务器)
# Usage: powershell -ExecutionPolicy Bypass -File deploy/pack-server-upload.ps1

$ErrorActionPreference = "Stop"
$Root = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
$Out = Join-Path $Root ([char]0x4e0a + [char]0x4f20 + [char]0x5230 + [char]0x670d + [char]0x52a1 + [char]0x5668)
$Code = Join-Path $Out ([char]0x4ee3 + [char]0x7801)
$Db = Join-Path $Out ([char]0x6570 + [char]0x636e + [char]0x5e93)

Write-Host "pack: clean $Out"
if (Test-Path $Out) {
    Remove-Item -Recurse -Force $Out
}
New-Item -ItemType Directory -Path $Code -Force | Out-Null
New-Item -ItemType Directory -Path $Db -Force | Out-Null

# PowerShell 将 [quote_id] 等路径当作通配符，必须用 -LiteralPath 递归复制
function Copy-TreeLiteral {
    param(
        [Parameter(Mandatory = $true)][string]$SourceDir,
        [Parameter(Mandatory = $true)][string]$DestDir
    )
    $stack = New-Object System.Collections.Stack
    $stack.Push(@($SourceDir, $DestDir))
    while ($stack.Count -gt 0) {
        $pair = $stack.Pop()
        $currentSrc = $pair[0]
        $currentDst = $pair[1]
        if (-not (Test-Path -LiteralPath $currentDst)) {
            New-Item -ItemType Directory -Path $currentDst -Force | Out-Null
        }
        Get-ChildItem -LiteralPath $currentSrc -Force | ForEach-Object {
            if ($_.PSIsContainer) {
                $stack.Push(@($_.FullName, (Join-Path $currentDst $_.Name)))
            } elseif ($_.Extension -ne ".md") {
                Copy-Item -LiteralPath $_.FullName -Destination (Join-Path $currentDst $_.Name) -Force
            }
        }
    }
}

$RequiredRoutes = @(
    "app\api\quotes\[quote_id]\route.ts",
    "app\api\markup-configs\[customer_id]\route.ts",
    "app\api\admin\markup-configs\[customer_id]\route.ts",
    "app\api\admin\queues\[[...slug]]\route.ts",
    "app\api\alerts\[id]\resolve\route.ts",
    "app\api\admin\customers\[customer_id]\provider-credentials\route.ts",
    "app\api\flock\quotes\route.ts",
    "app\api\addresses\mothership-suggest\route.ts"
)

function Convert-ShFilesToLf {
    param([Parameter(Mandatory = $true)][string]$BaseDir)
    Get-ChildItem -Path $BaseDir -Filter "*.sh" -Recurse -File -ErrorAction SilentlyContinue | ForEach-Object {
        $text = [System.IO.File]::ReadAllText($_.FullName)
        $fixed = $text -replace "`r`n", "`n" -replace "`r", "`n"
        if ($text -ne $fixed) {
            [System.IO.File]::WriteAllText($_.FullName, $fixed, [System.Text.UTF8Encoding]::new($false))
            Write-Host "pack: LF $($_.FullName.Substring($BaseDir.Length).TrimStart('\','/'))"
        }
    }
}

function Test-RequiredRoutes {
    param([string]$BaseDir)
    $missing = @()
    foreach ($rel in $RequiredRoutes) {
        $path = Join-Path $BaseDir $rel
        if (-not (Test-Path -LiteralPath $path)) {
            $missing += $rel
        }
    }
    if ($missing.Count -gt 0) {
        throw "pack: missing dynamic API routes (bracket paths): $($missing -join ', ')"
    }
}

$CodeDirs = @(
    "app",
    "components",
    "hooks",
    "lib",
    "modules",
    "workers",
    "prisma",
    "public"
)

foreach ($dir in $CodeDirs) {
    $src = Join-Path $Root $dir
    if (-not (Test-Path $src)) {
        throw "missing dir: $dir"
    }
    Write-Host "pack: copy $dir"
    $dstDir = Join-Path $Code $dir
    Copy-TreeLiteral -SourceDir $src -DestDir $dstDir
}

$deploySrc = Join-Path $Root "deploy"
$deployDst = Join-Path $Code "deploy"
New-Item -ItemType Directory -Path $deployDst -Force | Out-Null
Get-ChildItem -Path $deploySrc -Force | Where-Object {
    $_.Name -ne "database" -and
    $_.Name -ne "pack-server-upload.ps1" -and
    $_.Name -ne "pack-readme-template.md"
} | ForEach-Object {
    Copy-Item -Path $_.FullName -Destination $deployDst -Recurse -Force
}

$CodeFiles = @(
    "package.json",
    "package-lock.json",
    "next.config.ts",
    "tsconfig.json",
    "middleware.ts",
    "next-env.d.ts",
    "postcss.config.mjs",
    "tailwind.config.ts",
    "Dockerfile",
    "Dockerfile.worker",
    ".dockerignore"
)

foreach ($file in $CodeFiles) {
    $src = Join-Path $Root $file
    if (Test-Path $src) {
        Copy-Item -Path $src -Destination (Join-Path $Code $file) -Force
        Write-Host "pack: copy $file"
    }
}

$envExample = Join-Path $Root "deploy\.env.bt-host-mysql.example"
if (Test-Path $envExample) {
    Copy-Item -Path $envExample -Destination (Join-Path $deployDst ".env.bt-host-mysql.example") -Force
    Write-Host "pack: copy deploy/.env.bt-host-mysql.example"
}

$staleEnvPath = Join-Path $Code ".env"
if ((Test-Path -LiteralPath $Code) -and (Test-Path -LiteralPath $staleEnvPath)) {
    Remove-Item -LiteralPath $staleEnvPath -Force
    Write-Host "pack: removed stale 代码/.env"
}

$DbFiles = @("chajia.sql", "chajia.sql.gz", "README.md")
foreach ($file in $DbFiles) {
    $src = Join-Path $Root "deploy\database\$file"
    if (Test-Path $src) {
        Copy-Item -Path $src -Destination (Join-Path $Db $file) -Force
        Write-Host "pack: database $file"
    }
}

function Get-DirSizeBytes($path) {
    (Get-ChildItem -Path $path -Recurse -File -ErrorAction SilentlyContinue |
        Measure-Object -Property Length -Sum).Sum
}

$codeSize = [math]::Round((Get-DirSizeBytes $Code) / 1MB, 2)
$dbSize = [math]::Round((Get-DirSizeBytes $Db) / 1KB, 1)
$fileCount = (Get-ChildItem -Path $Out -Recurse -File).Count

Write-Host ""
Write-Host "pack: normalize shell scripts (CRLF -> LF)"
Convert-ShFilesToLf -BaseDir $Code

Write-Host ""
Write-Host "pack: verify dynamic API routes"
Test-RequiredRoutes -BaseDir $Code

# 安全：上传包禁止夹带机密/构建产物（避免覆盖服务器 .env 或浪费磁盘）
$ForbiddenInCode = @(
    (Join-Path $Code ".env"),
    (Join-Path $Code "node_modules"),
    (Join-Path $Code ".next"),
    (Join-Path $Code ".rpa"),
    (Join-Path $Code ".git"),
    (Join-Path $Code ".dev")
)
foreach ($bad in $ForbiddenInCode) {
    if (Test-Path -LiteralPath $bad) {
        throw "pack: forbidden path present in 代码/: $bad"
    }
}

Write-Host ""
Write-Host "pack: done -> $Out"
Write-Host "pack: files=$fileCount codeMB=$codeSize dbKB=$dbSize"
Write-Host "pack: SAFETY ok (no .env/node_modules/.next/.rpa/.git in 代码/)"

# Restore top-level README (removed by clean)
$readmeSrc = Join-Path $Root "deploy\pack-readme-template.md"
if (Test-Path $readmeSrc) {
    Copy-Item $readmeSrc (Join-Path $Out "README.md") -Force
}

$auxDir = Join-Path $Out ([char]0x8f85 + [char]0x52a9 + [char]0x811a + [char]0x672c)
New-Item -ItemType Directory -Path $auxDir -Force | Out-Null
$testScriptsSrc = Join-Path $Root "deploy\test-scripts"
if (Test-Path $testScriptsSrc) {
    Get-ChildItem -Path $testScriptsSrc -Filter "*.sh" -File | ForEach-Object {
        Copy-Item -LiteralPath $_.FullName -Destination (Join-Path $auxDir $_.Name) -Force
        Write-Host "pack: aux $($_.Name)"
    }
    Convert-ShFilesToLf -BaseDir $auxDir
}

$releaseNotesCandidates = @(
    (Join-Path $Root "deploy\RELEASE-NOTES.md"),
    (Join-Path $Root "deploy\服务器更新说明.md")
)
foreach ($releaseNotesSrc in $releaseNotesCandidates) {
    if (-not (Test-Path -LiteralPath $releaseNotesSrc)) { continue }
    $releaseNotesDst = Join-Path $Out "RELEASE-NOTES.md"
    Copy-Item -LiteralPath $releaseNotesSrc -Destination $releaseNotesDst -Force
    Write-Host "pack: RELEASE-NOTES.md"
    break
}
