//===----------------------------------------------------------------------===//
// Copyright © 2026 Apple Inc. and the container project authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//===----------------------------------------------------------------------===//

import ContainerTestSupport
import Testing

/// Serial prune tests — `container prune` affects all stopped containers regardless of name.
@Suite(.serialized)
struct TestCLIPruneCommandSerial {
    @Test func testContainerPruneNoContainers() async throws {
        try await ContainerFixture.with { f in
            // Establish empty state — the serial global pass runs after the concurrent
            // pass, and any test that failed mid-cleanup could leave stopped containers
            // that would break the "reclaimed zero" assertion. Machine-backing containers
            // are excluded from `container delete --all` by design so this doesn't
            // interfere with the machine plugin.
            _ = try? f.run(["delete", "--all", "--force"])

            let result = try f.run(["prune"]).check()
            #expect(result.error.contains("Reclaimed Zero KB in disk space"), "should show no containers message")
        }
    }

    @Test func testContainerPruneStoppedContainers() async throws {
        try await ContainerFixture.with { f in
            let image = WarmupImage.alpine320.rawValue
            if try !f.isImagePresent(image) { try f.doPull(image) }

            // One running container that must survive the prune.
            try await f.withContainer(image: image, tag: "running", containerArgs: ["sleep", "3600"]) { npcName in
                // Two containers to stop and prune.
                try await f.withContainer(
                    image: image, tag: "prune0", containerArgs: ["sleep", "3600"], autoRemove: false
                ) { pc0Name in
                    try await f.withContainer(
                        image: image, tag: "prune1", containerArgs: ["sleep", "3600"], autoRemove: false
                    ) { pc1Name in
                        let pc0Id = try f.getContainerId(pc0Name)
                        let pc1Id = try f.getContainerId(pc1Name)

                        try f.doStop(pc0Name)
                        try f.doStop(pc1Name)

                        // Poll until both containers reach stopped state.
                        try await f.retry(attempts: 150, delay: .milliseconds(200)) {
                            try f.getContainerStatus(pc0Name) == "stopped" && f.getContainerStatus(pc1Name) == "stopped"
                        }

                        let result = try f.run(["prune"]).check()
                        #expect(
                            result.output.contains(pc0Id) && result.output.contains(pc1Id),
                            "prune output should list stopped container IDs")
                        #expect(
                            !result.error.contains("Reclaimed Zero KB in disk space"),
                            "reclaimed space should not be zero")

                        let npcStatus = try f.getContainerStatus(npcName)
                        #expect(npcStatus == "running", "running container should not be pruned")
                    }
                }
            }
        }
    }
}
