# 构建 api / player / admin 生产镜像并导出为 tar
# 用法（在项目根目录或本目录执行均可）:
#   .\docs\docker\build-and-export-images.ps1
#   .\docs\docker\build-and-export-images.ps1 -Service admin
#   .\docs\docker\build-and-export-images.ps1 -Tag v1.2.3
#   .\docs\docker\build-and-export-images.ps1 -UseCache
#   .\docs\docker\build-and-export-images.ps1 -ExportOnly

param(
  [ValidateSet("all", "api", "player", "admin")]
  [string]$Service = "all",
  [string]$Tag = $env:IMAGE_TAG,
  [switch]$UseCache,
  [switch]$ExportOnly,
  [string]$Output = ""
)

$ErrorActionPreference = "Stop"
$Root = (Resolve-Path (Join-Path $PSScriptRoot "../..")).Path
Set-Location $Root

$ComposeFile = "docker-compose.prod.yml"
$EnvFile = ".env.docker"
$AllServices = @("api", "player", "admin")

function Get-DefaultTag { "latest" }

function Get-DefaultOutput {
  param([string]$Svc, [string]$ImageTag)
  if ($Svc -eq "all") { return "thebet365-images-$ImageTag.tar" }
  return "thebet365-$Svc-$ImageTag.tar"
}

function Assert-ImageTag {
  param([string]$Value)
  if ($Value -notmatch '^[A-Za-z0-9_][A-Za-z0-9_.-]{0,127}$') {
    throw "镜像 tag 不合法: $Value"
  }
}

if (-not (Test-Path $ComposeFile)) {
  throw "未找到 $ComposeFile（当前目录: $Root）"
}

if (-not (Test-Path $EnvFile)) {
  if (Test-Path ".env.docker.example") {
    Write-Warning "未找到 .env.docker，使用 .env.docker.example（生产请复制并修改密钥）"
    $EnvFile = ".env.docker.example"
  } else {
    throw "未找到 .env.docker 或 .env.docker.example"
  }
}

if ([string]::IsNullOrWhiteSpace($Tag)) {
  $Tag = Get-DefaultTag
}
Assert-ImageTag $Tag

$BuildServices = if ($Service -eq "all") { $AllServices } else { @($Service) }
$SaveImages = $BuildServices | ForEach-Object { "thebet365-${_}:${Tag}" }

if ([string]::IsNullOrWhiteSpace($Output)) {
  $Output = Get-DefaultOutput -Svc $Service -ImageTag $Tag
}

if (-not $ExportOnly) {
  Write-Host "==> 构建镜像: $($BuildServices -join ', ') (tag: $Tag)"
  $buildArgs = @(
    "compose", "-f", $ComposeFile, "--env-file", $EnvFile, "build"
  )
  if (-not $UseCache) {
    $buildArgs += "--no-cache"
  }
  $buildArgs += $BuildServices
  $oldImageTag = $env:IMAGE_TAG
  try {
    $env:IMAGE_TAG = $Tag
    & docker @buildArgs
    if ($LASTEXITCODE -ne 0) { throw "docker build 失败，退出码 $LASTEXITCODE" }
  } finally {
    $env:IMAGE_TAG = $oldImageTag
  }
}

$OutputPath = if ([System.IO.Path]::IsPathRooted($Output)) { $Output } else { Join-Path $Root $Output }

Write-Host "==> 导出镜像 ($Service) -> $OutputPath"
& docker save @SaveImages -o $OutputPath
if ($LASTEXITCODE -ne 0) { throw "docker save 失败，退出码 $LASTEXITCODE" }

$manifestPath = if ($OutputPath.EndsWith(".tar")) {
  $OutputPath.Substring(0, $OutputPath.Length - 4) + ".manifest.txt"
} else {
  "$OutputPath.manifest.txt"
}

$gitCommit = "unknown"
$gitDirty = "unknown"
try {
  $gitCommit = (& git rev-parse HEAD 2>$null).Trim()
  & git diff --quiet
  $diffExit = $LASTEXITCODE
  & git diff --cached --quiet
  $cachedExit = $LASTEXITCODE
  $gitDirty = if ($diffExit -eq 0 -and $cachedExit -eq 0) { "false" } else { "true" }
} catch {}

$tarHash = (Get-FileHash -Algorithm SHA256 $OutputPath).Hash.ToLowerInvariant()
$manifestLines = @(
  "tag=$Tag"
  "service=$Service"
  "built_at=$((Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ"))"
  "git_commit=$gitCommit"
  "git_dirty=$gitDirty"
  "tar=$([System.IO.Path]::GetFileName($OutputPath))"
  "tar_sha256=$tarHash"
)
foreach ($svc in $BuildServices) {
  $imageId = (& docker image inspect "thebet365-${svc}:${Tag}" --format "{{.Id}}").Trim()
  if ($Service -eq "all") {
    $manifestLines += "${svc}_image=thebet365-${svc}:${Tag}"
    $manifestLines += "${svc}_image_id=$imageId"
  } else {
    $manifestLines += "image=thebet365-${svc}:${Tag}"
    $manifestLines += "image_id=$imageId"
  }
}
$manifestLines | Set-Content -Encoding UTF8 $manifestPath

$sizeMb = [math]::Round((Get-Item $OutputPath).Length / 1MB, 2)
Write-Host "完成: $OutputPath (${sizeMb} MB)"
Write-Host "Manifest: $manifestPath"

$recreate = if ($Service -eq "all") { "api player admin" } else { $Service }
Write-Host @"

服务器导入并重建:
  docker load -i $([System.IO.Path]::GetFileName($OutputPath))
  docker compose -f docker-compose.prod.yml --env-file .env.docker up -d --force-recreate $recreate
"@
