#!/usr/bin/env nu
# Extension Publishing Tool
# Publishes extensions to OCI registry with validation and packaging

use ../core/nulib/lib_provisioning/oci/client.nu *
use ../core/nulib/lib_provisioning/utils/logger.nu *

# Publish extension to OCI registry
export def main [
    extension_path: string      # Path to extension directory
    --registry: string = ""     # OCI registry (default from config)
    --namespace: string = ""    # Registry namespace (default from config)
    --version: string           # Extension version
    --auth-token: string = ""   # Authentication token path
    --dry-run                   # Validate without publishing
    --force (-f)                # Overwrite existing version
] {
    print $"📦 Publishing extension to OCI registry..."

    # Load config if registry/namespace not provided
    let config = if ($registry | is-empty) or ($namespace | is-empty) {
        get-oci-config
    } else {
        {
            registry: $registry
            namespace: $namespace
            auth_token_path: $auth_token
        }
    }

    let final_registry = if ($registry | is-empty) { $config.registry } else { $registry }
    let final_namespace = if ($namespace | is-empty) { $config.namespace } else { $namespace }
    let token_path = if ($auth_token | is-empty) { $config.auth_token_path } else { $auth_token }

    # 1. Validate extension structure
    print "1️⃣  Validating extension structure..."
    let validation = (validate-extension-structure $extension_path)

    if not $validation.valid {
        print $"❌ Extension validation failed:"
        for error in $validation.errors {
            print $"   • ($error)"
        }
        exit 1
    }

    print "   ✅ Extension structure valid"

    # 2. Read extension manifest
    print "2️⃣  Reading extension manifest..."
    let manifest_file = ($extension_path | path join "extension.yaml")

    if not ($manifest_file | path exists) {
        print "❌ extension.yaml not found"
        exit 1
    }

    let manifest = (open $manifest_file | from yaml)

    let ext_name = ($manifest.extension.name? | default (error make {msg: "Extension name not found in manifest"}))
    let ext_version = if ($version | is-not-empty) {
        $version
    } else {
        $manifest.extension.version? | default (error make {msg: "Extension version not specified"})
    }
    let ext_type = ($manifest.extension.type? | default "unknown")

    print $"   📋 Name: ($ext_name)"
    print $"   🏷️  Version: ($ext_version)"
    print $"   📦 Type: ($ext_type)"

    # 3. Check if version already exists
    if not $force {
        print "3️⃣  Checking if version already exists..."

        let token = (load-oci-token $token_path)
        let exists = (oci-artifact-exists $final_registry $final_namespace $ext_name $ext_version)

        if $exists {
            print $"❌ Version ($ext_version) already exists in registry"
            print "   Use --force to overwrite"
            exit 1
        }

        print "   ✅ Version available"
    }

    # 4. Create OCI artifact
    print "4️⃣  Creating OCI artifact..."
    let artifact_path = (package-extension-as-oci $extension_path $manifest $ext_version)

    print $"   📁 Artifact created at: ($artifact_path)"

    if $dry_run {
        print "🏁 Dry run complete (artifact not published)"
        print $"   Artifact path: ($artifact_path)"
        exit 0
    }

    # 5. Push to registry
    print "5️⃣  Pushing to OCI registry..."

    let token = (load-oci-token $token_path)
    let success = (oci-push-artifact
        $artifact_path
        $final_registry
        $final_namespace
        $ext_name
        $ext_version
        --auth-token $token
    )

    # Clean up artifact
    rm -rf $artifact_path

    if $success {
        print ""
        print $"✅ Successfully published ($ext_name):($ext_version)"
        print $"   Registry: ($final_registry)"
        print $"   Namespace: ($final_namespace)"
        print $"   Pull with: oci://($final_registry)/($final_namespace)/($ext_name):($ext_version)"
    } else {
        print ""
        print "❌ Failed to push artifact to OCI registry"
        exit 1
    }
}

# Validate extension directory structure
def validate-extension-structure [path: string]: nothing -> record {
    let errors = []

    # Check if path exists
    if not ($path | path exists) {
        $errors = ($errors | append $"Path does not exist: ($path)")
        return {valid: false, errors: $errors}
    }

    # Check required file: extension.yaml
    let manifest_file = ($path | path join "extension.yaml")
    if not ($manifest_file | path exists) {
        $errors = ($errors | append "Missing required file: extension.yaml")
    }

    # Validate manifest structure
    if ($manifest_file | path exists) {
        let manifest = (open $manifest_file | from yaml)

        # Check required fields
        if ($manifest.extension?.name? | is-empty) {
            $errors = ($errors | append "extension.name is required in manifest")
        }

        if ($manifest.extension?.type? | is-empty) {
            $errors = ($errors | append "extension.type is required in manifest")
        }

        if ($manifest.extension?.version? | is-empty) {
            $errors = ($errors | append "extension.version is required in manifest")
        }
    }

    # Check for at least one content directory
    let has_kcl = (($path | path join "kcl") | path exists)
    let has_scripts = (($path | path join "scripts") | path exists)
    let has_templates = (($path | path join "templates") | path exists)

    if not ($has_kcl or $has_scripts or $has_templates) {
        $errors = ($errors | append "Extension must have at least one of: kcl/, scripts/, templates/")
    }

    {
        valid: ($errors | is-empty)
        errors: $errors
    }
}

