$ErrorActionPreference = 'Stop'

function Assert-True([bool]$condition, [string]$message) {
    if (-not $condition) { throw $message }
}

function Assert-Match([string]$content, [string]$pattern, [string]$message) {
    Assert-True ($content -match $pattern) $message
}

function Assert-NotMatch([string]$content, [string]$pattern, [string]$message) {
    Assert-True ($content -notmatch $pattern) $message
}

$sourcePath = Join-Path $PSScriptRoot '..\ParkrobTrajplanner\CoarsePath\Test\MovementTest.CoarsePathTest.cs'
Assert-True (Test-Path -LiteralPath $sourcePath) 'P1 coarse-path UI movement-test source must exist.'
$source = Get-Content -Raw -Encoding UTF8 $sourcePath

Assert-Match $source 'class\s+CoarsePathPlanningTest\s*:\s*MovementTest' 'The UI entry must inherit MovementTest.'
Assert-Match $source 'namespace\s+MultiWheelC\s*;' 'MovementTest entries must use the project discovery namespace.'
Assert-Match $source 'CoarsePathPlanningService' 'The UI must keep one shared planning service for map-cache reuse.'
Assert-Match $source 'Task\.Run\s*\(' 'Planning must run in a background Task.'
Assert-Match $source 'CancellationTokenSource' 'The UI must retain a cancellation source for its active task.'
Assert-Match $source 'override\s+void\s+TestStop\s*\(' 'The UI must implement TestStop cancellation.'
Assert-Match $source 'CoarsePathScenarioFactory\.Create\s*\(' 'The UI must expose fixed scenario entries through the factory.'
Assert-Match $source 'ManualCoarsePathObstacle' 'The manual UI must construct typed manual obstacles.'
Assert-Match $source 'CreateManualObstacleDemo\s*\(' 'The manual UI must submit obstacles through the factory.'
Assert-Match $source 'MaximumManualObstacleCount\s*=\s*20' 'The manual UI must bound obstacle input to 20.'
Assert-Match $source 'ReadManualObstacles\s*\(' 'The manual UI must read the requested obstacle sequence.'
Assert-Match $source 'Interlocked\.Increment\s*\(' 'The manual UI must issue a fresh obstacle snapshot version.'
Assert-Match $source 'Circle\s*\(' 'The manual UI must support circle input.'
Assert-Match $source 'AxisAlignedRectangle\s*\(' 'The manual UI must support axis-aligned rectangle input.'
Assert-Match $source 'UI\.GetInput\s*\(' 'The manual demo must accept user input.'
Assert-Match $source 'ReadPositiveTimeoutInput\s*\(' 'The manual UI must read a finite positive timeout.'
Assert-Match $source 'Configuration\.SearchTimeout\s*=\s*searchTimeout' 'The manual UI must apply the timeout to the current job.'
Assert-Match $source 'TimeSpan\.FromSeconds\s*\(' 'The manual timeout must convert seconds to TimeSpan.'
Assert-Match $source 'timeoutSeconds\s*<=\s*0' 'The manual timeout must reject zero and negative values.'
Assert-Match $source 'getCartLocation\s*\(' 'The manual demo must read the AMR body-center pose.'
Assert-Match $source 'DrawLegend\s*\(' 'The painter output must include a visual legend.'
Assert-Match $source 'PlanningGridMap' 'The painter output must consume the planning grid result.'
Assert-Match $source '\.IsOccupied\s*\(' 'The painter output must draw occupied grid cells.'
Assert-Match $source 'ResolutionMm' 'The painter output must draw the grid at its true resolution.'
Assert-Match $source 'SnapshotId' 'The painter output must show the map snapshot identity.'
Assert-Match $source 'IsGearSwitchPoint' 'The painter output must highlight gear-switch points.'
Assert-Match $source 'GoalPositionToleranceMeters' 'The painter output must draw goal tolerance.'
Assert-True (([regex]::Matches($source, 'PathSearchElapsed')).Count -ge 2) 'The status layer and Toast must both show path-search elapsed time.'
Assert-Match $source 'BuildToastMessage[\s\S]*TerminationReason' 'The failure Toast must include the termination reason.'
Assert-Match $source 'ExpandedNodeCount' 'The status layer must show expanded-node statistics.'
Assert-Match $source 'VehicleKinematics\.TryGetMaximumCurvaturePerMeter' 'The status layer must show the effective turning radius.'
Assert-NotMatch $source 'PlanningMapFactory' 'Movement tests must not build maps directly; use CoarsePathPlanningService.'
Assert-NotMatch $source '\.Result\b' 'The UI must not block on Task.Result.'
Assert-NotMatch $source '\.Wait\s*\(' 'The UI must not block the movement-test thread.'

