#Requires -Version 5.1
# EasyAI Windows 一键部署脚本
# 仅支持 IP 访问（本地/局域网），不含域名与 HTTPS
# 一行命令: git clone https://git.51easyai.com/wangbo/easyai; cd easyai; .\start.ps1

# 设置控制台编码为 UTF-8，确保中文正确显示
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$OutputEncoding = [System.Text.Encoding]::UTF8

$ErrorActionPreference = "Stop"
$script:LogDir = $PSScriptRoot
if (-not $script:LogDir) { $script:LogDir = $env:TEMP }
$script:LogFile = Join-Path $script:LogDir "start.ps1.log"
# 尽早写入启动标记，便于闪退时排查
try { "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] === 脚本加载 ===" | Out-File -FilePath $script:LogFile -Append -Encoding utf8 } catch { }

function Write-Log {
    param([string]$Msg)
    try {
        $line = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] $Msg"
        Add-Content -Path $script:LogFile -Value $line -Encoding UTF8 -ErrorAction SilentlyContinue
    } catch { }
}

trap {
    $errMsg = $_.Exception.Message
    $errStack = if ($_.ScriptStackTrace) { " | 堆栈: $($_.ScriptStackTrace)" } else { "" }
    try {
        $logPath = $script:LogFile
        if (-not $logPath) { $logPath = Join-Path $env:TEMP "start.ps1.log" }
        $logLine = "[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] 错误退出: $errMsg$errStack"
        Add-Content -Path $logPath -Value $logLine -Encoding UTF8 -ErrorAction SilentlyContinue
    } catch { }
    try {
        Write-Host ""
        Write-Host "========================================" -ForegroundColor Red
        Write-Host "  发生错误，详见日志: $script:LogFile" -ForegroundColor Red
        Write-Host "========================================" -ForegroundColor Red
        Write-Host $errMsg -ForegroundColor Red
        if ($errStack) { Write-Host $errStack }
    } catch { }
    Wait-ForExit
    exit 1
}

# 结束时保持窗口不关闭，便于查看输出和 Debug（CI 环境自动跳过）
function Wait-ForExit {
    if ($env:CI -eq "true") { return }
    Write-Host ""
    Read-Host "按 Enter 键退出"
}

# 仅配置模式：DEPLOY_DRY_RUN=1 只生成配置文件，不执行 Docker 安装和启动
$script:DeployDryRun = if ($env:DEPLOY_DRY_RUN -eq "1") { $true } else { $false }
$script:DeployIP = ""
$script:DeployModeSkip = $false

# Docker Desktop for Windows 下载链接
$DockerDesktopUrl = "https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe"

function Write-Step { param($Msg) Write-Host $Msg }
function Write-Ok   { param($Msg) Write-Host "   ✓ $Msg" -ForegroundColor Green }
function Write-Err  { param($Msg) Write-Host "❌ $Msg" -ForegroundColor Red; throw $Msg }
function Write-Warn { param($Msg) Write-Host "⚠️ $Msg" -ForegroundColor Yellow }

# ==================== 项目初始化 ====================
function Init-ProjectDir {
    $scriptDir = $PSScriptRoot
    if (-not $scriptDir) { throw "无法获取脚本所在目录。请在 easyai 目录下打开 PowerShell 执行: .\start.ps1" }
    $composePath = Join-Path $scriptDir "docker-compose.yml"
    if (Test-Path $composePath) {
        Set-Location $scriptDir
        Write-Step "📁 项目目录: $scriptDir"
        return
    }
    Write-Err "未找到 docker-compose.yml，请在 easyai 项目目录下运行 start.ps1"
}

