//===----------------------------------------------------------------------===//
// Copyright © 2025 Apple Inc. and the container project authors. All rights reserved.
//
// 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 ContainerBuildIR
import ContainerBuildReporting
import Foundation

/// Example demonstrating how to build an IR graph programmatically.
///
/// This example creates a multi-stage Node.js application build.
public enum IRExample {

    /// Create a simple single-stage build.
    public static func createSimpleBuild() throws -> BuildGraph {
        guard let baseImage = ImageReference(parsing: "ubuntu:22.04") else {
            throw ReferenceError.invalidFormat("ubuntu:22.04")
        }

        return try GraphBuilder.singleStage(
            from: baseImage,
            platform: .linuxAMD64
        ) { builder in
            try builder
                .run("apt-get update && apt-get install -y curl")
                .workdir("/app")
                .copyFromContext(paths: ["package.json", "src/"], to: "/app/")
                .run("npm install")
                .env("NODE_ENV", "production")
                .expose([PortSpec(port: 3000)])
                .cmd(Command.exec(["node", "src/index.js"]))
        }
    }

    /// Create a realistic multi-stage build showing actual parallelism
    public static func createParallelBuild(reporter: Reporter? = nil) throws -> BuildGraph {
        // In container builds, true parallelism happens between stages or with independent resources

        guard let nodeImage = ImageReference(parsing: "node:18-alpine"),
            let goImage = ImageReference(parsing: "golang:1.21-alpine"),
            let alpineImage = ImageReference(parsing: "alpine:3.18")
        else {
            throw ReferenceError.invalidFormat("Invalid image reference")
        }

        return try GraphBuilder.multiStage(reporter: reporter) { builder in
            // Stage 1: Build frontend assets
            try builder
                .stage(name: "frontend-builder", from: nodeImage)
                .workdir("/frontend")
                .copyFromContext(paths: ["frontend/package*.json"], to: "./")
                .run("npm ci")
                .copyFromContext(paths: ["frontend/"], to: "./")
                .run("npm run build")

            // Stage 2: Build backend
            try builder
                .stage(name: "backend-builder", from: goImage)
                .workdir("/backend")
                .copyFromContext(paths: ["go.mod", "go.sum"], to: "./")
                .run("go mod download")
                .copyFromContext(paths: ["*.go", "cmd/", "internal/"], to: "./")
                .run("CGO_ENABLED=0 go build -o server ./cmd/server")

            // Stage 3: Runtime - depends on both builders
            try builder
                .stage(name: "runtime", from: alpineImage)
                .copyFromStage(.named("frontend-builder"), paths: ["/frontend/dist"], to: "/app/static")
                .copyFromStage(.named("backend-builder"), paths: ["/backend/server"], to: "/app/server")
                .run("chmod +x /app/server")
                .expose([PortSpec(port: 8080)])
                .cmd(.exec(["/app/server"]))
        }
    }

    /// Create a multi-stage build for a Go application.
    public static func createMultiStageBuild() throws -> BuildGraph {
        try GraphBuilder.multiStage { builder in
            // Build stage
            guard let builderImage = ImageReference(parsing: "golang:1.21-alpine") else {
                throw ReferenceError.invalidFormat("golang:1.21-alpine")
            }

            try builder
                .stage(
                    name: "builder",
                    from: builderImage
                )
                .workdir("/build")
                .copyFromContext(paths: ["go.mod", "go.sum"], to: "./")
                .run("go mod download")
                .copyFromContext(paths: ["*.go"], to: "./")
                .run("CGO_ENABLED=0 go build -o app")

            // Runtime stage
            try builder
                .scratch(name: "runtime")
                .copyFromStage(
                    .named("builder"),
                    paths: ["/build/app"],
                    to: "/app",
                    chmod: .mode(0o755)
                )
                .copyFromStage(
                    .named("builder"),
                    paths: ["/etc/ssl/certs/ca-certificates.crt"],
                    to: "/etc/ssl/certs/"
                )
                .user(.uid(1000))
                .entrypoint(.exec(["/app"]))
        }
    }

