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,
    [string]$OutputDir = "reports\1c-sql\saved-state-object-report",
    [string]$BaseMetadataDir = "reports\1c-sql\upo\structured-metadata-all-kinds",
    [string]$ExtensionGuidIndex = "reports\1c-sql\upo\xml-guid-index-extensions.json",
    [string]$ExtensionManifestSummary = "reports\1c-sql\upo\extension-manifest-xml-part-summary.json",
    [string]$ConfigCASAllDir = "reports\1c-sql\upo\ConfigCAS-all",
    [string]$Python = "python",
    [string]$MarkdownOutput = "",
    [int]$MarkdownMaxDiffLines = 80,
    [switch]$SkipMarkdown
)

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." }

$repoRoot = (Resolve-Path ".").ProviderPath
$resolvedOutput = [System.IO.Path]::GetFullPath($OutputDir)
New-Item -ItemType Directory -Force -Path $resolvedOutput | Out-Null

$comparisonPath = Join-Path $resolvedOutput "saved-state-object-comparison.json"
$detailPath = Join-Path $resolvedOutput "saved-state-object-details.json"
$checkPath = Join-Path $resolvedOutput "saved-state-object-report-check.json"
$configSaveDir = Join-Path $resolvedOutput "ConfigSave"
$configCASSaveDir = Join-Path $resolvedOutput "ConfigCASSave"
$activeConfigDir = Join-Path $resolvedOutput "ActiveConfig"
$activeConfigCASDir = Join-Path $resolvedOutput "ActiveConfigCAS"

foreach ($dir in @($configSaveDir, $configCASSaveDir, $activeConfigDir, $activeConfigCASDir)) {
    New-Item -ItemType Directory -Force -Path $dir | Out-Null
}

function Invoke-Step {
    param(
        [string]$Name,
        [scriptblock]$Script
    )
    $started = Get-Date
    & $Script
    [pscustomobject]@{
        name = $Name
        started_at = $started.ToString("o")
        finished_at = (Get-Date).ToString("o")
        passed = $true
    }
}

function Read-JsonFile {
    param([string]$Path)
    return Get-Content -LiteralPath $Path -Raw -Encoding UTF8 | ConvertFrom-Json
}

function Export-TableFiles {
    param(
        [string]$Table,
        [string]$TargetDir,
        [string[]]$FileName
    )
    $args = @(
        "-NoProfile", "-ExecutionPolicy", "Bypass",
        "-File", "scripts\export_1c_sql_files.ps1",
        "-Server", $Server,
        "-Database", $Database,
        "-User", $User,
        "-Password", $Password,
        "-Table", $Table,
        "-OutputPath", $TargetDir
    )
    [object[]]$normalizedFileName = @($FileName)
    if (($normalizedFileName | Measure-Object).Count -gt 0) {
        $args += "-FileName"
        $args += $normalizedFileName
    }
    $json = & powershell @args
    if ($LASTEXITCODE -ne 0) {
        throw "Export failed for $Table."
    }
    return ($json | Out-String | ConvertFrom-Json)
}

function Get-JsonProperty {
    param(
        [object]$Object,
        [string]$Name,
        [object]$Default = $null
    )
    if ($null -eq $Object) { return $Default }
    if ($Object.PSObject.Properties.Name -contains $Name) {
        return $Object.$Name
    }
    return $Default
}

function Join-LimitedValues {
    param(
        [object[]]$Values,
        [int]$Limit = 12
    )
    $items = @($Values | Where-Object { $null -ne $_ -and [string]$_ -ne "" } | Select-Object -First $Limit)
    return ,$items
}

