import Foundation
import Testing
@testable import CodexBarCore
#if canImport(Darwin)
import Darwin
#else
import Glibc
#endif

@Suite(.serialized)
struct KiroStatusProbeTests {
    @Test
    func `fetch returns usage when account probe times out`() async throws {
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ "$1" = "whoami" ]; then
              sleep 5
              printf 'Logged in with Google\\nEmail: person@example.com\\n'
              exit 0
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\\n'
              printf 'Credits (12.50 of 50 covered in plan)\\n'
              printf '████████████████████ 25%%\\n'
              exit 0
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi

            exit 1
            """)
        defer { try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent()) }

        let probe = KiroStatusProbe(cliBinaryResolver: { cliURL.path }, accountProbeTimeout: 0.2)
        let snapshot = try await probe.fetch()

        #expect(snapshot.planName == "KIRO FREE")
        #expect(snapshot.creditsUsed == 12.50)
        #expect(snapshot.accountEmail == nil)
        #expect(snapshot.authMethod == nil)
    }

    @Test
    func `pipe and PTY share the account deadline`() async throws {
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ "$1" = "whoami" ]; then
              if [ ! -t 1 ]; then
                sleep 5
                exit 1
              fi
              sleep 0.45
              printf 'Logged in with Google\nEmail: person@example.com\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
              printf 'Credits (12.50 of 50 covered in plan)\n'
              printf '████████████████████ 25%%\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi
            exit 1
            """)
        defer { try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent()) }

        let snapshot = try await KiroStatusProbe(
            cliBinaryResolver: { cliURL.path },
            accountProbeTimeout: 0.8,
            pipeTimeoutCap: 0.4).fetch()

        #expect(snapshot.planName == "KIRO FREE")
        #expect(snapshot.accountEmail == nil)
        #expect(snapshot.authMethod == nil)
    }

    @Test
    func `accepted pipe output cannot overrun the usage deadline`() async throws {
        let pipePIDFile = FileManager.default.temporaryDirectory
            .appendingPathComponent("codexbar-kiro-deadline-\(UUID().uuidString).pid")
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\nEmail: person@example.com\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              if [ -t 1 ]; then
                exit 97
              fi
              if [ ! -t 1 ]; then
                printf '%s\n' "$$" > '\(pipePIDFile.path)'
                printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
                printf 'Credits (12.50 of 50 covered in plan)\n'
                printf '████████████████████ 25%%\n'
                trap '' TERM
                while true; do sleep 1; done
              fi
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi
            exit 1
            """)
        defer {
            if let text = try? String(contentsOf: pipePIDFile, encoding: .utf8),
               let pipePID = pid_t(text.trimmingCharacters(in: .whitespacesAndNewlines))
            {
                _ = kill(pipePID, SIGKILL)
            }
            try? FileManager.default.removeItem(at: pipePIDFile)
            try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent())
        }

        let registry = KiroTestProcessRegistry()
        let probe = KiroStatusProbe(
            cliBinaryResolver: { cliURL.path },
            usageProbeTimeout: 4,
            pipeTimeoutCap: 2,
            pipeProcessRegistry: registry.dependencies)
        let completion = KiroTestCompletionMarker()
        let task = Task {
            defer { completion.markCompleted() }
            return try await probe.fetch()
        }
        defer {
            task.cancel()
            registry.terminateAll()
        }

        try await KiroProcessTestSupport.waitForCondition(
            "the accepted-pipe deadline probe to finish and release all registered processes",
            timeout: .seconds(20))
        {
            completion.isCompleted() && registry.activePIDs().isEmpty
        }
        await #expect {
            _ = try await task.value
        } throws: { error in
            guard case KiroStatusProbeError.timeout = error else { return false }
            return true
        }

        // A loaded runner may legitimately start PTY fallback before the pipe child gets scheduled. Assert cleanup
        // at the registry boundary instead of assuming either child executed far enough to create its marker file.
        let observedPIDs = registry.observedPIDs()
        #expect(!observedPIDs.isEmpty)
        #expect(registry.activePIDs().isEmpty)
        for pid in observedPIDs {
            #expect(kill(pid, 0) == -1)
        }
        if let pipePID = KiroProcessTestSupport.readPID(from: pipePIDFile) {
            #expect(kill(pipePID, 0) == -1)
        }
    }

    @Test
    func `fetch preserves account info when account probe succeeds`() async throws {
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\\nEmail: person@example.com\\n'
              exit 0
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\\n'
              printf 'Credits (12.50 of 50 covered in plan)\\n'
              printf '████████████████████ 25%%\\n'
              exit 0
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi

            exit 1
            """)
        defer { try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent()) }

        let probe = KiroProcessTestSupport.makeFunctionalProbe(cliURL: cliURL)
        let snapshot = try await probe.fetch()

        #expect(snapshot.accountEmail == "person@example.com")
        #expect(snapshot.authMethod == "Google")
    }
}

extension KiroStatusProbeTests {
    @Test
    func `fetch supports kiro cli that only completes through pipes`() async throws {
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ -t 1 ]; then
              sleep 30
              exit 1
            fi

            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\nEmail: person@example.com\n'
              exit 0
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
              printf 'Credits (12.50 of 50 covered in plan)\n'
              printf '████████████████████ 25%%\n'
              exit 0
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi

