param(
    [string]$Server = $env:ONEC_SQL_SERVER,
    [string]$Database = $env:ONEC_SQL_DATABASE,
    [string]$User = $env:ONEC_SQL_USER,
    [string]$Password = $env:ONEC_SQL_PASSWORD,
    [ValidateSet("Config", "ConfigSave", "ConfigCAS", "ConfigCASSave", "Params")]
    [string]$Table = "Params",
    [string]$OutputPath = $env:ONEC_SQL_EXPORT_PATH,
    [string[]]$FileName,
    [int]$Limit = 0,
    [int]$CommandTimeoutSeconds = 0
)

Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"

if (-not $Server) { throw "Server is required. Use -Server or ONEC_SQL_SERVER." }
if (-not $Database) { throw "Database is required. Use -Database or ONEC_SQL_DATABASE." }
if (-not $User) { throw "User is required. Use -User or ONEC_SQL_USER." }
if (-not $Password) { throw "Password is required. Use -Password or ONEC_SQL_PASSWORD." }
if (-not $OutputPath) { throw "OutputPath is required. Use -OutputPath or ONEC_SQL_EXPORT_PATH." }

$resolvedOutput = [System.IO.Path]::GetFullPath($OutputPath)
New-Item -ItemType Directory -Force -Path $resolvedOutput | Out-Null

$connectionString = "Server=$Server;Database=$Database;User ID=$User;Password=$Password;Encrypt=False;TrustServerCertificate=True;Application Name=Codex 1C SQL File Export;"
$connection = [System.Data.SqlClient.SqlConnection]::new($connectionString)
$command = $connection.CreateCommand()
$command.CommandTimeout = $CommandTimeoutSeconds

$where = ""
if ($FileName -and $FileName.Count -gt 0) {
    $placeholders = @()
    for ($i = 0; $i -lt $FileName.Count; $i++) {
        $name = "@file$i"
        $placeholders += $name
        $null = $command.Parameters.Add($name, [System.Data.SqlDbType]::NVarChar, 512)
        $command.Parameters[$name].Value = $FileName[$i]
    }
    $where = "WHERE FileName IN ($($placeholders -join ', '))"
}

$top = ""
if ($Limit -gt 0 -and -not $where) {
    $top = "TOP ($Limit)"
}

$command.CommandText = "SELECT $top FileName, PartNo, BinaryData FROM dbo.[$Table] $where ORDER BY FileName, PartNo"

$fileCount = 0
$chunkCount = 0
$byteCount = [int64]0
$currentName = $null
$currentStream = $null
$manifest = @()

function Close-CurrentStream {
    if ($script:currentStream -ne $null) {
        $script:currentStream.Dispose()
        $script:currentStream = $null
        $script:fileCount++
    }
}

try {
    $connection.Open()
    $reader = $command.ExecuteReader([System.Data.CommandBehavior]::SequentialAccess)
    try {
        while ($reader.Read()) {
            $file = [string]$reader.GetValue(0)
            if ([System.IO.Path]::GetFileName($file) -ne $file) {
                throw "Unsafe FileName value from SQL: $file"
            }

            if ($currentName -ne $file) {
                Close-CurrentStream
                $currentName = $file
                $target = Join-Path $resolvedOutput $file
                $currentStream = [System.IO.File]::Open($target, [System.IO.FileMode]::Create, [System.IO.FileAccess]::Write, [System.IO.FileShare]::None)
                $manifest += [pscustomobject]@{ file_name = $file; path = $target }
            }

            $buffer = [byte[]]$reader.GetValue(2)
            $currentStream.Write($buffer, 0, $buffer.Length)
            $chunkCount++
            $byteCount += $buffer.Length
        }
    }
    finally {
        if ($reader -ne $null) { $reader.Dispose() }
        Close-CurrentStream
    }
}
finally {
    if ($connection -ne $null) { $connection.Dispose() }
}

$result = [pscustomobject]@{
    table = $Table
    output_path = $resolvedOutput
    files = $fileCount
    chunks = $chunkCount
    bytes = $byteCount
    exported = $manifest
}

$result | ConvertTo-Json -Depth 5