# ==================== 配置问答 ====================
function Run-DeployQuestions {
    Write-Host ""
    Write-Host "================================"
    Write-Host "  EasyAI 部署配置（Windows）"
    Write-Host "================================"
    Write-Host ""

    # 非交互模式：环境变量 DEPLOY_IP 已设置
    if ($env:DEPLOY_IP) {
        $script:DeployIP = $env:DEPLOY_IP.Trim()
        Write-Step "使用环境变量: DEPLOY_IP=$($script:DeployIP)"
        return
    }

    Write-Host "请选择访问方式："
    Write-Host "   [1] 本地访问 (127.0.0.1) - 仅本机访问，无需放行端口"
    Write-Host "   [2] 局域网访问 - 同网段设备访问，需放行 3001、3002、3003 端口"
    $choice = Read-Host "请选择 [1/2]"

    switch ($choice) {
        "1" {
            $script:DeployIP = "127.0.0.1"
            Write-Step "已选择本地访问"
        }
        "2" {
            # 自动获取局域网 IP（排除回环和 APIPA 地址）
            $detectedIp = $null
            try {
                $addrs = Get-NetIPAddress -AddressFamily IPv4 -ErrorAction SilentlyContinue | Where-Object {
                    $_.InterfaceAlias -notlike "*Loopback*" -and
                    $_.IPAddress -notmatch "^127\." -and
                    $_.IPAddress -notmatch "^169\.254\."
                }
                $detectedIp = ($addrs | Sort-Object InterfaceIndex | Select-Object -First 1).IPAddress
            } catch { }
            if ($detectedIp) {
                $defaultHint = "回车使用 [$detectedIp]"
                Write-Host "   检测到局域网 IP: $detectedIp"
                $inputIp = Read-Host "请输入本机局域网 IP（$defaultHint 或手动输入）"
                $inputIp = $inputIp.Trim()
                $script:DeployIP = if ([string]::IsNullOrWhiteSpace($inputIp)) { $detectedIp } else { $inputIp }
            } else {
                Write-Host "   提示: 未自动检测到局域网 IP，可在本机运行 ipconfig 查看"
                $lanIp = Read-Host "请输入本机局域网 IP 地址"
                $lanIp = $lanIp.Trim()
                if ([string]::IsNullOrWhiteSpace($lanIp)) { Write-Err "IP 不能为空" }
                $script:DeployIP = $lanIp
            }
            Write-Warn "请确保防火墙已放行 3001、3002、3003 端口"
        }
        default {
            Write-Err "无效选择"
        }
    }
}

# ==================== 生成配置文件 ====================
function Setup-EnvFiles {
    Write-Host ""
    Write-Step "📝 配置环境文件..."

    # 复制 .env.tools、.env.ASG、.env.AMS（无 example 后缀的从 .sample 生成）
    if (-not (Test-Path ".env.tools")) {
        Copy-Item ".env.tools.sample" ".env.tools"
        Write-Ok ".env.tools"
    }
    if (-not (Test-Path ".env.ASG")) {
        Copy-Item ".env.ASG.sample" ".env.ASG"
        Write-Ok ".env.ASG"
    }
    if (-not (Test-Path ".env.AMS")) {
        Copy-Item ".env.AMS.sample" ".env.AMS"
        Write-Ok ".env.AMS"
    }

    # 配置 .env
    if (-not (Test-Path ".env")) {
        Copy-Item ".env.sample" ".env"
    }

    $content = Get-Content ".env" -Raw -Encoding UTF8
    if (-not $content) { $content = "" }

    $content = $content -replace '(?m)^NUXT_PUBLIC_BASE_APIURL=.*', "NUXT_PUBLIC_BASE_APIURL=http://$($script:DeployIP):3001"
    $content = $content -replace '(?m)^NUXT_PUBLIC_BASE_SOCKETURL=.*', "NUXT_PUBLIC_BASE_SOCKETURL=ws://$($script:DeployIP):3002"
    $content = $content -replace '(?m)^NUXT_PUBLIC_SG_APIURL=.*', "NUXT_PUBLIC_SG_APIURL=http://$($script:DeployIP):3003"

    # 保持文件末尾换行
    if ($content -and -not $content.EndsWith("`n")) { $content += "`n" }
    $envPath = Join-Path (Get-Location) ".env"
    [System.IO.File]::WriteAllText($envPath, $content, [System.Text.UTF8Encoding]::new($false))
    Write-Ok ".env 已配置为 IP 模式 ($($script:DeployIP))"
}

# ==================== Docker 检测 ====================
function Test-DockerInstalled {
    $docker = Get-Command docker -ErrorAction SilentlyContinue
    if (-not $docker) { return $false }
    try {
        $null = & docker --version 2>&1
        return $true
    } catch {
        return $false
    }
}

# 检查 Docker 引擎是否已启动并可响应（带超时，避免 docker info 卡住）
function Test-DockerRunning {
    param([int]$TimeoutSeconds = 10)
    try {
        $psi = New-Object System.Diagnostics.ProcessStartInfo
        $psi.FileName = "docker"
        $psi.Arguments = "info"
        $psi.RedirectStandardOutput = $true
        $psi.RedirectStandardError = $true
        $psi.UseShellExecute = $false
        $psi.CreateNoWindow = $true
        $p = [System.Diagnostics.Process]::Start($psi)
        $exited = $p.WaitForExit($TimeoutSeconds * 1000)
        if (-not $exited) {
            try { $p.Kill() } catch { }
            return $false
        }
        return ($p.ExitCode -eq 0)
    } catch {
        return $false
    }
}

