<#
.SYNOPSIS
  nhip-no-probe.ps1 - Harness-25 "no hien thi" 3-part debt line (B1 section 3.3 floor).

.DESCRIPTION
  ONE-LINE, READ-ONLY, ZERO-MUTATION debt probe. Called (never inlined) by the
  session command surfaces (/pause, /tiep, session-start, session-end) so a stop
  point always PRINTS the standing debt. Contract:
    (1) NO-API, NO-MUTATION - only reads JSON + measures file bytes + lists dirs.
    (2) exit 0 ALWAYS - a probe MUST NEVER block a stop point. Any exception ->
        prints 'probe-loi (khong chan)' and still exit 0.
    (3) PowerShell 5.1 compatible, ASCII-only script body (gotcha #30): every
        output token is ASCII (NHIP-NO / kiem / light / deep / OVERDUE / over-cap /
        run-chua-gom / CONFIG-THIEU / probe-loi) so a BOM-less .ps1 decoded under the
        system ANSI codepage never mojibakes.
    (4) NO hardcoded cadence/cap - light_every / deep_every / autoinject_cap_bytes are
        READ FROM config keys. A MISSING h24_cadence key prints CONFIG-THIEU(h24_cadence)
        IN-LINE (no assumed default, not swallowed into the generic probe-loi) - keeps
        the no-default signal H24-2 depends on.

  Output contract - EXACTLY one line (5 ve - ve-5 so-yc them @S181; doc-fix S185 theo harness-audit §8:
  comment nay tung khai 4 ve trong khi code :264 in 5 - doc-stale lop "chuoi trang-thai noi sai su that"):
    NHIP-NO: kiem light a/e deep b/f <ok|OVERDUE:light|deep> | tran-bo-nho x over-cap | run-chua-gom y | pull-cach N ngay | so-yc treo T / nhac2+ K
      * kiem  = audit cadence: a = counter - last_audit.light_at_counter (light debt),
                e = h24_cadence.light_every ; b/f = deep pair. OVERDUE when a>=e or b>=f.
      * tran-bo-nho = x agent-memory/*/MEMORY.md files whose (Get-Item).Length exceeds
                archive_gate.autoinject_cap_bytes (byte-measured FRESH, NOT the stale
                'measured' snapshot; Get-Content-count is FORBIDDEN - bay E-010/S130).
      * run-chua-gom = y orphan run-folders per the DUAL-ACCEPT rule ported verbatim
                from tiep.md:40-53 (flat *-synthesis.md OR C8 harvest/*-synthesis.md,
                each non-empty; retired form-3 NOT ported).
      * pull-cach   = N days since the most recent INBOUND receive-date in
                broadcasts/_index.md (col-1 of rows ABOVE the OUTBOUND header).
                WHY THIS VE EXISTS (S143): the standing instruction was "watch
                broadcasts/inbox" - but inbox only ever holds mail ALREADY pulled,
                so it can NEVER surface un-pulled mail. It stayed silent by design
                while SE waited on a hub reply that had been sitting in the hub
                outbox since 18-07, until the hub had to send a nudge. This ve
                measures the thing that was actually blind.
                THRESHOLD IS DELIBERATELY ABSENT: prints the raw number only.
                If key h24_cadence.pull_warn_days is present it also appends
                OVERDUE:pull - but SE does NOT invent that number (owner's call).
                Precedent for that discipline: session_ctx_kb sat DELIBERATELY
                EMPTY until the owner set it explicitly at S146 - the number was
                never guessed by SE, it was waited for. Same rule applies here.
                Unparseable/absent index -> 'pull-cach n-a' (NEVER kills ve 1-3).

.PARAMETER RepoRoot
  Repo root. Default = resolved one level up from this script (scripts/ -> repo root).
  A non-existent RepoRoot is a probe error -> 'probe-loi (khong chan)' + exit 0.

.EXAMPLE
  powershell.exe -ExecutionPolicy Bypass -File scripts/nhip-no-probe.ps1
#>
param(
    [string]$RepoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..')).Path
)

$ErrorActionPreference = 'Stop'

# Read a file's byte length via Get-Item (FORBIDDEN: Get-Content line/char count -
# a no-BOM file counted through Get-Content mis-sizes VN text x2-3, bug E-010/S130).
function Get-ByteLen {
    param([string]$Path)
    return (Get-Item -LiteralPath $Path -ErrorAction Stop).Length
}

try {
    if (-not (Test-Path -LiteralPath $RepoRoot)) {
        throw "RepoRoot not found: $RepoRoot"
    }

    # -----------------------------------------------------------------------
    # Ve 1 - nhip-kiem (audit cadence debt). Numerators from .session-counter.json,
    # denominators from memory-budget.json h24_cadence (READ, never hardcoded).
    # -----------------------------------------------------------------------
    $counterPath = Join-Path $RepoRoot '.claude\governance\.session-counter.json'
    $budgetPath  = Join-Path $RepoRoot '.claude\agent-memory\memory-budget.json'

    $sc     = Get-Content -LiteralPath $counterPath -Raw -Encoding UTF8 | ConvertFrom-Json
    $budget = Get-Content -LiteralPath $budgetPath  -Raw -Encoding UTF8 | ConvertFrom-Json

    $cad = $budget.h24_cadence
    if ($null -eq $cad) {
        # h24_cadence key ABSENT -> fail-loud IN-LINE (no default, not probe-loi).
        $kiemSeg = 'CONFIG-THIEU(h24_cadence)'
    }
    elseif ($null -eq $cad.light_every) {
        $kiemSeg = 'CONFIG-THIEU(h24_cadence.light_every)'
    }
    elseif ($null -eq $cad.deep_every) {
        $kiemSeg = 'CONFIG-THIEU(h24_cadence.deep_every)'
    }
    else {
        $counter = [int]$sc.counter
        $lightAt = [int]$sc.last_audit.light_at_counter
        $deepAt  = [int]$sc.last_audit.deep_at_counter
        $lightEvery = [int]$cad.light_every
        $deepEvery  = [int]$cad.deep_every

        $aLight = $counter - $lightAt
        $bDeep  = $counter - $deepAt

        $over = @()
        if ($aLight -ge $lightEvery) { $over += 'light' }
        if ($bDeep  -ge $deepEvery)  { $over += 'deep' }
        $status = if ($over.Count -gt 0) { 'OVERDUE:' + ($over -join ',') } else { 'ok' }

        $kiemSeg = ("light {0}/{1} deep {2}/{3} {4}" -f $aLight, $lightEvery, $bDeep, $deepEvery, $status)
    }

    # -----------------------------------------------------------------------
    # Ve 2 - tran-bo-nho (memory ceiling). Byte-measure agent-memory/*/MEMORY.md
    # FRESH vs archive_gate.autoinject_cap_bytes (from key, not hardcoded).
    # -----------------------------------------------------------------------
    $cap = $budget.archive_gate.autoinject_cap_bytes
    if ($null -eq $cap) {
        $tranSeg = 'tran-bo-nho CONFIG-THIEU(autoinject_cap_bytes) over-cap'
    }
    else {
        $capN = [int]$cap
        $overCap = 0
        $memRoot = Join-Path $RepoRoot '.claude\agent-memory'
        if (Test-Path $memRoot) {
            foreach ($mem in (Get-ChildItem -Path $memRoot -Directory -ErrorAction SilentlyContinue)) {
                $mf = Join-Path $mem.FullName 'MEMORY.md'
                if (Test-Path $mf) {
                    if ((Get-ByteLen $mf) -gt $capN) { $overCap++ }
                }
            }
        }
        # B1/H3 @S152: ALSO measure archive/_INDEX.md vs tiers.l2_index.cap_bytes.
        # Before this, the probe printed "0 over-cap" while the cicd _INDEX sat at 110.5%
        # of its cap (S152 eval S2) -> an ACTIVE false claim; and cap_bytes had ZERO
        # runtime consumers (H18 ghost-wire class-2). This block is now the consumer.
        $idxOver = 0
        $idxCap = $budget.tiers.l2_index.cap_bytes
        if ($null -eq $idxCap) {
            $tranSeg = ("tran-bo-nho {0} over-cap l2idx CONFIG-THIEU(cap_bytes)" -f $overCap)
        }
        else {
            $idxCapN = [int]$idxCap
            if (Test-Path $memRoot) {
                foreach ($mem in (Get-ChildItem -Path $memRoot -Directory -ErrorAction SilentlyContinue)) {
                    $ix = Join-Path $mem.FullName 'archive\_INDEX.md'
                    if (Test-Path $ix) {
                        if ((Get-ByteLen $ix) -gt $idxCapN) { $idxOver++ }
                    }
                }
            }
            $tranSeg = ("tran-bo-nho {0} over-cap l2idx {1} over" -f $overCap, $idxOver)
        }
    }

    # -----------------------------------------------------------------------
    # Ve 3 - run-chua-gom (orphan runs). DUAL-ACCEPT ported from tiep.md:40-53:
    #   form-1 flat  <dir>/*-synthesis.md      (-s = exists AND non-empty)
    #   form-2 C8    <dir>/harvest/*-synthesis.md
    #   form-3 (run.md '## synthesis' heading) = RETIRED @S123, NOT ported.
    # -----------------------------------------------------------------------
    $orphan = 0
    $runsDir = Join-Path $RepoRoot '.claude\workflows\runs'
    if (Test-Path $runsDir) {
        foreach ($d in (Get-ChildItem -Path $runsDir -Directory -ErrorAction SilentlyContinue)) {
            $runMd = Join-Path $d.FullName 'run.md'
            if (-not (Test-Path $runMd)) { continue }   # no run.md -> not a run -> skip
            $found = $false
            foreach ($f in (Get-ChildItem -Path $d.FullName -Filter '*-synthesis.md' -File -ErrorAction SilentlyContinue)) {
                if ((Get-ByteLen $f.FullName) -gt 0) { $found = $true; break }
            }
            if (-not $found) {
                $harvestDir = Join-Path $d.FullName 'harvest'
                if (Test-Path $harvestDir) {
                    foreach ($f in (Get-ChildItem -Path $harvestDir -Filter '*-synthesis.md' -File -ErrorAction SilentlyContinue)) {
                        if ((Get-ByteLen $f.FullName) -gt 0) { $found = $true; break }
                    }
                }
            }
            if (-not $found) { $orphan++ }
        }
    }
    $runSeg = ("run-chua-gom {0}" -f $orphan)

    # -----------------------------------------------------------------------
    # Ve 4 - pull-cach (broadcast pull lag). Own try/catch ON PURPOSE: a NEW ve
    # must never be able to blind the 3 ves that already work. Any failure here
    # degrades to 'pull-cach n-a', it does NOT fall through to probe-loi.
    # -----------------------------------------------------------------------
    $pullSeg = 'pull-cach n-a'
    try {
        $idxPath = Join-Path $RepoRoot 'broadcasts\_index.md'
        if (Test-Path -LiteralPath $idxPath) {
            $lines = Get-Content -LiteralPath $idxPath -Encoding UTF8
            # Rows ABOVE the OUTBOUND header are INBOUND. Match on the ASCII token
            # only - the real heading carries an emoji this script must not contain.
            $cut = $lines.Count
            for ($i = 0; $i -lt $lines.Count; $i++) {
                if ($lines[$i] -match '^#{1,6}.*OUTBOUND') { $cut = $i; break }
            }
            $newest = $null
            for ($i = 0; $i -lt $cut; $i++) {
                if ($lines[$i] -match '^\|\s*(\d{4}-\d{2}-\d{2})\s*\|') {
                    $d = [datetime]::ParseExact($Matches[1], 'yyyy-MM-dd', $null)
                    if ($null -eq $newest -or $d -gt $newest) { $newest = $d }
                }
            }
            if ($null -ne $newest) {
                $days = [int]([datetime]::Now.Date - $newest.Date).TotalDays
                $pullSeg = ("pull-cach {0} ngay" -f $days)
                # Threshold is OPTIONAL and owner-owned. Absent key = stay INFORM,
                # do NOT assume a default (that would be inventing owner's number).
                $warn = $null
                if ($null -ne $cad) { $warn = $cad.pull_warn_days }
                if ($null -ne $warn -and $days -ge [int]$warn) {
                    $pullSeg = ("pull-cach {0}/{1} ngay OVERDUE:pull" -f $days, [int]$warn)
                }
            }
        }
    }
    catch {
        $pullSeg = 'pull-cach n-a'
    }

    # -----------------------------------------------------------------------
    # (5) SO YEU-CAU CHU DU AN (C4-06 / W4, S181) - hai ve: dang-treo + nhac-tu-lan-2.
    #
    #  (!) PHAN BIET HAI THU KHAC NHAU, in HAI CHUOI KHAC NHAU:
    #       'chua-co-du-lieu' = so vang / khong doc duoc / 0 dong YC  -> phep do KHONG TON TAI
    #       'treo 0 / nhac2+ 0' = da doc duoc n dong, dem ra bang 0   -> phep do CO, ket qua 0
    #     Gop hai cai nay lam mot chinh la 'den xanh gia' ma hub goi ten (A4).
    #  (!) Dong khong tach duoc cot thi DEM RIENG va IN RA, khong bo im lang
    #     (loi cam 4c 'liet-khong-co' / '[skip] im lang').
    #  (!) Cot trang thai mang chu co DAU ('mo' co dau moc). File nay BAT BUOC pure-ASCII
    #     (gotcha #30) nen dung CODE-POINT, khong go chu co dau vao day.
    $ycSeg = 'so-yc chua-co-du-lieu'
    try {
        $ycPath = Join-Path $RepoRoot 'docs/governance/so-yeu-cau-chu-du-an.md'
        if (Test-Path -LiteralPath $ycPath) {
            # -Encoding UTF8 BAT BUOC: PS 5.1 mac dinh doc theo codepage ANSI, so do
            # chu co dau trong cot trang thai bi bien dang TRUOC khi so sanh => dem ra 0
            # trong khi so co dong MO that. Da xay ra that o lan cam dau tien (S181).
            $ycLines = Get-Content -LiteralPath $ycPath -Encoding UTF8
            $OPEN = 'm' + [char]0x1EDF   # "mo" voi dau moc = trang thai MO cua yeu-cau
            $tot = 0; $treo = 0; $nhac2 = 0; $bad = 0; $dup = 0
            foreach ($L in $ycLines) {
                if ($L -notmatch '^\|\s*`YC-\d+`') { continue }
                $c = $L -split '\|'
                if ($c.Count -lt 9) { $bad++; continue }
                # (!) DEM THEO **MUC**, KHONG THEO **DONG** (va @S181 sau khi vai tang-2 bat loi).
                #     Cot cuoi 'nhac-lai-cua' co gia tri => dong nay la LAN NHAC LAI cua mot muc
                #     da dem o ma goc => BO QUA. Dem ca no la dem theo LAN, dung don vi luat CAM.
                #     Dau van tay cua loi do: ca "nhac 5 lan" ma hoa thanh 5 dong se cho 4 dong
                #     count>=2 - dung con so ma luat goi la SAI.
                if (($c[8] -replace '[*` ]','') -ne '') { $dup++; continue }
                $tot++
                # (!) Strip dau nhan markdown truoc khi so: o bang hay duoc viet **mo** cho noi bat.
                #     Ban dau so thang chuoi => '**mo**' truot => in 'treo 0' trong khi so co muc MO.
                #     Da xay ra that ngay luot cam dau tien (S181).
                if (($c[4] -replace '[*` ]','') -eq $OPEN) { $treo++ }
                $m = [regex]::Match($c[5], '\d+')
                if ($m.Success -and [int]$m.Value -ge 2) { $nhac2++ }
            }
            if ($tot -gt 0) {
                # in ca MAU SO muc + so dong nhac-lai da gop, de nguoi doc kiem duoc don vi
                $ycSeg = ("so-yc treo {0} / nhac2+ {1} (muc {2}, gop {3} dong nhac-lai)" -f $treo, $nhac2, $tot, $dup)
                if ($bad -gt 0) { $ycSeg += (" / {0} dong KHONG TACH DUOC COT" -f $bad) }
            }
        }
    }
    catch {
        $ycSeg = 'so-yc chua-co-du-lieu'
    }

    Write-Host ("NHIP-NO: kiem {0} | {1} | {2} | {3} | {4}" -f $kiemSeg, $tranSeg, $runSeg, $pullSeg, $ycSeg)
}
catch {
    # A probe NEVER blocks a stop point. Any failure prints one generic line.
    Write-Host 'probe-loi (khong chan)'
}

exit 0