$runnerStart = $source.IndexOf('internal static class CoarsePathPlanningTestRunner')
Assert-True ($runnerStart -ge 0) 'Shared runner source must exist.'
$runnerSource = $source.Substring($runnerStart)
Assert-Match $runnerSource 'RunScenario[\s\S]*getCartLocation\s*\(' 'Fixed scenarios must read the current AMR pose.'
Assert-Match $runnerSource 'CoarsePathScenarioFactory\.Create\s*\(\s*scenario\s*,' 'Fixed scenarios must use the AMR-aware factory overload.'
Assert-Match $runnerSource 'AMR' 'The runner must expose AMR pose diagnostics.'
Assert-Match $runnerSource 'ArgumentException\("AMR' 'Invalid AMR input must be reported without starting planning.'
Assert-Match $runnerSource 'double\.IsNaN|double\.IsInfinity' 'The runner must reject non-finite AMR coordinates.'
Assert-Match $runnerSource 'DrawStatus\s*\([\s\S]*AmrPoseSnapshot' 'Result status must receive the frozen AMR snapshot.'
$inputFailureStart = $runnerSource.IndexOf('internal static void ShowInputFailure')
$inputFailureEnd = $runnerSource.IndexOf('private static void Finish', $inputFailureStart)
Assert-True ($inputFailureStart -ge 0 -and $inputFailureEnd -gt $inputFailureStart) 'Input failure renderer must be isolated before completion handling.'
$inputFailureSource = $runnerSource.Substring($inputFailureStart, $inputFailureEnd - $inputFailureStart)
Assert-Match $inputFailureSource 'Painter\.DrawText\s*\(' 'Invalid AMR input must be shown on the Painter status layer.'

$readmePath = Join-Path $PSScriptRoot '..\ParkrobTrajplanner\CoarsePath\README.md'
Assert-True (Test-Path -LiteralPath $readmePath) 'CoarsePath README must exist beside its module.'
$readme = Get-Content -Raw -Encoding UTF8 $readmePath
foreach ($requiredText in @(
    'CoarsePathPlanningV1',
    'CoarsePathPlanningTest',
    'CreateManualGoalDemo',
    'getCartLocation',
    'mm',
    'deg',
    'rad',
    'CancellationTokenSource',
    'TestStop',
    'NoFeasiblePath',
    'IsGearSwitchPoint')) {
    Assert-True ($readme.Contains($requiredText)) "P1 README must document $requiredText."
}

$readmeStructure = @(
    'File Structure',
    'Planning Data Flow',
    'Build Status and Stop',
    'Coordinates and Units',
    'Minimal Call Example',
    'Cache and SourceVersion',
    'Detailed Usage Guide',
    'P1 Manual Tests and Visualization',
    'Common Errors',
    'First-Version Limits',
    'CoarsePathPlanningService.Plan(job, cancellationToken)',
    'CoarsePathPlanningJob',
    'PlanningGridMap',
    'SourceVersion',
    'CoarsePathPlanningV1',
    'CancellationTokenSource',
    'NoFeasiblePath',
    'IsGearSwitchPoint',
    'CreateManualObstacleDemo',
    'ManualCoarsePathObstacle',
    'manual-user-input',
    '0-20',
    'PathSearchElapsed',
    '1.20 m',
    'Reeds-Shepp',
    'AxisAlignedRectangle',
    'Create(CoarsePathTestScenario scenario, double amrXMillimeters',
    'DetectionHeadingRadians',
    '../Map/README.md'
)
foreach ($requiredText in $readmeStructure) {
    Assert-True ($readme.Contains($requiredText)) "Restructured CoarsePath README must document $requiredText."
}

Write-Output 'Coarse path P1 UI source checks passed.'