# 启动 Docker Desktop 并等待就绪
function Start-DockerDesktopAndWait {
    param(
        [int]$MaxWaitSeconds = 120,
        [int]$CheckIntervalSeconds = 5
    )

    Write-Step "Docker 未运行，正在启动 Docker Desktop..."
    $dockerDesktopPath = "${env:ProgramFiles}\Docker\Docker\Docker Desktop.exe"
    if (-not (Test-Path $dockerDesktopPath)) {
        $dockerDesktopPath = "${env:ProgramFiles(x86)}\Docker\Docker\Docker Desktop.exe"
    }
    if (-not (Test-Path $dockerDesktopPath)) {
        Write-Warn "未找到 Docker Desktop 可执行文件"
        Write-Host "请手动启动 Docker Desktop 后重新运行本脚本。"
        return $false
    }

    # 尝试使用 docker desktop start（Docker Desktop 4.38+ 支持）
    $useCli = $false
    try {
        $null = & docker desktop start 2>&1
        $useCli = $true
        Write-Step "已通过 docker desktop start 发送启动命令"
    } catch {
        # 回退到直接启动进程
    }

    if (-not $useCli) {
        Start-Process -FilePath $dockerDesktopPath -WindowStyle Hidden
        Write-Step "已启动 Docker Desktop 进程"
    }

    Write-Host "等待 Docker 引擎就绪（最长 $MaxWaitSeconds 秒）..." -ForegroundColor Cyan
    $elapsed = 0
    while ($elapsed -lt $MaxWaitSeconds) {
        Start-Sleep -Seconds $CheckIntervalSeconds
        $elapsed += $CheckIntervalSeconds
        Write-Host "   已等待 $elapsed 秒..." -NoNewline
        if (Test-DockerRunning) {
            Write-Host " 完成" -ForegroundColor Green
            Write-Ok "Docker 已就绪"
            return $true
        }
        Write-Host ""
    }

    Write-Warn "等待超时，Docker 可能仍在启动中"
    Write-Host "请稍后手动执行 docker info 确认，或重新运行本脚本。"
    return $false
}

# 确保 Docker 已安装且运行，若未运行则尝试启动
function Ensure-DockerRunning {
    if (-not (Test-DockerInstalled)) {
        return $false
    }
    if (Test-DockerRunning) {
        Write-Ok "Docker 引擎已运行"
        $v = docker --version 2>$null
        if ($v) { Write-Host "   $v" }
        return $true
    }
    return (Start-DockerDesktopAndWait)
}

function Install-DockerDesktop {
    $isWin = ($env:OS -eq "Windows_NT") -or ($PSVersionTable.PSVersion.Major -ge 6 -and $IsWindows)
    if (-not $isWin) {
        Write-Warn "自动安装仅支持 Windows。请手动安装 Docker Desktop。"
        Write-Host "下载地址: $DockerDesktopUrl"
        Wait-ForExit; exit 1
    }

    # 优先 winget，其次 Chocolatey
    if (Get-Command winget -ErrorAction SilentlyContinue) {
        Write-Step "正在通过 winget 安装 Docker Desktop for Windows..."
        winget install --id Docker.DockerDesktop -e --accept-source-agreements --accept-package-agreements
        if ($LASTEXITCODE -eq 0) {
            Write-Ok "安装已启动。安装完成后请重启终端，然后重新运行本脚本。"
            exit 0
        }
    }
    if (Get-Command choco -ErrorAction SilentlyContinue) {
        Write-Step "正在通过 Chocolatey 安装 Docker Desktop for Windows..."
        choco install docker-desktop -y
        if ($LASTEXITCODE -eq 0) {
            Write-Ok "安装已启动。安装完成后请重启终端，然后重新运行本脚本。"
            exit 0
        }
    }

    Write-Warn "未找到 winget 或 Chocolatey，请手动安装 Docker Desktop。"
    Write-Host "下载地址: $DockerDesktopUrl"
    Wait-ForExit; exit 1
}

