param(
    [string]$Segment = 'S2',
    [string]$Pattern = 'ALL'
)

$ErrorActionPreference = 'Stop'

$Workspace = 'C:\Users\user\Documents\New project'
$OutRoot = Join-Path $Workspace 'KOUCHA_GOLD_LATE_SINGLE_FATAL_EXIT_COMPARE'
$TerminalExe = 'C:\Program Files\XMTrading MT5\terminal64.exe'
$TesterRoot = 'C:\Users\user\AppData\Roaming\MetaQuotes\Tester\2FA8A7E69CED7DC259B1AD86A247F675'
$AgentRoot = Join-Path $TesterRoot 'Agent-127.0.0.1-3000'
$AgentFiles = Join-Path $AgentRoot 'MQL5\Files'
$AgentLog = Join-Path $AgentRoot ("logs\" + (Get-Date -Format 'yyyyMMdd') + ".log")

if (!(Test-Path -LiteralPath $TerminalExe)) {
    throw "MT5 terminal not found: $TerminalExe"
}

$manifestPath = Join-Path $OutRoot 'late_single_fatal_exit_manifest.csv'
if (!(Test-Path -LiteralPath $manifestPath)) {
    throw "Manifest not found. Run make_late_single_fatal_exit_tests.ps1 first."
}

$statusPath = Join-Path $OutRoot 'run_status.csv'
$preflightPath = Join-Path $OutRoot 'input_preflight_validation.csv'
'TestName,Phase,StartTime,EndTime,ElapsedSeconds,ExitCode,IniPath,Note' | Set-Content -LiteralPath $statusPath -Encoding UTF8
'TestName,Pattern,Segment,CheckName,Expected,Actual,Status,EvidenceFile,EvidenceLineOrValue,Comment' | Set-Content -LiteralPath $preflightPath -Encoding UTF8

function Add-RunStatus {
    param([string]$TestName, [string]$Phase, [datetime]$StartTime, [datetime]$EndTime, [object]$ExitCode, [string]$IniPath, [string]$Note)
    $elapsed = if ($EndTime -and $StartTime) { [int]($EndTime - $StartTime).TotalSeconds } else { '' }
    $line = '"{0}","{1}","{2}","{3}","{4}","{5}","{6}","{7}"' -f $TestName,$Phase,$StartTime.ToString('s'),$EndTime.ToString('s'),$elapsed,$ExitCode,$IniPath,($Note -replace '"','""')
    Add-Content -LiteralPath $statusPath -Value $line -Encoding UTF8
}

function Add-Preflight {
    param($Test, [string]$CheckName, [string]$Expected, [string]$Actual, [string]$Status, [string]$EvidenceFile, [string]$Evidence, [string]$Comment)
    $line = '"{0}","{1}","{2}","{3}","{4}","{5}","{6}","{7}","{8}","{9}"' -f `
        $Test.TestName,$Test.Pattern,$Test.Segment,$CheckName,$Expected,($Actual -replace '"','""'),$Status,$EvidenceFile,($Evidence -replace '"','""'),($Comment -replace '"','""')
    Add-Content -LiteralPath $preflightPath -Value $line -Encoding UTF8
}

function Stop-TestProcesses {
    $targets = Get-CimInstance Win32_Process |
        Where-Object {
            ($_.Name -match 'terminal64|metatester64') -and
            ($_.CommandLine -match 'KOUCHA_GOLD|terminal64|metatester64')
        }
    foreach ($t in $targets) {
        Stop-Process -Id $t.ProcessId -Force -ErrorAction SilentlyContinue
    }
}

function Clear-AgentTempFiles {
    param([string]$InventoryPath)
    $names = @(
        'KOUCHA_GOLD_KIWAMI_TRADE_EVENTS.csv',
        'KOUCHA_GOLD_ENTRY_OPPORTUNITY_AUDIT.csv',
        'KOUCHA_GOLD_KIWAMI_SURVIVAL_TICK_LOG.csv'
    )
    $items = @()
    foreach ($name in $names) {
        $path = Join-Path $AgentFiles $name
        if (Test-Path -LiteralPath $path) {
            $items += Get-Item -LiteralPath $path
        }
    }
    $items | Select-Object FullName,Length,LastWriteTime | Export-Csv -LiteralPath $InventoryPath -NoTypeInformation -Encoding UTF8
    foreach ($item in $items) {
        Remove-Item -LiteralPath $item.FullName -Force
    }
}

function Copy-IfExists {
    param([string]$Source, [string]$Destination)
    if (Test-Path -LiteralPath $Source) {
        Copy-Item -LiteralPath $Source -Destination $Destination -Force
        return $Source
    }
    return ''
}

function Read-NewLogLines {
    param([int]$FromLine)
    if (!(Test-Path -LiteralPath $AgentLog)) {
        return @()
    }
    $lines = @(Get-Content -LiteralPath $AgentLog)
    if ($lines.Count -le $FromLine) {
        return @()
    }
    return @($lines[$FromLine..($lines.Count - 1)])
}

function Read-ConfigValue {
    param([string]$Path, [string]$Key)
    $prefix = "$Key="
    foreach ($line in (Get-Content -LiteralPath $Path -Encoding Unicode)) {
        if ($line.StartsWith($prefix)) {
            return $line
        }
    }
    return ''
}

function Validate-StaticConfig {
    param($Test)
    $checks = @(
        @{Name='Deposit'; Expected='Deposit=50000'; Actual=(Read-ConfigValue $Test.Ini 'Deposit')},
        @{Name='Leverage'; Expected='Leverage=1:888'; Actual=(Read-ConfigValue $Test.Ini 'Leverage')},
        @{Name='Symbol'; Expected='Symbol=GOLD'; Actual=(Read-ConfigValue $Test.Ini 'Symbol')},
        @{Name='Period'; Expected='Period=M5'; Actual=(Read-ConfigValue $Test.Ini 'Period')},
        @{Name='Model'; Expected='Model=0'; Actual=(Read-ConfigValue $Test.Ini 'Model')},
        @{Name='FromDate'; Expected=("FromDate=" + $Test.FromDate); Actual=(Read-ConfigValue $Test.Ini 'FromDate')},
        @{Name='ToDate'; Expected=("ToDate=" + $Test.ToDate); Actual=(Read-ConfigValue $Test.Ini 'ToDate')},
        @{Name='UseSinglePositionContraryEarlyExit'; Expected=("UseSinglePositionContraryEarlyExit=" + $Test.ExpectedUseSingle); Actual=(Read-ConfigValue $Test.Set 'UseSinglePositionContraryEarlyExit')},
        @{Name='SingleEarlyExitSoftLossYen'; Expected=("SingleEarlyExitSoftLossYen=" + $Test.ExpectedSoft); Actual=(Read-ConfigValue $Test.Set 'SingleEarlyExitSoftLossYen')},
        @{Name='SingleEarlyExitHardLossYen'; Expected=("SingleEarlyExitHardLossYen=" + $Test.ExpectedHard); Actual=(Read-ConfigValue $Test.Set 'SingleEarlyExitHardLossYen')},
        @{Name='SingleEarlyExitOppositeTickScore'; Expected=("SingleEarlyExitOppositeTickScore=" + $Test.ExpectedOppScore); Actual=(Read-ConfigValue $Test.Set 'SingleEarlyExitOppositeTickScore')},
        @{Name='SingleEarlyExitOppositeScoreDiff'; Expected=("SingleEarlyExitOppositeScoreDiff=" + $Test.ExpectedOppDiff); Actual=(Read-ConfigValue $Test.Set 'SingleEarlyExitOppositeScoreDiff')},
        @{Name='UseSingleEarlyExitPostCooldown'; Expected=("UseSingleEarlyExitPostCooldown=" + $Test.ExpectedUseCooldown); Actual=(Read-ConfigValue $Test.Set 'UseSingleEarlyExitPostCooldown')},
        @{Name='SingleEarlyExitRequireM5CloseConfirmation'; Expected=("SingleEarlyExitRequireM5CloseConfirmation=" + $Test.ExpectedM5); Actual=(Read-ConfigValue $Test.Set 'SingleEarlyExitRequireM5CloseConfirmation')},
        @{Name='MaxSingleEarlyExitPerDay'; Expected=("MaxSingleEarlyExitPerDay=" + $Test.ExpectedMaxDay); Actual=(Read-ConfigValue $Test.Set 'MaxSingleEarlyExitPerDay')},
        @{Name='MaxSingleEarlyExitPerSegment'; Expected=("MaxSingleEarlyExitPerSegment=" + $Test.ExpectedMaxSeg); Actual=(Read-ConfigValue $Test.Set 'MaxSingleEarlyExitPerSegment')}
    )
    $ok = $true
    foreach ($c in $checks) {
        $status = if ($c.Actual -like ($c.Expected + '*')) { 'OK' } else { 'NG' }
        if ($status -eq 'NG') { $ok = $false }
        Add-Preflight -Test $Test -CheckName $c.Name -Expected $c.Expected -Actual $c.Actual -Status $status -EvidenceFile $Test.Ini -Evidence $c.Actual -Comment 'static ini/set check'
    }
    return $ok
}

$manifest = Import-Csv -LiteralPath $manifestPath
if ($Segment -ne 'ALL') {
    $manifest = $manifest | Where-Object { $_.Segment -eq $Segment }
}
if ($Pattern -ne 'ALL') {
    $manifest = $manifest | Where-Object { $_.Pattern -eq $Pattern }
}

foreach ($test in $manifest) {
    Stop-TestProcesses
    Start-Sleep -Seconds 2

    if (!(Validate-StaticConfig -Test $test)) {
        throw "Static preflight failed for $($test.TestName). See $preflightPath"
    }

    $testDir = Split-Path -Parent $test.Ini
    Clear-AgentTempFiles -InventoryPath (Join-Path $testDir ($test.TestName + '_agent_temp_delete_inventory.csv'))

    $logStartLine = 0
    if (Test-Path -LiteralPath $AgentLog) {
        $logStartLine = @(Get-Content -LiteralPath $AgentLog).Count
    }

    $start = Get-Date
    Add-RunStatus -TestName $test.TestName -Phase 'START' -StartTime $start -EndTime $start -ExitCode '' -IniPath $test.Ini -Note 'starting terminal backtest'
    $proc = Start-Process -FilePath $TerminalExe -ArgumentList "/config:`"$($test.Ini)`"" -WindowStyle Hidden -PassThru

    $initialLine = ''
    $preflightOk = $false
    for ($i = 0; $i -lt 90; $i++) {
        Start-Sleep -Seconds 2
        $newLines = Read-NewLogLines -FromLine $logStartLine
        $initialLine = ($newLines | Select-String -Pattern 'initial deposit' | Select-Object -First 1).Line
        if ($initialLine) {
            if ($initialLine -match 'initial deposit 50000 JPY, leverage 1:888') { $preflightOk = $true }
            break
        }
        if ($proc.HasExited) { break }
    }

    if (!$preflightOk) {
        $end = Get-Date
        Add-RunStatus -TestName $test.TestName -Phase 'PREFLIGHT_FAIL' -StartTime $start -EndTime $end -ExitCode '' -IniPath $test.Ini -Note "initialLine=$initialLine"
        Add-Preflight -Test $test -CheckName 'PREFLIGHT_OK' -Expected 'initial deposit 50000 JPY, leverage 1:888' -Actual $initialLine -Status 'NG' -EvidenceFile $AgentLog -Evidence $initialLine -Comment 'runtime tester log check'
        if (-not $proc.HasExited) {
            try { [void]$proc.CloseMainWindow(); Start-Sleep -Seconds 3 } catch {}
            if (-not $proc.HasExited) { Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue }
        }
        throw "Runtime preflight failed for $($test.TestName): $initialLine"
    }

    Add-RunStatus -TestName $test.TestName -Phase 'PREFLIGHT_OK' -StartTime $start -EndTime (Get-Date) -ExitCode '' -IniPath $test.Ini -Note $initialLine
    Add-Preflight -Test $test -CheckName 'PREFLIGHT_OK' -Expected 'initial deposit 50000 JPY, leverage 1:888' -Actual $initialLine -Status 'OK' -EvidenceFile $AgentLog -Evidence $initialLine -Comment 'runtime tester log check'

    $finishedDetected = $false
    while (-not $proc.HasExited) {
        Start-Sleep -Seconds 10
        $newLines = Read-NewLogLines -FromLine $logStartLine
        if ($newLines -match 'thread finished|shutdown finished|MetaTester 5 stopped') {
            $finishedDetected = $true
            try { [void]$proc.CloseMainWindow(); Start-Sleep -Seconds 5 } catch {}
            if (-not $proc.HasExited) { Stop-Process -Id $proc.Id -Force -ErrorAction SilentlyContinue }
            break
        }
    }

    $endTime = Get-Date
    $exitCode = ''
    try { $exitCode = $proc.ExitCode } catch {}
    Add-RunStatus -TestName $test.TestName -Phase 'END' -StartTime $start -EndTime $endTime -ExitCode $exitCode -IniPath $test.Ini -Note ($(if ($finishedDetected) { 'tester thread finished' } else { 'terminal process ended' }))

    $eventSrc = Copy-IfExists -Source (Join-Path $AgentFiles 'KOUCHA_GOLD_KIWAMI_TRADE_EVENTS.csv') -Destination (Join-Path $testDir ($test.TestName + '_trade_events.csv'))
    $auditSrc = Copy-IfExists -Source (Join-Path $AgentFiles 'KOUCHA_GOLD_ENTRY_OPPORTUNITY_AUDIT.csv') -Destination (Join-Path $testDir ($test.TestName + '_entry_opportunity_audit.csv'))
    $logSrc = Copy-IfExists -Source $AgentLog -Destination (Join-Path $testDir ($test.TestName + '_tester.log'))
    Add-RunStatus -TestName $test.TestName -Phase 'COPY' -StartTime $start -EndTime (Get-Date) -ExitCode $exitCode -IniPath $test.Ini -Note "eventSrc=$eventSrc auditSrc=$auditSrc logSrc=$logSrc"
}

Stop-TestProcesses
Write-Host "Finished late single fatal exit tests in $OutRoot for Segment=$Segment"