    /// Create a Python application with best practices.
    public static func createPythonBuild() throws -> BuildGraph {
        let graph = try BuildGraph(
            stages: [
                // Dependencies stage
                BuildStage(
                    name: "dependencies",
                    base: {
                        guard let pythonImage = ImageReference(parsing: "python:3.11-slim") else {
                            throw ReferenceError.invalidFormat("python:3.11-slim")
                        }
                        return ImageOperation(
                            source: .registry(pythonImage),
                            platform: .linuxAMD64
                        )
                    }(),
                    nodes: [
                        BuildNode(
                            operation: MetadataOperation(
                                action: .setWorkdir(path: "/app")
                            )
                        ),
                        BuildNode(
                            operation: FilesystemOperation(
                                action: .copy,
                                source: .context(ContextSource(paths: ["requirements.txt"])),
                                destination: "/app/"
                            )
                        ),
                        BuildNode(
                            operation: ExecOperation(
                                command: .shell("pip install --user --no-cache-dir -r requirements.txt")
                            )
                        ),
                    ]
                ),

                // Application stage
                BuildStage(
                    name: "app",
                    base: {
                        guard let pythonImage = ImageReference(parsing: "python:3.11-slim") else {
                            throw ReferenceError.invalidFormat("python:3.11-slim")
                        }
                        return ImageOperation(
                            source: .registry(pythonImage),
                            platform: .linuxAMD64
                        )
                    }(),
                    nodes: [
                        // Copy dependencies from first stage
                        BuildNode(
                            operation: FilesystemOperation(
                                action: .copy,
                                source: .stage(.named("dependencies"), paths: ["/root/.local"]),
                                destination: "/root/.local"
                            )
                        ),
                        // Set up application
                        BuildNode(
                            operation: MetadataOperation(
                                action: .setWorkdir(path: "/app")
                            )
                        ),
                        BuildNode(
                            operation: FilesystemOperation(
                                action: .copy,
                                source: .context(ContextSource(paths: ["*.py", "src/"])),
                                destination: "/app/"
                            )
                        ),
                        // Configure runtime
                        BuildNode(
                            operation: MetadataOperation(
                                action: .setEnv(
                                    key: "PYTHONPATH",
                                    value: .literal("/root/.local/lib/python3.11/site-packages")
                                )
                            )
                        ),
                        BuildNode(
                            operation: MetadataOperation(
                                action: .setUser(user: .uidGid(uid: 1000, gid: 1000))
                            )
                        ),
                        BuildNode(
                            operation: MetadataOperation(
                                action: .expose(ports: [PortSpec(port: 8000)])
                            )
                        ),
                        BuildNode(
                            operation: MetadataOperation(
                                action: .setHealthcheck(
                                    healthcheck: Healthcheck(
                                        test: .shell("curl -f http://localhost:8000/health || exit 1"),
                                        interval: 30,
                                        timeout: 3,
                                        retries: 3
                                    )
                                )
                            )
                        ),
                        BuildNode(
                            operation: MetadataOperation(
                                action: .setCmd(command: .exec(["python", "main.py"]))
                            )
                        ),
                    ]
                ),
            ],
            targetPlatforms: [.linuxAMD64, .linuxARM64]
        )

        return graph
    }

    /// Demonstrate advanced features like cache mounts and secrets.
    public static func createAdvancedBuild() throws -> BuildGraph {
        guard let nodeImage = ImageReference(parsing: "node:18-alpine") else {
            throw ReferenceError.invalidFormat("node:18-alpine")
        }

        return try GraphBuilder.singleStage(
            from: nodeImage
        ) { builder in
            try builder
                .workdir("/app")

                // Cache mount for package manager
                .run(
                    "npm ci",
                    mounts: [
                        Mount(
                            type: .cache,
                            target: "/root/.npm",
                            options: MountOptions(sharing: .shared)
                        )
                    ]
                )

                // Secret mount for private registry
                .run(
                    "npm install @private/package",
                    mounts: [
                        Mount(
                            type: .secret,
                            target: "/root/.npmrc",
                            source: .secret("npm-token"),
                            options: MountOptions(readOnly: true, mode: 0o600)
                        )
                    ]
                )

                // Build with tmpfs for temporary files
                .run(
                    "npm run build",
                    mounts: [
                        Mount(
                            type: .tmpfs,
                            target: "/tmp",
                            options: MountOptions(size: 1024 * 1024 * 1024)  // 1GB
                        )
                    ]
                )

                // Multi-platform metadata
                .label("org.opencontainers.image.source", "https://github.com/example/app")
                .label("org.opencontainers.image.version", "${VERSION}")
                .label("org.opencontainers.image.created", "${BUILD_DATE}")
        }
    }

    /// Validate and analyze a build graph.
    public static func analyzeGraph(_ graph: BuildGraph) {
        // Validate
        let validator = StandardValidator()
        let validationResult = validator.validate(graph)

        print("Validation Results:")
        print("  Errors: \(validationResult.errors.count)")
        for error in validationResult.errors {
            print("    - \(error)")
        }
        print("  Warnings: \(validationResult.warnings.count)")
        for warning in validationResult.warnings {
            print("    - \(warning.message)")
            if let suggestion = warning.suggestion {
                print("      Suggestion: \(suggestion)")
            }
        }

        // Analyze with reporter
        print("\nSemantic Analysis:")

        // Graph statistics
        let stats = graph.analyze()
        print("\nGraph Statistics:")
        print("  Stages: \(stats.stageCount)")
        print("  Operations: \(stats.operationCount)")
        print("  Critical Path: \(stats.criticalPathLength) operations")
    }
}