function Test-Docker {
    Write-Host ""
    Write-Host "================================"
    Write-Host "  Docker 检查"
    Write-Host "================================"
    Write-Host ""

    if (Test-DockerInstalled) {
        Write-Ok "Docker 已安装"
        # 检查并确保 Docker 引擎运行
        if (-not (Ensure-DockerRunning)) {
            Write-Warn "Docker 未能自动启动"
            Write-Host ""
            Write-Host "请手动启动 Docker Desktop，确认其完全启动后再重新运行本脚本。"
            Wait-ForExit
            exit 1
        }
        return
    }

    Write-Warn "未检测到 Docker Desktop for Windows"
    Write-Host ""
    Write-Host "请选择："
    Write-Host "   [1] 手动安装 - 打开下载页面，退出脚本"
    Write-Host "   [2] 自动安装 - 通过 winget 或 Chocolatey 安装（需管理员权限）"
    $choice = Read-Host "请选择 [1/2]"

    switch ($choice) {
        "1" {
            Write-Host ""
            Write-Host "Docker Desktop for Windows 下载地址:"
            Write-Host "  $DockerDesktopUrl"
            Write-Host ""
            Write-Host "安装完成后请重新运行本脚本。"
            if ($env:OS -eq "Windows_NT") {
                Start-Process $DockerDesktopUrl
            }
            Wait-ForExit; exit 1
        }
        "2" {
            Install-DockerDesktop
        }
        default {
            Write-Err "无效选择"
        }
    }
}

# ==================== 启动服务 ====================
function Start-Services {
    Write-Host ""
    Write-Step "🚀 启动 EasyAI 服务..."

    $hasComposeV2 = $false
    try {
        $null = docker compose version 2>&1
        $hasComposeV2 = $true
    } catch { }

    if ($hasComposeV2) {
        docker compose pull
        if ($LASTEXITCODE -ne 0) { Write-Err "docker compose pull 失败" }
        docker compose up -d
        if ($LASTEXITCODE -ne 0) { Write-Err "docker compose up 失败" }
    } else {
        docker-compose pull
        if ($LASTEXITCODE -ne 0) { Write-Err "docker-compose pull 失败" }
        docker-compose up -d
        if ($LASTEXITCODE -ne 0) { Write-Err "docker-compose up 失败" }
    }
    Write-Host "🎉 EasyAI 应用启动成功"
}

# ==================== 主流程 ====================
function Main {
    Init-ProjectDir

    # 检查是否已有 .env 且非强制重新配置
    if ((Test-Path ".env") -and -not $env:DEPLOY_FORCE_RECONFIG -and -not $env:DEPLOY_IP) {
        Write-Step "📁 检测到已有 .env 配置"
        $reconfigure = Read-Host "是否重新配置访问方式？[y/N]"
        $trimmed = $reconfigure.Trim().ToLower()
        $startsWithY = ($trimmed.Length -gt 0) -and ($trimmed[0] -eq [char]121)
        if (-not $startsWithY) {
            Write-Step "使用现有配置继续..."
            # 从现有 .env 读取 IP（用于最后输出）
            $line = Get-Content ".env" -Encoding UTF8 | Where-Object { $_ -like "NUXT_PUBLIC_BASE_APIURL=*" } | Select-Object -First 1
            if ($line -and $line -like "*:3001*") {
                $prefix = ".*http://"
                $suffix = ":3001.*"
                $script:DeployIP = ($line -replace $prefix, "" -replace $suffix, "").Trim()
            }
            $script:DeployModeSkip = $true
        }
    }

    if (-not $script:DeployModeSkip) {
        Run-DeployQuestions
    }

    if (-not $script:DeployModeSkip) {
        Setup-EnvFiles
    } else {
        if (-not (Test-Path ".env.tools")) { Copy-Item ".env.tools.sample" ".env.tools" }
        if (-not (Test-Path ".env.ASG")) { Copy-Item ".env.ASG.sample" ".env.ASG" }
        if (-not (Test-Path ".env.AMS")) { Copy-Item ".env.AMS.sample" ".env.AMS" }
    }

    if ($script:DeployDryRun) {
        Write-Host ""
        Write-Warn "dry-run 模式：跳过 Docker 安装和服务启动"
        Write-Host "   配置文件已生成，可直接运行 .\start.ps1 完成部署"
    } else {
        Test-Docker
        Start-Services
    }

    Write-Host ""
    Write-Host "================================"
    Write-Host "  部署完成"
    Write-Host "================================"
    $accessIp = if ($script:DeployIP) { $script:DeployIP } else { "127.0.0.1" }
    Write-Host "访问地址: http://${accessIp}:3010"
    Write-Host "默认登录账户: admin"
    Write-Host "默认登录密码: 123456"
    Write-Host ""
    Wait-ForExit
}

Write-Log "=== 脚本启动 ==="
Write-Host "EasyAI Windows 部署脚本启动中..." -ForegroundColor Cyan
Write-Host "日志文件: $script:LogFile"
Write-Host ""
Main
Write-Log "=== 脚本正常结束 ==="