function New-AgentSummary {
    param(
        [object]$Comparison,
        [object]$Detail
    )
    $detailByName = @{}
    foreach ($item in @($Detail.object_details)) {
        $fullName = [string](Get-JsonProperty -Object $item -Name "full_name" -Default "")
        if ($fullName) { $detailByName[$fullName] = $item }
    }

    $objects = @()
    foreach ($change in @($Comparison.object_changes)) {
        $fullName = [string](Get-JsonProperty -Object $change -Name "full_name" -Default (Get-JsonProperty -Object $change -Name "name" -Default ""))
        $item = $null
        if ($fullName -and $detailByName.ContainsKey($fullName)) {
            $item = $detailByName[$fullName]
        }

        $parts = @()
        $addedTerms = @()
        $removedTerms = @()
        $textDiffParts = 0
        $activeMissingParts = 0
        foreach ($part in @((Get-JsonProperty -Object $item -Name "details" -Default @()))) {
            $payload = Get-JsonProperty -Object $part -Name "payload" -Default $null
            $diff = Get-JsonProperty -Object $payload -Name "text_diff" -Default $null
            $semanticHints = Get-JsonProperty -Object $payload -Name "semantic_hints" -Default $null
            $activeExists = Get-JsonProperty -Object $part -Name "active_exists" -Default $null
            if ($activeExists -eq $false) { $activeMissingParts += 1 }
            if ($null -ne $diff) {
                $textDiffParts += 1
                $addedTerms += @(Get-JsonProperty -Object $semanticHints -Name "added_terms" -Default @())
                $removedTerms += @(Get-JsonProperty -Object $semanticHints -Name "removed_terms" -Default @())
            }
            $parts += [pscustomobject]@{
                file_name = Get-JsonProperty -Object $part -Name "file_name" -Default $null
                payload_role = Get-JsonProperty -Object $part -Name "payload_role" -Default $null
                saved_table = Get-JsonProperty -Object $part -Name "saved_table" -Default $null
                active_table = Get-JsonProperty -Object $part -Name "active_table" -Default $null
                active_exists = $activeExists
                text_comparable = Get-JsonProperty -Object $payload -Name "text_comparable" -Default $null
                summary = Get-JsonProperty -Object $payload -Name "summary" -Default $null
                delta_chars = Get-JsonProperty -Object $diff -Name "delta_chars" -Default $null
            }
        }

        $objects += [pscustomobject]@{
            full_name = $fullName
            layer = Get-JsonProperty -Object $change -Name "layer" -Default $null
            extension = Get-JsonProperty -Object $change -Name "extension" -Default $null
            kind = Get-JsonProperty -Object $change -Name "kind" -Default $null
            kind_ru = Get-JsonProperty -Object $change -Name "kind_ru" -Default $null
            name = Get-JsonProperty -Object $change -Name "name" -Default $null
            synonym = Get-JsonProperty -Object $change -Name "synonym" -Default $null
            change_state = Get-JsonProperty -Object $change -Name "change_state" -Default $null
            parts_count = ($parts | Measure-Object).Count
            text_diff_parts = $textDiffParts
            active_missing_parts = $activeMissingParts
            added_terms = Join-LimitedValues -Values ($addedTerms | Sort-Object -Unique) -Limit 12
            removed_terms = Join-LimitedValues -Values ($removedTerms | Sort-Object -Unique) -Limit 12
            parts = $parts
        }
    }

    $systemChanges = @()
    foreach ($change in @($Comparison.system_changes)) {
        $systemChanges += [pscustomobject]@{
            name = Get-JsonProperty -Object $change -Name "name" -Default $null
            layer = Get-JsonProperty -Object $change -Name "layer" -Default $null
            extension = Get-JsonProperty -Object $change -Name "extension" -Default $null
        }
    }

    return [pscustomobject]@{
        purpose = "Compact agent-facing summary of saved-but-not-applied changes in 1C terms."
        default_next_action = "Inspect object details before generating or applying any code changes."
        object_changes = $objects
        system_changes = $systemChanges
        object_names = @($objects | ForEach-Object { $_.full_name })
        system_change_names = @($systemChanges | ForEach-Object { $_.name })
    }
}

$steps = @()

