param(
    [string]$RestDockerHost = "ssh://docker.cin.su",
    [string]$McpDockerHost = "ssh://docker.cin.su",
    [string]$RestComposePath = "core/deploy/docker/adapter-1c/compose.yaml",
    [string]$McpComposePath = "core/deploy/docker/adapter-1c-mcp/compose.yaml",
    [string]$RestEnvFile,
    [string]$McpEnvFile,
    [string]$RestServiceName = "adapter-1c-rest",
    [string]$RestAuditServiceName = "adapter-1c-audit",
    [string]$McpServiceName = "adapter-1c-mcp",
    [string]$McpAuditServiceName = "adapter-1c-mcp-audit",
    [string[]]$BaseId,
    [string]$AdapterUrl = "http://docker.cin.su:8011",
    [string]$McpUrl = "http://docker.cin.su:8021",
    [string]$ObjectRef,
    [string]$ObjectKind,
    [string]$ObjectName,
    [string]$ObjectGuid,
    [ValidateSet("ConfigSave", "ConfigCASSave")]
    [string]$SavedStateTable = "ConfigSave",
    [int]$TimeoutSec = 120,
    [switch]$NoBuild,
    [switch]$SkipRest,
    [switch]$SkipMcp,
    [switch]$SkipVerify,
    [switch]$SkipDrainCheck,
    [switch]$SkipWritePlanSafetySmoke,
    [switch]$SkipWriteRollbackSafetySmoke,
    [switch]$SkipSavedStateDiffSmoke,
    [switch]$SkipSavedStateWriteSmoke,
    [switch]$SkipCodeWriteSavedStateSmoke,
    [switch]$RequireSavedStateWriteSmoke,
    [switch]$RequireSelectorChainWritePlanComposition
)

$ErrorActionPreference = "Stop"

function Invoke-CheckedCommand {
    param(
        [string]$Label,
        [string[]]$Command
    )
    Write-Host "[run] $Label"
    $exe = $Command[0]
    $args = $Command[1..($Command.Length - 1)]
    Write-Host ("[cmd] {0} {1}" -f $exe, ($args -join " "))
    & $exe @args
    if ($LASTEXITCODE -ne 0) {
        throw "$Label failed with code=$LASTEXITCODE"
    }
}

function Invoke-ComposeUp {
    param(
        [string]$Label,
        [string]$DockerHost,
        [string]$ComposePath,
        [string]$EnvFile,
        [string]$ServiceName
    )
    $command = @("docker", "--host", $DockerHost, "compose", "-f", $ComposePath, "up", "-d", "--no-deps")
    if ($EnvFile) {
        $command = @("docker", "--host", $DockerHost, "compose", "--env-file", $EnvFile, "-f", $ComposePath, "up", "-d", "--no-deps")
    }
    if (-not $NoBuild) {
        $command += "--build"
    }
    $command += $ServiceName
    Invoke-CheckedCommand -Label $Label -Command $command
}

function Wait-RestAdapterIdle {
    if ($SkipDrainCheck) {
        Write-Host "[skip] REST drain check was explicitly skipped"
        return
    }
    $deadline = [DateTime]::UtcNow.AddSeconds($TimeoutSec)
    $lastIssue = ""
    while ([DateTime]::UtcNow -lt $deadline) {
        try {
            $health = Invoke-RestMethod -Method Get -Uri ($AdapterUrl.TrimEnd('/') + "/health") -TimeoutSec 10
            $runtime = $health.runtime
            if (-not $runtime) {
                Write-Warning "REST adapter is a legacy image without runtime drain telemetry; proceeding with this one transition deployment"
                return
            }
            if ($runtime -and $runtime.state -eq "ready" -and [int]$runtime.active_rpc_count -eq 0) {
                Write-Host "[ready] REST adapter has no active RPC calls"
                return
            }
            $lastIssue = "state=$($runtime.state) active_rpc_count=$($runtime.active_rpc_count)"
        } catch {
            $lastIssue = $_.Exception.Message
        }
        Start-Sleep -Seconds 2
    }
    throw "REST adapter did not become idle before deployment: $lastIssue. Re-run later or pass -SkipDrainCheck only after confirming no write is active."
}

function Ensure-RestServiceToken {
    if ($env:ONEC_ADAPTER_SERVICE_TOKEN) {
        return
    }
    if ($env:ONEC_ADAPTER_ALLOW_UNAUTHENTICATED_ADMIN -match '^(?i:true|1|yes|on)$') {
        Write-Host "[security] Test profile: deploying without REST service token because ONEC_ADAPTER_ALLOW_UNAUTHENTICATED_ADMIN is enabled"
        return
    }
    $inspectJson = & docker --host $RestDockerHost inspect $RestServiceName 2>$null
    if ($LASTEXITCODE -eq 0 -and $inspectJson) {
        $container = @($inspectJson | ConvertFrom-Json)[0]
        $tokenEntry = @($container.Config.Env | Where-Object { $_ -like "ONEC_ADAPTER_SERVICE_TOKEN=*" })[0]
        if ($tokenEntry) {
            $existingToken = ($tokenEntry -split "=", 2)[1]
            if ($existingToken) {
                $env:ONEC_ADAPTER_SERVICE_TOKEN = $existingToken
                Write-Host "[security] Reusing the existing REST service token without printing or persisting it"
                return
            }
        }
    }
    throw "ONEC_ADAPTER_SERVICE_TOKEN is empty and no protected existing REST container token can be reused. Refusing to deploy an unauthenticated adapter."
}

function Write-ContainerSummary {
    param(
        [string]$Label,
        [string]$DockerHost,
        [string]$ServiceName
    )
    Write-Host "[status] $Label"
    & docker --host $DockerHost ps --filter "name=$ServiceName" --format "name={{.Names}} image={{.Image}} status={{.Status}} ports={{.Ports}}"
    if ($LASTEXITCODE -ne 0) {
        throw "$Label status failed with code=$LASTEXITCODE"
    }
}