            exit 1
            """)
        defer { try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent()) }

        let probe = KiroProcessTestSupport.makeFunctionalProbe(cliURL: cliURL)
        let snapshot = try await probe.fetch()

        #expect(snapshot.planName == "KIRO FREE")
        #expect(snapshot.creditsUsed == 12.50)
        #expect(snapshot.accountEmail == "person@example.com")
    }

    @Test
    func `slow pipe remains viable after PTY fallback starts`() async throws {
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ -t 1 ]; then
              exit 97
            fi
            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\nEmail: person@example.com\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              sleep 1
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
              printf 'Credits (12.50 of 50 covered in plan)\n'
              printf '████████████████████ 25%%\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi
            exit 1
            """)
        defer { try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent()) }

        let snapshot = try await KiroProcessTestSupport.makeFunctionalProbe(
            cliURL: cliURL,
            usageProbeTimeout: 15,
            pipeTimeoutCap: 0.2).fetch()

        #expect(snapshot.planName == "KIRO FREE")
    }

    @Test
    func `fetch falls back to PTY for older kiro cli`() async throws {
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ ! -t 1 ]; then
              sleep 30
              exit 1
            fi

            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\nEmail: person@example.com\n'
              exit 0
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
              printf 'Credits (12.50 of 50 covered in plan)\n'
              printf '████████████████████ 25%%\n'
              exit 0
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi

            exit 1
            """)
        defer { try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent()) }

        let probe = KiroProcessTestSupport.makeFunctionalProbe(
            cliURL: cliURL,
            pipeTimeoutCap: 0.2)
        let snapshot = try await probe.fetch()

        #expect(snapshot.planName == "KIRO FREE")
        #expect(snapshot.creditsUsed == 12.50)
        #expect(snapshot.accountEmail == "person@example.com")
    }

    @Test
    func `fetch falls back to PTY after incomplete pipe output`() async throws {
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ ! -t 1 ]; then
              if [ "$1" = "whoami" ]; then
                printf 'Logged in with Google\nEmail: person@example.com\n'
                exit 0
              fi
              if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
                printf 'Plan: loading...\n'
                exit 0
              fi
              if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
                exit 0
              fi
            fi

            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\nEmail: person@example.com\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
              printf 'Credits (12.50 of 50 covered in plan)\n'
              printf '████████████████████ 25%%\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi
            exit 1
            """)
        defer { try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent()) }

        let snapshot = try await KiroProcessTestSupport.makeFunctionalProbe(cliURL: cliURL).fetch()

        #expect(snapshot.planName == "KIRO FREE")
        #expect(snapshot.creditsUsed == 12.50)
    }

    @Test
    func `pipe cleanup finishes before PTY fallback starts`() async throws {
        let childPIDFile = FileManager.default.temporaryDirectory
            .appendingPathComponent("codexbar-kiro-pipe-child-\(UUID().uuidString).pid")
        let ptyMarker = FileManager.default.temporaryDirectory
            .appendingPathComponent("codexbar-kiro-pty-fallback-\(UUID().uuidString).started")
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\nEmail: person@example.com\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              if [ ! -t 1 ]; then
                (trap '' TERM; while true; do sleep 1; done) &
                child=$!
                printf '%s\n' "$child" > '\(childPIDFile.path)'
                printf 'Plan: loading...\n'
                exit 0
              fi
              if test -s '\(childPIDFile.path)' && kill -0 "$(cat '\(childPIDFile.path)')" 2>/dev/null; then
                printf 'pipe child still running\n' >&2
                exit 97
              fi
              : > '\(ptyMarker.path)'
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
              printf 'Credits (12.50 of 50 covered in plan)\n'
              printf '████████████████████ 25%%\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi
            exit 1
            """)
        defer {
            if let text = try? String(contentsOf: childPIDFile, encoding: .utf8),
               let childPID = pid_t(text.trimmingCharacters(in: .whitespacesAndNewlines))
            {
                _ = kill(childPID, SIGKILL)
            }
            try? FileManager.default.removeItem(at: childPIDFile)
            try? FileManager.default.removeItem(at: ptyMarker)
            try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent())
        }

        let snapshot = try await KiroProcessTestSupport.makeFunctionalProbe(cliURL: cliURL).fetch()

        #expect(snapshot.planName == "KIRO FREE")
        #expect(FileManager.default.fileExists(atPath: ptyMarker.path))
        let childPIDText = try String(contentsOf: childPIDFile, encoding: .utf8)
        let childPID = try #require(pid_t(childPIDText.trimmingCharacters(in: .whitespacesAndNewlines)))
        #expect(kill(childPID, 0) == -1)
    }

    @Test
    func `shutdown registry terminates an active pipe probe`() async throws {
        let pipePIDFile = FileManager.default.temporaryDirectory
            .appendingPathComponent("codexbar-kiro-shutdown-\(UUID().uuidString).pid")
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\nEmail: person@example.com\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              if [ -t 1 ]; then
                exit 97
              fi
              printf '%s\n' "$$" > '\(pipePIDFile.path)'
              printf 'Plan: loading...\n'
              trap '' TERM
              while true; do sleep 1; done
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi
            exit 1
            """)

        let registry = KiroTestProcessRegistry()
        let probe = KiroProcessTestSupport.makeFunctionalProbe(
            cliURL: cliURL,
            processRegistry: registry)
        let completion = KiroTestCompletionMarker()
        let task = Task {
            defer { completion.markCompleted() }
            return try await probe.fetch()
        }
        defer {
            task.cancel()
            registry.terminateAll()
            if let text = try? String(contentsOf: pipePIDFile, encoding: .utf8),
               let pipePID = pid_t(text.trimmingCharacters(in: .whitespacesAndNewlines))
            {
                _ = kill(pipePID, SIGKILL)
            }
            try? FileManager.default.removeItem(at: pipePIDFile)
            try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent())
        }

        let pipePID = try await KiroProcessTestSupport.waitForPID(in: pipePIDFile)
        #expect(registry.isRegistered(pipePID))

        registry.terminate(pipePID)
        try await KiroProcessTestSupport.waitForCondition(
            "shutdown termination to finish the pipe probe, unregister it, and reap its process",
            timeout: .seconds(20))
        {
            completion.isCompleted()
                && !registry.isRegistered(pipePID)
                && registry.didUnregister(pipePID)
                && kill(pipePID, 0) == -1
        }
        await #expect(throws: (any Error).self) {
            _ = try await task.value
        }

        #expect(!registry.isRegistered(pipePID))
        #expect(registry.didUnregister(pipePID))
        #expect(kill(pipePID, 0) == -1)
    }

    @Test
    func `fetch combines pipe stdout with stderr warnings`() async throws {
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ -t 1 ]; then
              exit 91
            fi
            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\nEmail: person@example.com\n'
              printf 'warning: cached session\n' >&2
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
              printf 'Credits (12.50 of 50 covered in plan)\n'
              printf '████████████████████ 25%%\n'
              printf 'warning: telemetry unavailable\n' >&2
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi
            exit 1
            """)
        defer { try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent()) }

        let snapshot = try await KiroProcessTestSupport.makeFunctionalProbe(cliURL: cliURL).fetch()

        #expect(snapshot.planName == "KIRO FREE")
        #expect(snapshot.accountEmail == "person@example.com")
        #expect(snapshot.authMethod == "Google")
    }

    @Test
    func `fetch falls back to PTY after pipe requires a terminal`() async throws {
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ ! -t 1 ]; then
              printf 'terminal required\n' >&2
              exit 2
            fi
            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\nEmail: person@example.com\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
              printf 'Credits (12.50 of 50 covered in plan)\n'
              printf '████████████████████ 25%%\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              printf 'Context window: 7.5%% used (estimated)\n'
              printf '█ Context files 2.5%% (estimated)\n'
              exit 0
            fi
            exit 1
            """)
        defer { try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent()) }

        let snapshot = try await KiroProcessTestSupport.makeFunctionalProbe(cliURL: cliURL).fetch()

        #expect(snapshot.planName == "KIRO FREE")
        #expect(snapshot.accountEmail == "person@example.com")
        #expect(snapshot.contextUsage?.totalPercentUsed == 7.5)
        #expect(snapshot.contextUsage?.contextFilesPercent == 2.5)
    }

    @Test
    func `pipe auth failure on stderr remains authoritative`() async throws {
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ ! -t 1 ]; then
              printf 'Opening browser...\n'
              printf 'Not logged in\n' >&2
              exit 1
            fi
            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\nEmail: person@example.com\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
              printf 'Credits (12.50 of 50 covered in plan)\n'
              printf '████████████████████ 25%%\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi
            exit 1
            """)
        defer { try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent()) }

        await #expect {
            _ = try await KiroProcessTestSupport.makeFunctionalProbe(cliURL: cliURL).fetch()
        } throws: { error in
            guard case KiroStatusProbeError.notLoggedIn = error else { return false }
            return true
        }
    }

    @Test
    func `fetch rejects account markers from failed whoami`() async throws {
        let whoAmIFailed = FileManager.default.temporaryDirectory
            .appendingPathComponent("codexbar-kiro-failed-whoami-\(UUID().uuidString).started")
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\nEmail: person@example.com\n'
              : > '\(whoAmIFailed.path)'
              exit 23
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
              printf 'Credits (12.50 of 50 covered in plan)\n'
              printf '████████████████████ 25%%\n'
              exit 0
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi

            exit 1
            """)
        defer {
            try? FileManager.default.removeItem(at: whoAmIFailed)
            try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent())
        }

        let snapshot = try await KiroProcessTestSupport.makeFunctionalProbe(cliURL: cliURL).fetch()
        try await KiroProcessTestSupport.waitForFile(whoAmIFailed)

        #expect(snapshot.accountEmail == nil)
        #expect(snapshot.authMethod == nil)
    }

    @Test
    func `fetch rejects valid-looking usage from failed command`() async throws {
        let whoAmISucceeded = FileManager.default.temporaryDirectory
            .appendingPathComponent("codexbar-kiro-successful-whoami-\(UUID().uuidString).started")
        let usageFailed = FileManager.default.temporaryDirectory
            .appendingPathComponent("codexbar-kiro-failed-usage-\(UUID().uuidString).started")
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ -t 1 ]; then
              if [ "$1" = "whoami" ]; then
                printf 'Logged in with Google\nEmail: person@example.com\n'
                : > '\(whoAmISucceeded.path)'
                exit 0
              fi
              if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
                printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
                printf 'Credits (12.50 of 50 covered in plan)\n'
                printf '████████████████████ 25%%\n'
                exit 0
              fi
            fi

            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\nEmail: person@example.com\n'
              : > '\(whoAmISucceeded.path)'
              exit 0
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
              printf 'Credits (12.50 of 50 covered in plan)\n'
              printf '████████████████████ 25%%\n'
              : > '\(usageFailed.path)'
              exit 23
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi

            exit 1
            """)
        defer {
            try? FileManager.default.removeItem(at: whoAmISucceeded)
            try? FileManager.default.removeItem(at: usageFailed)
            try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent())
        }

        let probe = KiroProcessTestSupport.makeFunctionalProbe(cliURL: cliURL)
        await #expect {
            _ = try await probe.fetch()
        } throws: { error in
            guard case KiroStatusProbeError.cliFailed = error else { return false }
            return true
        }
        try await KiroProcessTestSupport.waitForFile(whoAmISucceeded)
        try await KiroProcessTestSupport.waitForFile(usageFailed)
    }

    @Test
    func `fetch preserves not logged in when usage fails without auth detail`() async throws {
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ "$1" = "whoami" ]; then
              printf 'Not logged in\\n'
              exit 1
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              exit 1
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi

            exit 1
            """)
        defer { try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent()) }

        let probe = KiroProcessTestSupport.makeFunctionalProbe(cliURL: cliURL)
        await #expect {
            _ = try await probe.fetch()
        } throws: { error in
            guard case KiroStatusProbeError.notLoggedIn = error else { return false }
            return true
        }
    }

    @Test
    func `fetch preserves not logged in when whoami idles after login marker`() async throws {
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ "$1" = "whoami" ]; then
              printf 'Not logged in\n'
              sleep 5
              exit 1
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              exit 1
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi

            exit 1
            """)
        defer { try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent()) }

        let probe = KiroProcessTestSupport.makeFunctionalProbe(cliURL: cliURL)
        await #expect {
            _ = try await probe.fetch()
        } throws: { error in
            guard case KiroStatusProbeError.notLoggedIn = error else { return false }
            return true
        }
    }

    @Test
    func `fetch preserves not logged in when usage output cannot be parsed`() async throws {
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ "$1" = "whoami" ]; then
              printf 'Not logged in\\n'
              exit 1
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              exit 0
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi

            exit 1
            """)
        defer { try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent()) }

        let probe = KiroProcessTestSupport.makeFunctionalProbe(cliURL: cliURL)
        await #expect {
            _ = try await probe.fetch()
        } throws: { error in
            guard case KiroStatusProbeError.notLoggedIn = error else { return false }
            return true
        }
    }

    @Test
    func `fetch cancellation during context probe is preserved`() async throws {
        let contextStarted = FileManager.default.temporaryDirectory
            .appendingPathComponent("codexbar-kiro-context-\(UUID().uuidString).started")
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\nEmail: person@example.com\n'
              exit 0
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
              printf 'Credits (12.50 of 50 covered in plan)\n'
              printf '████████████████████ 25%%\n'
              exit 0
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              : > '\(contextStarted.path)'
              trap '' TERM
              while true; do sleep 1; done
            fi

            exit 1
            """)
        defer {
            try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent())
            try? FileManager.default.removeItem(at: contextStarted)
        }

        let registry = KiroTestProcessRegistry()
        let probe = KiroProcessTestSupport.makeFunctionalProbe(
            cliURL: cliURL,
            processRegistry: registry)
        let completion = KiroTestCompletionMarker()
        let task = Task {
            defer { completion.markCompleted() }
            return try await probe.fetch()
        }
        defer {
            task.cancel()
            registry.terminateAll()
        }

        try await KiroProcessTestSupport.waitForFile(contextStarted)

        task.cancel()
        try await KiroProcessTestSupport.waitForCondition(
            "context cancellation to finish the probe and release all registered processes")
        {
            completion.isCompleted() && registry.activePIDs().isEmpty
        }
        await #expect(throws: CancellationError.self) {
            try await task.value
        }
    }

    @Test
    func `cancellation during pipe cleanup wins over an expired context deadline`() async throws {
        let cleanupStarted = FileManager.default.temporaryDirectory
            .appendingPathComponent("codexbar-kiro-cleanup-\(UUID().uuidString).started")
        let unblockCleanup = DispatchSemaphore(value: 0)
        let registry = KiroTestProcessRegistry(
            blockOnUnregister: 3,
            blockStartedURL: cleanupStarted,
            unblock: unblockCleanup)
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ "$1" = "whoami" ]; then
              printf 'Logged in with Google\nEmail: person@example.com\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\n'
              printf 'Credits (12.50 of 50 covered in plan)\n'
              printf '████████████████████ 25%%\n'
              exit 0
            fi
            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi
            exit 1
            """)
        defer {
            unblockCleanup.signal()
            try? FileManager.default.removeItem(at: cleanupStarted)
            try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent())
        }

        let probe = KiroProcessTestSupport.makeFunctionalProbe(
            cliURL: cliURL,
            contextProbeTimeout: 0.2,
            processRegistry: registry)
        let completion = KiroTestCompletionMarker()
        let task = Task {
            defer { completion.markCompleted() }
            return try await probe.fetch()
        }
        defer {
            task.cancel()
            registry.terminateAll()
        }

        try await KiroProcessTestSupport.waitForFile(cleanupStarted)
        task.cancel()
        try await Task.sleep(for: .milliseconds(250))
        unblockCleanup.signal()

        try await KiroProcessTestSupport.waitForCondition(
            "cleanup cancellation to finish the probe and release all registered processes")
        {
            completion.isCompleted() && registry.activePIDs().isEmpty
        }
        await #expect(throws: CancellationError.self) {
            try await task.value
        }
    }

    @Test
    func `fetch cancellation while waiting for account probe is preserved`() async throws {
        let accountStarted = FileManager.default.temporaryDirectory
            .appendingPathComponent("codexbar-kiro-account-\(UUID().uuidString).started")
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ "$1" = "whoami" ]; then
              : > '\(accountStarted.path)'
              trap '' TERM
              while true; do sleep 1; done
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\\n'
              printf 'Credits (12.50 of 50 covered in plan)\\n'
              printf '████████████████████ 25%%\\n'
              exit 0
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
              exit 0
            fi

            exit 1
            """)
        defer {
            try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent())
            try? FileManager.default.removeItem(at: accountStarted)
        }

        let registry = KiroTestProcessRegistry()
        let probe = KiroProcessTestSupport.makeFunctionalProbe(
            cliURL: cliURL,
            processRegistry: registry)
        let completion = KiroTestCompletionMarker()
        let task = Task {
            defer { completion.markCompleted() }
            return try await probe.fetch()
        }
        defer {
            task.cancel()
            registry.terminateAll()
        }

        try await KiroProcessTestSupport.waitForFile(accountStarted)

        task.cancel()
        try await KiroProcessTestSupport.waitForCondition(
            "account cancellation to finish the probe and release all registered processes")
        {
            completion.isCompleted() && registry.activePIDs().isEmpty
        }
        await #expect(throws: CancellationError.self) {
            try await task.value
        }
    }

    @Test
    func `fetch returns promptly when usage helper spawns a detached child`() async throws {
        let root = FileManager.default.temporaryDirectory
            .appendingPathComponent("codexbar-kiro-pipe-\(UUID().uuidString)", isDirectory: true)
        let childPIDFile = root.appendingPathComponent("child.pid")
        let cliURL = root.appendingPathComponent("kiro-cli")
        try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true)
        defer { try? FileManager.default.removeItem(at: root) }
        defer {
            if let text = try? String(contentsOf: childPIDFile, encoding: .utf8),
               let childPID = pid_t(text.trimmingCharacters(in: .whitespacesAndNewlines))
            {
                _ = kill(childPID, SIGKILL)
            }
        }

        let script = """
        #!/bin/bash
        set -e
        if [ "$1" = "whoami" ]; then
          printf 'Logged in with Google\\nEmail: person@example.com\\n'
          exit 0
        fi

        if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
          /usr/bin/python3 -c '
        import os
        import subprocess
        import sys

        ready_read, ready_write = os.pipe()
        child = subprocess.Popen(
            [
                sys.executable,
                "-c",
                "import os,signal,sys,time; "
                "signal.signal(signal.SIGHUP, signal.SIG_IGN); "
                "signal.signal(signal.SIGTERM, signal.SIG_IGN); "
                "handle=open(sys.argv[1], \\\"w\\\"); handle.write(str(os.getpid())); handle.close(); "
                "os.write(int(sys.argv[2]), b\\\"1\\\"); os.close(int(sys.argv[2])); time.sleep(60)",
                os.environ["CODEXBAR_TEST_CHILD_PID_FILE"],
                str(ready_write),
            ],
            start_new_session=True,
            pass_fds=(ready_write,),
        )
        os.close(ready_write)
        if os.read(ready_read, 1) != b"1":
            raise RuntimeError("detached helper exited before signaling readiness")
        os.close(ready_read)
        '
          test -s "$CODEXBAR_TEST_CHILD_PID_FILE"
          printf 'Estimated Usage | resets on 2026-06-01 | KIRO FREE\\n'
          printf 'Credits (12.50 of 50 covered in plan)\\n'
          printf '████████████████████ 25%%\\n'
          exit 0
        fi

        if [ "$1" = "chat" ] && [ "$3" = "/context" ]; then
          printf 'Context window: 40%% used\\n'; exit 0
        fi

        exit 1
        """
        try script.write(to: cliURL, atomically: true, encoding: .utf8)
        try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: cliURL.path)

        let previousPIDFile = ProcessInfo.processInfo.environment["CODEXBAR_TEST_CHILD_PID_FILE"]
        setenv("CODEXBAR_TEST_CHILD_PID_FILE", childPIDFile.path, 1)
        defer {
            if let previousPIDFile {
                setenv("CODEXBAR_TEST_CHILD_PID_FILE", previousPIDFile, 1)
            } else {
                unsetenv("CODEXBAR_TEST_CHILD_PID_FILE")
            }
        }

        let registry = KiroTestProcessRegistry()
        let probe = KiroProcessTestSupport.makeFunctionalProbe(
            cliURL: cliURL,
            processRegistry: registry)
        let completion = KiroTestCompletionMarker()
        let task = Task {
            defer { completion.markCompleted() }
            return try await probe.fetch()
        }
        defer {
            task.cancel()
            registry.terminateAll()
        }
        try await KiroProcessTestSupport.waitForCondition(
            "the detached-child probe to finish without waiting for the 60-second helper",
            timeout: .seconds(20))
        {
            completion.isCompleted() && registry.activePIDs().isEmpty
        }
        let snapshot = try await task.value

        let childPID = try await KiroProcessTestSupport.waitForPID(in: childPIDFile)
        try await KiroProcessTestSupport.waitForExit(
            of: childPID,
            timeout: .seconds(20),
            description: "the detached Kiro usage helper to exit")

        // Keep the optional context probe parseable so this timing check covers detached-child cleanup.
        #expect(snapshot.planName == "KIRO FREE")
        #expect(snapshot.creditsUsed == 12.50 && snapshot.contextUsage?.totalPercentUsed == 40)
        #expect(kill(childPID, 0) == -1)
    }
}

extension KiroStatusProbeTests {
    // MARK: - Happy Path Parsing

    @Test
    func `parses basic usage output`() throws {
        let output = """
        | KIRO FREE                                          |
        ████████████████████████████████████████████████████ 25%
        (12.50 of 50 covered in plan), resets on 01/15
        """

        let probe = KiroStatusProbe()
        let snapshot = try probe.parse(output: output)

        #expect(snapshot.planName == "KIRO FREE")
        #expect(snapshot.displayPlanName == "Kiro Free")
        #expect(snapshot.creditsPercent == 25)
        #expect(snapshot.creditsUsed == 12.50)
        #expect(snapshot.creditsTotal == 50)
        #expect(snapshot.bonusCreditsUsed == nil)
        #expect(snapshot.bonusCreditsTotal == nil)
        #expect(snapshot.bonusExpiryDays == nil)
        #expect(snapshot.resetsAt != nil)
    }

    private func makeCLI(_ script: String) throws -> URL {
        let root = FileManager.default.temporaryDirectory
            .appendingPathComponent("codexbar-kiro-cli-\(UUID().uuidString)", isDirectory: true)
        let cliURL = root.appendingPathComponent("kiro-cli")
        try FileManager.default.createDirectory(at: root, withIntermediateDirectories: true)
        try script.write(to: cliURL, atomically: true, encoding: .utf8)
        try FileManager.default.setAttributes([.posixPermissions: 0o755], ofItemAtPath: cliURL.path)
        return cliURL
    }

    @Test
    func `parses output with bonus credits`() throws {
        let output = """
        | KIRO PRO                                           |
        ████████████████████████████████████████████████████ 80%
        (40.00 of 50 covered in plan), resets on 02/01
        Bonus credits: 5.00/10 credits used, expires in 7 days
        """

        let probe = KiroStatusProbe()
        let snapshot = try probe.parse(output: output)

        #expect(snapshot.planName == "KIRO PRO")
        #expect(snapshot.displayPlanName == "Kiro Pro")
        #expect(snapshot.creditsPercent == 80)
        #expect(snapshot.creditsUsed == 40.00)
        #expect(snapshot.creditsTotal == 50)
        #expect(snapshot.bonusCreditsUsed == 5.00)
        #expect(snapshot.bonusCreditsTotal == 10)
        #expect(snapshot.bonusExpiryDays == 7)
    }

    @Test
    func `parses output without percent fallbacks to credits ratio`() throws {
        let output = """
        | KIRO FREE                                          |
        (12.50 of 50 covered in plan), resets on 01/15
        """

        let probe = KiroStatusProbe()
        let snapshot = try probe.parse(output: output)

        #expect(snapshot.creditsPercent == 25)
    }

    @Test
    func `parses bonus credits without expiry`() throws {
        let output = """
        | KIRO FREE                                          |
        ████████████████████████████████████████████████████ 60%
        (30.00 of 50 covered in plan), resets on 04/01
        Bonus credits: 2.00/5 credits used
        """

        let probe = KiroStatusProbe()
        let snapshot = try probe.parse(output: output)

        #expect(snapshot.bonusCreditsUsed == 2.0)
        #expect(snapshot.bonusCreditsTotal == 5.0)
        #expect(snapshot.bonusExpiryDays == nil)
    }

    @Test
    func `parses output with ANSI codes`() throws {
        let output = """
        \u{001B}[32m| KIRO FREE                                          |\u{001B}[0m
        \u{001B}[38;5;11m████████████████████████████████████████████████████\u{001B}[0m 50%
        (25.00 of 50 covered in plan), resets on 03/15
        """

        let probe = KiroStatusProbe()
        let snapshot = try probe.parse(output: output)

        #expect(snapshot.planName == "KIRO FREE")
        #expect(snapshot.creditsPercent == 50)
        #expect(snapshot.creditsUsed == 25.00)
        #expect(snapshot.creditsTotal == 50)
    }

    @Test
    func `parses output with single day`() throws {
        let output = """
        | KIRO FREE                                          |
        ████████████████████████████████████████████████████ 10%
        (5.00 of 50 covered in plan)
        Bonus credits: 2.00/5 credits used, expires in 1 day
        """

        let probe = KiroStatusProbe()
        let snapshot = try probe.parse(output: output)

        #expect(snapshot.bonusExpiryDays == 1)
    }

    @Test
    func `rejects output missing usage markers`() throws {
        let output = """
        | KIRO FREE                                          |
        """

        let probe = KiroStatusProbe()
        #expect(throws: KiroStatusProbeError.self) {
            try probe.parse(output: output)
        }
    }

    // MARK: - New Format (kiro-cli 1.24+, Q Developer)

    @Test
    func `parses Q developer managed plan`() throws {
        let output = """
        Plan: Q Developer Pro
        Your plan is managed by admin

        Tip: to see context window usage, run /context
        """

        let probe = KiroStatusProbe()
        let snapshot = try probe.parse(output: output)

        #expect(snapshot.planName == "Q Developer Pro")
        #expect(snapshot.creditsPercent == 0)
        #expect(snapshot.creditsUsed == 0)
        #expect(snapshot.creditsTotal == 0)
        #expect(snapshot.bonusCreditsUsed == nil)
        #expect(snapshot.resetsAt == nil)
    }

    @Test
    func `parses Q developer free plan`() throws {
        let output = """
        Plan: Q Developer Free
        Your plan is managed by admin
        """

        let probe = KiroStatusProbe()
        let snapshot = try probe.parse(output: output)

        #expect(snapshot.planName == "Q Developer Free")
        #expect(snapshot.creditsPercent == 0)
    }

    @Test
    func `parses new format with ANSI codes`() throws {
        let output = """
        \u{001B}[38;5;141mPlan: Q Developer Pro\u{001B}[0m
        Your plan is managed by admin
        """

        let probe = KiroStatusProbe()
        let snapshot = try probe.parse(output: output)

        #expect(snapshot.planName == "Q Developer Pro")
    }

    @Test
    func `rejects header only new format without managed marker`() {
        let output = """
        Plan: Q Developer Pro
        Tip: to see context window usage, run /context
        """

        let probe = KiroStatusProbe()
        #expect(throws: KiroStatusProbeError.self) {
            try probe.parse(output: output)
        }
    }

    @Test
    func `preserves parsed usage for managed plan with metrics`() throws {
        let output = """
        Plan: Q Developer Enterprise
        Your plan is managed by admin
        ████████████████████████████████████████████████████ 40%
        (20.00 of 50 covered in plan), resets on 03/15
        """

        let probe = KiroStatusProbe()
        let snapshot = try probe.parse(output: output)

        #expect(snapshot.planName == "Q Developer Enterprise")
        #expect(snapshot.creditsPercent == 40)
        #expect(snapshot.creditsUsed == 20)
        #expect(snapshot.creditsTotal == 50)
        #expect(snapshot.resetsAt != nil)
    }

    @Test
    func `parses kiro cli two usage format`() throws {
        let output = """
        \u{001B}[1mEstimated Usage\u{001B}[0m | resets on 2026-06-01 | \u{001B}[mKIRO FREE\u{001B}[0m

        🎁 Bonus credits: 45.53/2000 credits used, expires in 19 days

        \u{001B}[1mCredits\u{001B}[0m (0.17 of 50 covered in plan)
        ████████████████████████████████████████████████████████████████████████████████ 0%

        Overages: \u{001B}[1mDisabled\u{001B}[0m

        To manage your plan or configure overages navigate to https://app.kiro.dev/account/usage
        """

        let probe = KiroStatusProbe()
        let snapshot = try probe.parse(
            output: output,
            accountEmail: "person@example.com",
            authMethod: "Google")

        #expect(snapshot.planName == "KIRO FREE")
        #expect(snapshot.displayPlanName == "Kiro Free")
        #expect(snapshot.accountEmail == "person@example.com")
        #expect(snapshot.authMethod == "Google")
        #expect(snapshot.creditsUsed == 0.17)
        #expect(snapshot.creditsTotal == 50)
        #expect(snapshot.creditsRemaining == 49.83)
        #expect(snapshot.bonusCreditsUsed == 45.53)
        #expect(snapshot.bonusCreditsTotal == 2000)
        #expect(snapshot.bonusCreditsRemaining == 1954.47)
        #expect(snapshot.bonusExpiryDays == 19)
        #expect(snapshot.overagesStatus == "Disabled")
        #expect(snapshot.manageURL == "https://app.kiro.dev/account/usage")
        #expect(snapshot.resetsAt != nil)
    }

    @Test
    func `parses kiro overage credits and estimated cost`() throws {
        let output = """
        Estimated Usage | resets on 2026-06-01 | KIRO PRO
        Credits (1000.00 of 1000 covered in plan)
        ████████████████████████████████████████████████████████████████████████████████ 100%

        Overages: Enabled  billed at $0.04 per request
        Credits used: 40.29
        Est. cost: $1.61 USD

        To manage your plan or configure overages navigate to https://app.kiro.dev/account/usage
        """

        let probe = KiroStatusProbe()
        let snapshot = try probe.parse(output: output)

        #expect(snapshot.planName == "KIRO PRO")
        #expect(snapshot.creditsUsed == 1000)
        #expect(snapshot.creditsTotal == 1000)
        #expect(snapshot.overagesStatus == "Enabled  billed at $0.04 per request")
        #expect(snapshot.overageCreditsUsed == 40.29)
        #expect(snapshot.estimatedOverageCostUSD == 1.61)
    }

    @Test
    func `parses context usage`() throws {
        let output = """
        Context window: 1.3% used (estimated)
        ██████████████████████████████████████████████████████████████████████████████ 1.3%

        █ Context files 0.5% (estimated)
        █ Tools 0.8% (estimated)
        █ Kiro responses 0.0% (estimated)
        █ Your prompts 0.0% (estimated)
        """

        let probe = KiroStatusProbe()
        let context = try #require(probe.parseContextUsage(output: output))

        #expect(context.totalPercentUsed == 1.3)
        #expect(context.contextFilesPercent == 0.5)
        #expect(context.toolsPercent == 0.8)
        #expect(context.kiroResponsesPercent == 0)
        #expect(context.promptsPercent == 0)
    }

    // MARK: - Snapshot Conversion

    @Test
    func `converts snapshot to usage snapshot`() throws {
        let now = Date()
        let resetDate = try #require(Calendar.current.date(byAdding: .day, value: 7, to: now))

        let snapshot = KiroUsageSnapshot(
            planName: "KIRO PRO",
            creditsUsed: 25.0,
            creditsTotal: 100.0,
            creditsPercent: 25.0,
            bonusCreditsUsed: 5.0,
            bonusCreditsTotal: 20.0,
            bonusExpiryDays: 14,
            resetsAt: resetDate,
            updatedAt: now)

        let usage = snapshot.toUsageSnapshot()

        #expect(usage.primary?.usedPercent == 25.0)
        #expect(usage.primary?.resetsAt == resetDate)
        #expect(usage.secondary?.usedPercent == 25.0) // 5/20 * 100
        #expect(usage.loginMethod(for: .kiro) == nil)
        #expect(usage.accountOrganization(for: .kiro) == nil)
        #expect(usage.detailRow(label: "Plan")?.value == "Kiro Pro")
        #expect(usage.detailRow(label: "Credits left")?.value == "75")
    }

    @Test
    func `converts snapshot without bonus credits`() {
        let snapshot = KiroUsageSnapshot(
            planName: "KIRO FREE",
            creditsUsed: 10.0,
            creditsTotal: 50.0,
            creditsPercent: 20.0,
            bonusCreditsUsed: nil,
            bonusCreditsTotal: nil,
            bonusExpiryDays: nil,
            resetsAt: nil,
            updatedAt: Date())

        let usage = snapshot.toUsageSnapshot()

        #expect(usage.primary?.usedPercent == 20.0)
        #expect(usage.secondary == nil)
    }

    // MARK: - Error Cases

    @Test
    func `empty output throws parse error`() {
        let probe = KiroStatusProbe()

        #expect(throws: KiroStatusProbeError.self) {
            try probe.parse(output: "")
        }
    }

    @Test
    func `warning output throws parse error`() {
        let output = """
        \u{001B}[38;5;11m⚠️  Warning: Could not retrieve usage information from backend
        \u{001B}[38;5;8mError: dispatch failure (io error): an i/o error occurred
        """

        let probe = KiroStatusProbe()

        #expect(throws: KiroStatusProbeError.self) {
            try probe.parse(output: output)
        }
    }

    @Test
    func `unrecognized format throws parse error`() {
        // Simulates a CLI format change where none of the expected patterns match
        let output = """
        Welcome to Kiro!
        Your account is active.
        Usage: unknown format
        """

        let probe = KiroStatusProbe()

        #expect {
            try probe.parse(output: output)
        } throws: { error in
            guard case let KiroStatusProbeError.parseError(msg) = error else { return false }
            return msg.contains("No recognizable usage patterns")
        }
    }

    @Test
    func `login prompt throws not logged in`() {
        let output = """
        Failed to initialize auth portal.
        Please try again with: kiro-cli login --use-device-flow
        error: OAuth error: All callback ports are in use.
        """

        let probe = KiroStatusProbe()

        #expect {
            try probe.parse(output: output)
        } throws: { error in
            guard case KiroStatusProbeError.notLoggedIn = error else { return false }
            return true
        }
    }

    // MARK: - WhoAmI Validation

    @Test
    func `whoami not logged in throws`() {
        let probe = KiroStatusProbe()

        #expect {
            try probe.validateWhoAmIOutput(stdout: "Not logged in", stderr: "", terminationStatus: 1)
        } throws: { error in
            guard case KiroStatusProbeError.notLoggedIn = error else { return false }
            return true
        }
    }

    @Test
    func `whoami login required throws`() {
        let probe = KiroStatusProbe()

        #expect {
            try probe.validateWhoAmIOutput(stdout: "login required", stderr: "", terminationStatus: 1)
        } throws: { error in
            guard case KiroStatusProbeError.notLoggedIn = error else { return false }
            return true
        }
    }

    @Test
    func `whoami empty output with zero status throws`() {
        let probe = KiroStatusProbe()

        #expect {
            try probe.validateWhoAmIOutput(stdout: "", stderr: "", terminationStatus: 0)
        } throws: { error in
            guard case KiroStatusProbeError.cliFailed = error else { return false }
            return true
        }
    }

    @Test
    func `whoami non zero status with message throws`() {
        let probe = KiroStatusProbe()

        #expect {
            try probe.validateWhoAmIOutput(stdout: "", stderr: "Connection error", terminationStatus: 1)
        } throws: { error in
            guard case KiroStatusProbeError.cliFailed = error else { return false }
            return true
        }
    }

    @Test
    func `whoami success does not throw`() throws {
        let probe = KiroStatusProbe()

        let account = try probe.validateWhoAmIOutput(
            stdout: """
            Logged in with Google
            Email: user@example.com
            """,
            stderr: "",
            terminationStatus: 0)

        #expect(account.authMethod == "Google")
        #expect(account.email == "user@example.com")
    }

    @Test
    func `whoami legacy bare email parses account`() throws {
        let probe = KiroStatusProbe()

        let account = try probe.validateWhoAmIOutput(
            stdout: "user@example.com",
            stderr: "",
            terminationStatus: 0)

        #expect(account.authMethod == nil)
        #expect(account.email == "user@example.com")
    }
}

extension KiroStatusProbeTests {
    @Test
    func `fetch cancellation while joining account after usage failure is preserved`() async throws {
        let accountStarted = FileManager.default.temporaryDirectory
            .appendingPathComponent("codexbar-kiro-failed-usage-account-\(UUID().uuidString).started")
        let usageFailed = accountStarted.appendingPathExtension("usage-failed")
        let cliURL = try self.makeCLI(
            """
            #!/bin/sh
            if [ "$1" = "whoami" ]; then
              : > '\(accountStarted.path)'
              trap '' TERM
              while true; do sleep 1; done
            fi

            if [ "$1" = "chat" ] && [ "$3" = "/usage" ]; then
              : > '\(usageFailed.path)'
              exit 1
            fi

            exit 1
            """)
        defer {
            try? FileManager.default.removeItem(at: cliURL.deletingLastPathComponent())
            try? FileManager.default.removeItem(at: accountStarted)
            try? FileManager.default.removeItem(at: usageFailed)
        }

        let registry = KiroTestProcessRegistry()
        let probe = KiroProcessTestSupport.makeFunctionalProbe(
            cliURL: cliURL,
            processRegistry: registry)
        let completion = KiroTestCompletionMarker()
        let task = Task {
            defer { completion.markCompleted() }
            return try await probe.fetch()
        }
        defer {
            task.cancel()
            registry.terminateAll()
        }

        try await KiroProcessTestSupport.waitForFile(accountStarted)
        try await KiroProcessTestSupport.waitForFile(usageFailed)
        try await Task.sleep(for: .milliseconds(300))

        task.cancel()
        try await KiroProcessTestSupport.waitForCondition(
            "failed-usage account-join cancellation to finish and release all registered processes")
        {
            completion.isCompleted() && registry.activePIDs().isEmpty
        }
        await #expect(throws: CancellationError.self) {
            try await task.value
        }
    }
}
