param(
    [string]$Pattern = 'ALL'
)

$ErrorActionPreference = 'Stop'

$Workspace = 'C:\Users\user\Documents\New project'
$OutRoot = Join-Path $Workspace 'KOUCHA_GOLD_GLOBAL_MARKET_CLOSED_RETRY_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")
$manifestPath = Join-Path $OutRoot 'test_manifest.csv'

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

$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',
        'KOUCHA_GOLD_ENTRY_DECISION_TRACE.csv',
        'KOUCHA_GOLD_REACTIVE_HEDGE_ELIGIBILITY_TRACE.csv',
        'KOUCHA_GOLD_PERFORMANCE_STATS.csv',
        'KOUCHA_GOLD_COPYBUFFER_BREAKDOWN.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)
    $globalExpected = if ($Test.Pattern -eq 'R1_GlobalMarketClosedRetryOnly') { 'true' } else { 'false' }
    $checks = @(
        @{Name='Deposit'; Expected='Deposit=50000'; Actual=(Read-ConfigValue $Test.Ini 'Deposit'); Evidence=$Test.Ini},
        @{Name='Leverage'; Expected='Leverage=1:888'; Actual=(Read-ConfigValue $Test.Ini 'Leverage'); Evidence=$Test.Ini},
        @{Name='Symbol'; Expected='Symbol=GOLD'; Actual=(Read-ConfigValue $Test.Ini 'Symbol'); Evidence=$Test.Ini},
        @{Name='Period'; Expected='Period=M5'; Actual=(Read-ConfigValue $Test.Ini 'Period'); Evidence=$Test.Ini},
        @{Name='Model'; Expected='Model=0'; Actual=(Read-ConfigValue $Test.Ini 'Model'); Evidence=$Test.Ini},
        @{Name='FromDate'; Expected='FromDate=2026.05.01'; Actual=(Read-ConfigValue $Test.Ini 'FromDate'); Evidence=$Test.Ini},
        @{Name='ToDate'; Expected='ToDate=2026.05.29'; Actual=(Read-ConfigValue $Test.Ini 'ToDate'); Evidence=$Test.Ini},
        @{Name='UseBacktestFastCompareMode'; Expected='UseBacktestFastCompareMode=true'; Actual=(Read-ConfigValue $Test.Set 'UseBacktestFastCompareMode'); Evidence=$Test.Set},
        @{Name='UseBacktestDiagnosticMode'; Expected='UseBacktestDiagnosticMode=false'; Actual=(Read-ConfigValue $Test.Set 'UseBacktestDiagnosticMode'); Evidence=$Test.Set},
        @{Name='UseDxyVixOnlyCache'; Expected='UseDxyVixOnlyCache=true'; Actual=(Read-ConfigValue $Test.Set 'UseDxyVixOnlyCache'); Evidence=$Test.Set},
        @{Name='DxyVixOnlyCacheSeconds'; Expected='DxyVixOnlyCacheSeconds=5'; Actual=(Read-ConfigValue $Test.Set 'DxyVixOnlyCacheSeconds'); Evidence=$Test.Set},
        @{Name='UsePerformanceCache'; Expected='UsePerformanceCache=false'; Actual=(Read-ConfigValue $Test.Set 'UsePerformanceCache'); Evidence=$Test.Set},
        @{Name='UsePostDefenseHedgeExitControl'; Expected='UsePostDefenseHedgeExitControl=false'; Actual=(Read-ConfigValue $Test.Set 'UsePostDefenseHedgeExitControl'); Evidence=$Test.Set},
        @{Name='UsePostHedgeSelectiveExitOnly'; Expected='UsePostHedgeSelectiveExitOnly=false'; Actual=(Read-ConfigValue $Test.Set 'UsePostHedgeSelectiveExitOnly'); Evidence=$Test.Set},
        @{Name='UseSinglePositionContraryEarlyExit'; Expected='UseSinglePositionContraryEarlyExit=false'; Actual=(Read-ConfigValue $Test.Set 'UseSinglePositionContraryEarlyExit'); Evidence=$Test.Set},
        @{Name='UseHardStopOriginEntryBlock'; Expected='UseHardStopOriginEntryBlock=false'; Actual=(Read-ConfigValue $Test.Set 'UseHardStopOriginEntryBlock'); Evidence=$Test.Set},
        @{Name='UseSinglePositionReactiveDefenseHedge'; Expected='UseSinglePositionReactiveDefenseHedge=false'; Actual=(Read-ConfigValue $Test.Set 'UseSinglePositionReactiveDefenseHedge'); Evidence=$Test.Set},
        @{Name='UseGlobalMarketClosedRetry'; Expected=("UseGlobalMarketClosedRetry=$globalExpected"); Actual=(Read-ConfigValue $Test.Set 'UseGlobalMarketClosedRetry'); Evidence=$Test.Set}
    )
    $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 $c.Evidence -Evidence $c.Actual -Comment 'static ini/set check'
    }
    return $ok
}

$manifest = Import-Csv -LiteralPath $manifestPath
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'))
    $perfSrc = Copy-IfExists -Source (Join-Path $AgentFiles 'KOUCHA_GOLD_PERFORMANCE_STATS.csv') -Destination (Join-Path $testDir ($test.TestName + '_performance_stats.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 perfSrc=$perfSrc logSrc=$logSrc"
}

Stop-TestProcesses
Write-Host "Finished global market closed retry tests in $OutRoot Pattern=$Pattern"
