#Requires -Modules Pester
# Copyright (c) Microsoft Corporation.
# SPDX-License-Identifier: MIT

BeforeAll {
    $script:ScriptPath = Join-Path $PSScriptRoot '../../evals/New-AgentSurfaceSignatures.ps1'
    $script:FixtureRoot = Join-Path $PSScriptRoot 'fixtures'

    function script:Initialize-FixtureRepo {
        param(
            [Parameter(Mandatory)] [string]$Root,
            [switch]$SiblingPersonas
        )

        $agentsDir = Join-Path $Root '.github/agents'
        New-Item -ItemType Directory -Path $agentsDir -Force | Out-Null

        # The fixture collection co-locates both .agent.md files in one directory,
        # which is the same layout real collections use. Sibling discovery works
        # without a separate persona-bleed staging branch.
        Copy-Item -Recurse -LiteralPath (Join-Path $script:FixtureRoot 'agents/minimal-coll') -Destination $agentsDir
    }
}

Describe 'New-AgentSurfaceSignatures.ps1' -Tag 'Unit' {
    BeforeEach {
        $script:TestRoot = Join-Path $TestDrive ([Guid]::NewGuid().ToString())
        New-Item -ItemType Directory -Path $script:TestRoot -Force | Out-Null
        Initialize-FixtureRepo -Root $script:TestRoot
        $script:OutputDir = Join-Path $script:TestRoot 'evals/baseline-equivalence/surface-signatures'
    }

    Context 'Generated YAML shape' {
        BeforeEach {
            $script:OutputPath = & $script:ScriptPath `
                -Agent 'minimal-agent-a' `
                -RepoRoot $script:TestRoot `
                -OutputDir $script:OutputDir 6>$null
            $script:Yaml = [System.IO.File]::ReadAllText($script:OutputPath)
        }

        It 'Writes a YAML file at <OutputDir>/<Agent>.yml' {
            $script:OutputPath | Should -Be (Join-Path $script:OutputDir 'minimal-agent-a.yml')
            Test-Path -LiteralPath $script:OutputPath | Should -BeTrue
        }

        It 'Contains the generator comment and agent tag' {
            $script:Yaml | Should -Match '# Generated by scripts/evals/New-AgentSurfaceSignatures\.ps1'
            $script:Yaml | Should -Match '# Agent: minimal-agent-a'
        }

        It 'Emits the required and disallowed top-level keys' {
            $script:Yaml | Should -Match '(?m)^required:'
            $script:Yaml | Should -Match '(?m)^disallowed:'
        }

        It 'Uses the {name, type: output-matches, config.pattern} entry shape' {
            $script:Yaml | Should -Match '(?m)^\s{2}-\s+name:\s+\S+'
            $script:Yaml | Should -Match '(?m)^\s{4}type:\s+output-matches'
            $script:Yaml | Should -Match '(?m)^\s{4}config:'
            $script:Yaml | Should -Match "(?m)^\s{6}pattern:\s+'"
        }
    }

    Context 'Header rule derivation' {
        It 'Derives header-present from the agent body Start-responses-with directive' {
            $outputPath = & $script:ScriptPath `
                -Agent 'minimal-agent-a' `
                -RepoRoot $script:TestRoot `
                -OutputDir $script:OutputDir 6>$null
            $yaml = [System.IO.File]::ReadAllText($outputPath)

            $yaml | Should -Match '(?m)^\s+-\s+name:\s+header-present\s*$'
            # The header line literal "## ✨ Minimal Agent A:" should appear in the pattern after regex escaping.
            $yaml | Should -Match '\^\\#\\# ✨ Minimal Agent A:'
        }
    }

    Context 'Scope and disallow rule derivation' {
        It 'Emits scope-derived disallow name (writes-outside-<scope>-dir) when a scope directive exists' {
            $outputPath = & $script:ScriptPath `
                -Agent 'minimal-agent-a' `
                -RepoRoot $script:TestRoot `
                -OutputDir $script:OutputDir 6>$null
            $yaml = [System.IO.File]::ReadAllText($outputPath)

            # minimal-agent-a body declares ".copilot-tracking/minfix/" so scope == 'minfix'
            $yaml | Should -Match '(?m)^\s+-\s+name:\s+minfix-scope-language\s*$'
            $yaml | Should -Match '(?m)^\s+-\s+name:\s+writes-outside-minfix-dir\s*$'
            $yaml | Should -Match "(?i)\(C:\\\\\|/etc/\|/usr/\|~/Documents\)"
        }

        It 'Falls back to writes-outside-allowed-dirs and warns when no scope directive is present' {
            # Strip the .copilot-tracking line out of minimal-agent-b to force the no-scope branch.
            $bPath = Join-Path $script:TestRoot '.github/agents/minimal-coll/minimal-agent-b.agent.md'
            $bBody = [System.IO.File]::ReadAllText($bPath)
            [System.IO.File]::WriteAllText($bPath, ($bBody -replace '\.copilot-tracking/[A-Za-z0-9_/-]+', ''))

            $warnings = $null
            $outputPath = & $script:ScriptPath `
                -Agent 'minimal-agent-b' `
                -RepoRoot $script:TestRoot `
                -OutputDir $script:OutputDir `
                -WarningVariable warnings 3>$null 6>$null
            $yaml = [System.IO.File]::ReadAllText($outputPath)

            $yaml | Should -Match '(?m)^\s+-\s+name:\s+writes-outside-allowed-dirs\s*$'
            ($warnings -join "`n") | Should -Match 'emitting generic writes-outside-allowed-dirs'
        }
    }

    Context 'Persona-bleed disallow rules' {
        BeforeEach {
            $script:TestRoot = Join-Path $TestDrive ([Guid]::NewGuid().ToString())
            New-Item -ItemType Directory -Path $script:TestRoot -Force | Out-Null
            Initialize-FixtureRepo -Root $script:TestRoot
            $script:OutputDir = Join-Path $script:TestRoot 'evals/baseline-equivalence/surface-signatures'
        }

        It 'Excludes persona-bleed rules by default' {
            $outputPath = & $script:ScriptPath `
                -Agent 'minimal-agent-a' `
                -RepoRoot $script:TestRoot `
                -OutputDir $script:OutputDir 6>$null
            $yaml = [System.IO.File]::ReadAllText($outputPath)
            $yaml | Should -Not -Match 'persona-bleed-'
        }

        It 'Emits persona-bleed-<sibling> for each sibling when -IncludePersonaBleed is supplied' {
            $outputPath = & $script:ScriptPath `
                -Agent 'minimal-agent-a' `
                -RepoRoot $script:TestRoot `
                -OutputDir $script:OutputDir `
                -IncludePersonaBleed 6>$null
            $yaml = [System.IO.File]::ReadAllText($outputPath)

            $yaml | Should -Match '(?m)^\s+-\s+name:\s+persona-bleed-minimal-agent-b\s*$'
            $yaml | Should -Not -Match 'persona-bleed-minimal-agent-a'
        }
    }

    Context 'Idempotency' {
        It 'Reports skipped and leaves the file untouched on a second invocation without -Force' {
            $first = & $script:ScriptPath `
                -Agent 'minimal-agent-a' `
                -RepoRoot $script:TestRoot `
                -OutputDir $script:OutputDir 6>$null
            $stamp1 = (Get-Item -LiteralPath $first).LastWriteTimeUtc

            Start-Sleep -Milliseconds 20

            $second = & $script:ScriptPath `
                -Agent 'minimal-agent-a' `
                -RepoRoot $script:TestRoot `
                -OutputDir $script:OutputDir 6>&1
            $stamp2 = (Get-Item -LiteralPath $first).LastWriteTimeUtc

            $stamp2 | Should -Be $stamp1
            ($second -join "`n") | Should -Match 'skipped \(no changes\)'
        }
    }

    Context 'Error paths' {
        It 'Throws when the slug does not match any agent file' {
            { & $script:ScriptPath `
                -Agent 'does-not-exist' `
                -RepoRoot $script:TestRoot `
                -OutputDir $script:OutputDir 6>$null } | Should -Throw
        }
    }
}
