//===----------------------------------------------------------------------===//
// 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 Foundation
import SystemPackage
import Testing

@testable import ContainerizationEXT4

struct Ext4UnpackerTests {
    // alpine image
    let indexSHA: String = "ad59e9f71edceca7b1ac7c642410858489b743c97233b0a26a5e2098b1443762"
    let fsPath = FilePath(
        FileManager.default.uniqueTemporaryDirectory()
            .appendingPathComponent("ext4.unpacked.oci.img.delme", isDirectory: false))

    final class MockEXT4Unpacker {
        static func Unpack(index: String, fsPath: FilePath) async throws {
            let fs = try EXT4.Formatter(fsPath)
            let bundle = Bundle.module
            guard let indexPath = bundle.url(forResource: index, withExtension: nil) else {
                throw NSError(domain: "indexPath not found", code: 1)
            }
            let indexData = try Data(contentsOf: indexPath)
            guard let indexDict = try JSONSerialization.jsonObject(with: indexData, options: []) as? [String: Any]
            else {
                throw NSError(domain: "indexDict could not be loaded as json", code: 1)
            }
            guard let manifests = indexDict["manifests"] as? [[String: Any]] else {
                throw NSError(domain: "manifests field in index not found", code: 1)
            }
            guard let digest = manifests[0]["digest"] as? String else {
                throw NSError(domain: "digest field not found in index", code: 1)
            }
            guard
                let manifestPath = bundle.url(
                    forResource: String(digest.dropFirst("sha256:".count)), withExtension: nil)
            else {
                throw NSError(domain: "manifestPath not found", code: 1)
            }
            let manifestData = try Data(contentsOf: manifestPath)
            guard let manifestDict = try JSONSerialization.jsonObject(with: manifestData, options: []) as? [String: Any]
            else {
                throw NSError(domain: "manifestDict could not be loaded as json", code: 1)
            }
            guard let layers = manifestDict["layers"] as? [[String: Any]] else {
                throw NSError(domain: "layers field in manifests not found", code: 1)
            }
            for layer in layers {
                guard let layerDigestWithSHA = layer["digest"] as? String else {
                    throw NSError(domain: "digest field not found in layer", code: 1)
                }
                let layerDigest = String(layerDigestWithSHA.dropFirst("sha256:".count))
                guard let layerPath = bundle.url(forResource: layerDigest, withExtension: nil) else {
                    throw NSError(domain: "layer \(layerDigest) not found", code: 1)
                }
                try await fs.unpack(source: layerPath)
            }
            try fs.close()
        }
    }

    @Test func eXT4Unpacker() async throws {
        try await MockEXT4Unpacker.Unpack(index: self.indexSHA, fsPath: self.fsPath)
        let ext4 = try EXT4.EXT4Reader(blockDevice: self.fsPath)
        let children = try ext4.children(of: EXT4.RootInode)
        #expect(
            Set(children.map { $0.0 })
                == Set([
                    ".",
                    "..",
                    "media",
                    "var",
                    "opt",
                    "lost+found",
                    "tmp",
                    "mnt",
                    "sys",
                    "usr",
                    "srv",
                    "root",
                    "etc",
                    "dev",
                    "proc",
                    "run",
                    "home",
                    "bin",
                    "lib",
                    "sbin",
                ]))
    }
}