# Package extension as OCI artifact
def package-extension-as-oci [
    extension_path: string
    manifest: record
    version: string
]: nothing -> string {
    let temp_dir = (mktemp -d)

    print $"   📦 Packaging to: ($temp_dir)"

    # Copy extension files
    let dirs_to_copy = [
        {src: "kcl", desc: "KCL schemas"}
        {src: "scripts", desc: "Scripts"}
        {src: "templates", desc: "Templates"}
        {src: "docs", desc: "Documentation"}
    ]

    for dir_info in $dirs_to_copy {
        let src_path = ($extension_path | path join $dir_info.src)
        if ($src_path | path exists) {
            cp -r $src_path $temp_dir
            print $"   ✅ Copied ($dir_info.desc)"
        }
    }

    # Copy manifest
    let manifest_src = ($extension_path | path join "extension.yaml")
    cp $manifest_src $temp_dir
    print "   ✅ Copied manifest"

    # Create OCI config with metadata
    let oci_config = {
        created: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
        architecture: "any"
        os: "any"
        config: {
            Labels: {
                "org.opencontainers.image.title": ($manifest.extension.name? | default "unknown")
                "org.opencontainers.image.version": $version
                "org.opencontainers.image.description": ($manifest.extension.description? | default "")
                "org.opencontainers.image.authors": ($manifest.extension.author? | default "")
                "provisioning.extension.type": ($manifest.extension.type? | default "unknown")
                "provisioning.extension.requires": ($manifest.extension.requires? | default [] | to json)
            }
        }
        annotations: {
            "provisioning.extension.type": ($manifest.extension.type? | default "unknown")
            "provisioning.extension.name": ($manifest.extension.name? | default "unknown")
            "provisioning.extension.version": $version
        }
    }

    # Save OCI config
    $oci_config | to json | save $"($temp_dir)/oci-config.json"
    print "   ✅ Created OCI config"

    $temp_dir
}

# List published extensions
export def "list" [
    --registry: string = ""
    --namespace: string = ""
] {
    let config = if ($registry | is-empty) or ($namespace | is-empty) {
        get-oci-config
    } else {
        {registry: $registry, namespace: $namespace}
    }

    let final_registry = if ($registry | is-empty) { $config.registry } else { $registry }
    let final_namespace = if ($namespace | is-empty) { $config.namespace } else { $namespace }

    print $"📦 Extensions in ($final_registry)/($final_namespace):"

    let artifacts = (oci-list-artifacts $final_registry $final_namespace)

    if ($artifacts | is-empty) {
        print "   (no extensions found)"
        return
    }

    for artifact in $artifacts {
        print $"   • ($artifact)"
    }
}

# Delete published extension
export def "delete" [
    extension_name: string
    version: string
    --registry: string = ""
    --namespace: string = ""
    --auth-token: string = ""
    --force (-f)
] {
    let config = if ($registry | is-empty) or ($namespace | is-empty) {
        get-oci-config
    } else {
        {registry: $registry, namespace: $namespace, auth_token_path: $auth_token}
    }

    let final_registry = if ($registry | is-empty) { $config.registry } else { $registry }
    let final_namespace = if ($namespace | is-empty) { $config.namespace } else { $namespace }
    let token_path = if ($auth_token | is-empty) { $config.auth_token_path } else { $auth_token }

    if not $force {
        print $"⚠️  Delete ($extension_name):($version) from ($final_registry)/($final_namespace)?"
        let confirm = (input "Type 'yes' to confirm: ")

        if $confirm != "yes" {
            print "❌ Cancelled"
            return
        }
    }

    let token = (load-oci-token $token_path)
    let success = (oci-delete-artifact
        $final_registry
        $final_namespace
        $extension_name
        $version
        --auth-token $token
    )

    if $success {
        print $"✅ Deleted ($extension_name):($version)"
    } else {
        print "❌ Failed to delete extension"
        exit 1
    }
}

# Show extension info
export def "info" [
    extension_name: string
    version: string
    --registry: string = ""
    --namespace: string = ""
] {
    let config = if ($registry | is-empty) or ($namespace | is-empty) {
        get-oci-config
    } else {
        {registry: $registry, namespace: $namespace}
    }

    let final_registry = if ($registry | is-empty) { $config.registry } else { $registry }
    let final_namespace = if ($namespace | is-empty) { $config.namespace } else { $namespace }

    let metadata = (get-oci-extension-metadata $extension_name $version {
        registry: $final_registry
        namespace: $final_namespace
    })

    if ($metadata | is-empty) {
        print $"❌ Extension ($extension_name):($version) not found"
        exit 1
    }

    print $"📦 Extension: ($extension_name):($version)"
    print $"   Registry: ($final_registry)"
    print $"   Namespace: ($final_namespace)"
    print $"   Type: ($metadata.annotations?.\"provisioning.extension.type\"? | default 'unknown')"
    print $"   Digest: ($metadata.oci_digest)"
    print $"   Created: ($metadata.created)"
    print $"   Size: ($metadata.size) bytes"
}

# Helper to get OCI extension metadata
def get-oci-extension-metadata [
    extension_name: string
    version: string
    config: record
]: nothing -> record {
    use ../core/nulib/lib_provisioning/extensions/discovery.nu get-oci-extension-metadata

    get-oci-extension-metadata $extension_name $version $config
}