$steps += Invoke-Step "compare_saved_state_objects" {
    & powershell -NoProfile -ExecutionPolicy Bypass -File scripts\compare_1c_saved_state_objects.ps1 `
        -Server $Server `
        -Database $Database `
        -User $User `
        -Password $Password `
        -BaseMetadataDir $BaseMetadataDir `
        -ExtensionGuidIndex $ExtensionGuidIndex `
        -Output $comparisonPath | Out-Null
    if ($LASTEXITCODE -ne 0) { throw "Saved-state object comparison failed." }
}

$comparison = Read-JsonFile $comparisonPath

$steps += Invoke-Step "export_config_save" {
    $null = Export-TableFiles -Table "ConfigSave" -TargetDir $configSaveDir
}

$steps += Invoke-Step "export_config_cas_save" {
    $null = Export-TableFiles -Table "ConfigCASSave" -TargetDir $configCASSaveDir
}

$activeConfigFiles = @(
    $comparison.object_changes |
        ForEach-Object { $_.storage } |
        Where-Object { $_.active_table -eq "Config" } |
        ForEach-Object { [string]$_.file_name } |
        Sort-Object -Unique
)
$activeConfigCASFiles = @(
    $comparison.object_changes |
        ForEach-Object { $_.storage } |
        Where-Object { $_.active_table -eq "ConfigCAS" -and $_.active_exists } |
        ForEach-Object { [string]$_.file_name } |
        Sort-Object -Unique
)

$steps += Invoke-Step "export_active_config_payloads" {
    if (($activeConfigFiles | Measure-Object).Count -gt 0) {
        $null = Export-TableFiles -Table "Config" -TargetDir $activeConfigDir -FileName $activeConfigFiles
    }
}

$steps += Invoke-Step "export_active_config_cas_payloads" {
    if (($activeConfigCASFiles | Measure-Object).Count -gt 0) {
        $null = Export-TableFiles -Table "ConfigCAS" -TargetDir $activeConfigCASDir -FileName $activeConfigCASFiles
    }
}

$steps += Invoke-Step "analyze_saved_state_object_details" {
    & $Python scripts\analyze_1c_saved_state_object_details.py `
        --comparison $comparisonPath `
        --config-save-dir $configSaveDir `
        --config-dir $activeConfigDir `
        --config-cas-save-dir $configCASSaveDir `
        --config-cas-dir $activeConfigCASDir `
        --extension-manifest-summary $ExtensionManifestSummary `
        --config-cas-all-dir $ConfigCASAllDir `
        --output $detailPath | Out-Null
    if ($LASTEXITCODE -ne 0) { throw "Saved-state detail analysis failed." }
}

$detail = Read-JsonFile $detailPath
$agentSummary = New-AgentSummary -Comparison $comparison -Detail $detail

$reportPath = Join-Path $resolvedOutput "saved-state-object-report.json"
if (-not $MarkdownOutput) {
    $MarkdownOutput = Join-Path $resolvedOutput "saved-state-object-report.md"
} else {
    $MarkdownOutput = [System.IO.Path]::GetFullPath($MarkdownOutput)
}

$result = [pscustomobject]@{
    schema = "onec_saved_state_object_report.v1"
    server = $Server
    database = $Database
    report = $reportPath
    markdown = if ($SkipMarkdown) { $null } else { $MarkdownOutput }
    check = $checkPath
    output_dir = $resolvedOutput
    comparison = $comparisonPath
    detail = $detailPath
    agent_summary = $agentSummary
    payload_dirs = [pscustomobject]@{
        config_save = $configSaveDir
        config_cas_save = $configCASSaveDir
        active_config = $activeConfigDir
        active_config_cas = $activeConfigCASDir
        active_config_cas_all = $ConfigCASAllDir
    }
    counts = [pscustomobject]@{
        object_changes = $comparison.counts.object_changes
        system_changes = $comparison.counts.system_changes
        detail_objects = $detail.counts.objects
        detail_parts = $detail.counts.details
    }
    steps = $steps
    safety = [pscustomobject]@{
        read_only = $true
        sql_write_performed = $false
        public_terms_are_1c_objects = $true
        secrets_in_report = $false
    }
}

$result | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $reportPath -Encoding UTF8

if (-not $SkipMarkdown) {
    $steps += Invoke-Step "render_saved_state_markdown" {
        & $Python scripts\render_1c_saved_state_object_report_markdown.py `
            --report $reportPath `
            --output $MarkdownOutput `
            --max-diff-lines $MarkdownMaxDiffLines | Out-Null
        if ($LASTEXITCODE -ne 0) { throw "Saved-state Markdown rendering failed." }
    }
    $result.steps = $steps
    $result | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $reportPath -Encoding UTF8
}

$steps += Invoke-Step "check_saved_state_report" {
    & $Python scripts\check_1c_saved_state_object_report.py `
        --report $reportPath `
        --output $checkPath | Out-Null
    if ($LASTEXITCODE -ne 0) { throw "Saved-state report check failed." }
}
$result.steps = $steps
$result | ConvertTo-Json -Depth 12 | Set-Content -LiteralPath $reportPath -Encoding UTF8

$result | ConvertTo-Json -Depth 12