function Get-DuplicateValues {
    param(
        [string[]]$Values
    )
    $seen = @{}
    $duplicates = New-Object System.Collections.Generic.HashSet[string]
    foreach ($value in $Values) {
        if ($seen.ContainsKey($value)) {
            [void]$duplicates.Add($value)
        } else {
            $seen[$value] = $true
        }
    }
    return @($duplicates | Sort-Object)
}

function Normalize-BaseIds {
    param(
        [string[]]$Values
    )
    $result = @()
    foreach ($value in $Values) {
        foreach ($part in ($value -split ",")) {
            $trimmed = $part.Trim()
            if ($trimmed) {
                $result += $trimmed
            }
        }
    }
    return $result
}

try {
    if (-not $SkipRest) {
        if (-not $env:ONEC_ADAPTER_ALLOW_UNAUTHENTICATED_ADMIN) {
            $env:ONEC_ADAPTER_ALLOW_UNAUTHENTICATED_ADMIN = "true"
        }
        Ensure-RestServiceToken
        Wait-RestAdapterIdle
        Invoke-ComposeUp `
            -Label "Deploy REST adapter" `
            -DockerHost $RestDockerHost `
            -ComposePath $RestComposePath `
            -EnvFile $RestEnvFile `
            -ServiceName $RestServiceName
        Write-ContainerSummary `
            -Label "REST adapter container" `
            -DockerHost $RestDockerHost `
            -ServiceName $RestServiceName
        Invoke-ComposeUp `
            -Label "Deploy REST audit analyzer" `
            -DockerHost $RestDockerHost `
            -ComposePath $RestComposePath `
            -EnvFile $RestEnvFile `
            -ServiceName $RestAuditServiceName
        Write-ContainerSummary `
            -Label "REST audit analyzer container" `
            -DockerHost $RestDockerHost `
            -ServiceName $RestAuditServiceName
    }

    if (-not $SkipMcp) {
        Invoke-ComposeUp `
            -Label "Deploy MCP proxy" `
            -DockerHost $McpDockerHost `
            -ComposePath $McpComposePath `
            -EnvFile $McpEnvFile `
            -ServiceName $McpServiceName
        Write-ContainerSummary `
            -Label "MCP proxy container" `
            -DockerHost $McpDockerHost `
            -ServiceName $McpServiceName
        Invoke-ComposeUp `
            -Label "Deploy MCP audit analyzer" `
            -DockerHost $McpDockerHost `
            -ComposePath $McpComposePath `
            -EnvFile $McpEnvFile `
            -ServiceName $McpAuditServiceName
        Write-ContainerSummary `
            -Label "MCP audit analyzer container" `
            -DockerHost $McpDockerHost `
            -ServiceName $McpAuditServiceName
    }

    if (-not $SkipVerify) {
        $baseIds = @(Normalize-BaseIds -Values $BaseId)
        if (-not $baseIds) {
            Write-Host "[skip] BaseId was not provided; live verification skipped"
        } else {
            $duplicateBaseIds = @(Get-DuplicateValues -Values $baseIds)
            if ($duplicateBaseIds) {
                throw "Duplicate BaseId value(s): $($duplicateBaseIds -join ', ')"
            }
            $verifyCommand = @(
                "powershell",
                "-NoProfile",
                "-ExecutionPolicy",
                "Bypass",
                "-File",
                "scripts/verify_1c_adapter_deployment.ps1",
                "-BaseId"
            )
            $verifyCommand += ($baseIds -join ",")
            $verifyCommand += @(
                "-AdapterUrl",
                $AdapterUrl,
                "-McpUrl",
                $McpUrl,
                "-TimeoutSec",
                $TimeoutSec.ToString(),
                "-SavedStateTable",
                $SavedStateTable
            )
            if ($ObjectRef) {
                $verifyCommand += @("-ObjectRef", $ObjectRef)
            }
            if ($ObjectKind) {
                $verifyCommand += @("-ObjectKind", $ObjectKind)
            }
            if ($ObjectName) {
                $verifyCommand += @("-ObjectName", $ObjectName)
            }
            if ($ObjectGuid) {
                $verifyCommand += @("-ObjectGuid", $ObjectGuid)
            }
            if ($SkipRest) {
                $verifyCommand += "-SkipRest"
            }
            if ($SkipMcp) {
                $verifyCommand += "-SkipMcp"
            }
            if ($SkipWritePlanSafetySmoke) {
                $verifyCommand += "-SkipWritePlanSafetySmoke"
            }
            if ($SkipWriteRollbackSafetySmoke) {
                $verifyCommand += "-SkipWriteRollbackSafetySmoke"
            }
            if ($SkipSavedStateDiffSmoke) {
                $verifyCommand += "-SkipSavedStateDiffSmoke"
            }
            if ($SkipSavedStateWriteSmoke) {
                $verifyCommand += "-SkipSavedStateWriteSmoke"
            }
            if ($SkipCodeWriteSavedStateSmoke) {
                $verifyCommand += "-SkipCodeWriteSavedStateSmoke"
            }
            if ($RequireSavedStateWriteSmoke) {
                $verifyCommand += "-RequireSavedStateWriteSmoke"
            }
            if ($RequireSelectorChainWritePlanComposition) {
                $verifyCommand += "-RequireSelectorChainWritePlanComposition"
            }
            Invoke-CheckedCommand -Label "Verify 1C adapter deployment" -Command $verifyCommand
        }
    }

    Write-Host "[done] 1C adapter stack deploy finished"
    exit 0
} catch {
    Write-Error $_
    exit 1
}
