//===----------------------------------------------------------------------===//
// Copyright © 2025-2026 Apple Inc. and the Containerization 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 ContainerizationArchive
import ContainerizationExtras
import ContainerizationOCI
import Foundation
import Testing

@testable import Containerization

@Suite
public class ImageStoreTests: ContainsAuth {
    let store: ImageStore
    let dir: URL

    public init() {
        let dir = FileManager.default.uniqueTemporaryDirectory(create: true)
        let cs = try! LocalContentStore(path: dir)
        let store = try! ImageStore(path: dir, contentStore: cs)
        self.dir = dir
        self.store = store
    }

    deinit {
        try! FileManager.default.removeItem(at: self.dir)
    }

    @Test func testImageStoreOperation() async throws {
        let fileManager = FileManager.default
        let tempDir = fileManager.uniqueTemporaryDirectory()
        defer {
            try? fileManager.removeItem(at: tempDir)
        }

        let tarPath = Foundation.Bundle.module.url(forResource: "scratch", withExtension: "tar")!
        let reader = try ArchiveReader(format: .pax, filter: .none, file: tarPath)
        let rejectedPaths = try reader.extractContents(to: tempDir)
        #expect(rejectedPaths.count == 0, "unexpected rejected paths [\(rejectedPaths)]")

        let _ = try await self.store.load(from: tempDir)
        let loaded = try await self.store.load(from: tempDir)
        let expectedLoadedImage = "registry.local/integration-tests/scratch:latest"
        #expect(loaded.first!.reference == "registry.local/integration-tests/scratch:latest")

        guard let authentication = Self.authentication else {
            return
        }
        let imageReference = "ghcr.io/apple/containerization/dockermanifestimage:0.0.2"
        let busyboxImage = try await self.store.pull(reference: imageReference, auth: authentication)

        let got = try await self.store.get(reference: imageReference)
        #expect(got.descriptor == busyboxImage.descriptor)

        let newTag = "registry.local/integration-tests/dockermanifestimage:latest"
        let _ = try await self.store.tag(existing: imageReference, new: newTag)

        let tempFile = self.dir.appending(path: "export.tar")
        try await self.store.save(references: [imageReference, expectedLoadedImage], out: tempFile)
    }

    @Test(.disabled("External users cannot push images, disable while we find a better solution"))
    func testImageStorePush() async throws {
        guard let authentication = Self.authentication else {
            return
        }
        let imageReference = "ghcr.io/apple/containerization/dockermanifestimage:0.0.2"

        let remoteImageName = "ghcr.io/apple/test-images/image-push"
        let epoch = Int(Date().timeIntervalSince1970.description)
        let tag = epoch != nil ? String(epoch!) : "latest"
        let upstreamTag = "\(remoteImageName):\(tag)"
        let _ = try await self.store.tag(existing: imageReference, new: upstreamTag)
        try await self.store.push(reference: upstreamTag, auth: authentication)
    }

    @Test(.disabled("External users cannot push images, disable while we find a better solution"))
    func testImageStorePushMultipleReferences() async throws {
        guard let authentication = Self.authentication else {
            return
        }
        let imageReference = "ghcr.io/apple/containerization/dockermanifestimage:0.0.2"

        let remoteImageName = "ghcr.io/apple/test-images/image-push"
        let epoch = Int(Date().timeIntervalSince1970)
        let tags = ["\(remoteImageName):\(epoch)-a", "\(remoteImageName):\(epoch)-b", "\(remoteImageName):\(epoch)-c"]
        for tag in tags {
            let _ = try await self.store.tag(existing: imageReference, new: tag)
        }
        try await self.store.push(references: tags, auth: authentication, maxConcurrentUploads: 2)
    }

    @Test func testLoadImageWithoutAnnotations() async throws {
        let fileManager = FileManager.default
        let tempDir = fileManager.uniqueTemporaryDirectory()
        defer {
            try? fileManager.removeItem(at: tempDir)
        }

        let tarPath = Foundation.Bundle.module.url(forResource: "scratch_no_annotations", withExtension: "tar")!
        let reader = try ArchiveReader(format: .pax, filter: .none, file: tarPath)
        let rejectedPaths = try reader.extractContents(to: tempDir)
        #expect(rejectedPaths.count == 0, "unexpected rejected paths [\(rejectedPaths)]")

        let loaded = try await self.store.load(from: tempDir)

        #expect(loaded.count == 1)

        let reference = loaded.first!.reference
        #expect(reference.hasPrefix("untagged@sha256:"))

        let retrieved = try await self.store.get(reference: reference)
        #expect(retrieved.reference == reference)
    }
}
