# Migration Tool: Monorepo to Multi-Repo with OCI
# Helps migrate from monorepo structure to OCI-based dependency management
# Version: 1.0.0

use ../core/nulib/lib_provisioning/config/loader.nu get-config
use ../core/nulib/lib_provisioning/oci/client.nu *
use std log

# Migrate workspace from local extensions to OCI
export def migrate-workspace [
    workspace_name: string    # Workspace to migrate
    --registry: string = "localhost:5000"  # Target OCI registry
    --namespace: string = "provisioning-extensions"  # Target namespace
    --publish                 # Publish extensions to OCI during migration
    --dry-run                 # Show what would be done without making changes
]
{
    log info $"Migrating workspace: ($workspace_name) to OCI registry"

    let config = (get-config)

    # Find workspace directory
    let workspace_path = ($config.paths.base | path join ".." $"workspace_($workspace_name)")

    if not ($workspace_path | path exists) {
        error make {
            msg: $"Workspace not found: ($workspace_path)"
        }
    }

    # Analyze current extensions
    let extensions = (analyze-extensions $workspace_path)

    log info $"Found ($extensions | length) extensions to migrate"

    # Publish extensions to OCI if requested
    mut published = []
    if $publish and not $dry_run {
        $published = (publish-extensions $extensions $registry $namespace)
    }

    # Update workspace configuration
    let new_config = (generate-oci-config $extensions $registry $namespace)

    if not $dry_run {
        # Backup current config
        let config_path = ($workspace_path | path join "config" "provisioning.yaml")
        let backup_path = ($config_path + ".backup")

        if ($config_path | path exists) {
            cp $config_path $backup_path
            log info $"Backed up config to ($backup_path)"
        }

        # Write new config
        $new_config | to yaml | save -f $config_path
        log info $"Updated workspace configuration"
    }

    # Generate migration report
    {
        workspace: $workspace_name
        extensions_found: ($extensions | length)
        extensions_published: ($published | length)
        new_config: $new_config
        dry_run: $dry_run
    }
}

# Analyze extensions in workspace
def analyze-extensions [
    workspace_path: string
] -> list<record> {
    mut extensions = []

    # Check for providers
    let providers_path = ($workspace_path | path join "extensions" "providers")
    if ($providers_path | path exists) {
        let provider_dirs = (ls $providers_path | where type == "dir")

        for provider in $provider_dirs {
            let manifest = (load-manifest ($provider.name | path join "manifest.yaml"))

            if not ($manifest | is-empty) {
                $extensions = ($extensions | append {
                    type: "provider"
                    name: ($provider.name | path basename)
                    path: $provider.name
                    version: ($manifest.version? | default "1.0.0")
                    manifest: $manifest
                })
            }
        }
    }

    # Check for taskservs
    let taskservs_path = ($workspace_path | path join "extensions" "taskservs")
    if ($taskservs_path | path exists) {
        let taskserv_dirs = (ls $taskservs_path | where type == "dir")

        for taskserv in $taskserv_dirs {
            let manifest = (load-manifest ($taskserv.name | path join "manifest.yaml"))

            if not ($manifest | is-empty) {
                $extensions = ($extensions | append {
                    type: "taskserv"
                    name: ($taskserv.name | path basename)
                    path: $taskserv.name
                    version: ($manifest.version? | default "1.0.0")
                    manifest: $manifest
                })
            }
        }
    }

    # Check for clusters
    let clusters_path = ($workspace_path | path join "extensions" "clusters")
    if ($clusters_path | path exists) {
        let cluster_dirs = (ls $clusters_path | where type == "dir")

        for cluster in $cluster_dirs {
            let manifest = (load-manifest ($cluster.name | path join "manifest.yaml"))

            if not ($manifest | is-empty) {
                $extensions = ($extensions | append {
                    type: "cluster"
                    name: ($cluster.name | path basename)
                    path: $cluster.name
                    version: ($manifest.version? | default "1.0.0")
                    manifest: $manifest
                })
            }
        }
    }

    $extensions
}

# Load manifest if exists
def load-manifest [
    manifest_path: string
]
{
    if ($manifest_path | path exists) {
        let result = (do {
            open $manifest_path | from yaml
        } | complete)

        if $result.exit_code != 0 {
            {}
        } else {
            $result.stdout
        }
    } else {
        {}
    }
}

# Publish extensions to OCI registry
def publish-extensions [
    extensions: list<record>
    registry: string
    namespace: string
] -> list<record> {
    mut published = []

    for ext in $extensions {
        log info $"Publishing ($ext.type)/($ext.name):($ext.version)"

        let push_result = (do {
            let result = (push-artifact $ext.path $registry $namespace $ext.name $ext.version)

            if $result {
                $published = ($published | append {
                    name: $ext.name
                    type: $ext.type
                    version: $ext.version
                    reference: $"($registry)/($namespace)/($ext.name):($ext.version)"
                })
                log info $"  ✓ Published ($ext.name):($ext.version)"
            } else {
                log error $"  ✗ Failed to publish ($ext.name):($ext.version)"
            }
        } | complete)

        if $push_result.exit_code != 0 {
            log error $"  ✗ Error publishing ($ext.name): ($push_result.stderr)"
        }
    }

    $published
}

# Generate OCI-based configuration
def generate-oci-config [
    extensions: list<record>
    registry: string
    namespace: string
]
{
    # Group extensions by type
    let providers = ($extensions | where type == "provider" | each { |ext|
        $"oci://($registry)/($namespace)/($ext.name):($ext.version)"
    })

    let taskservs = ($extensions | where type == "taskserv" | each { |ext|
        $"oci://($registry)/($namespace)/($ext.name):($ext.version)"
    })

    let clusters = ($extensions | where type == "cluster" | each { |ext|
        $"oci://($registry)/($namespace)/($ext.name):($ext.version)"
    })

    {
        dependencies: {
            extensions: {
                source_type: "oci"
                oci: {
                    registry: $registry
                    namespace: $namespace
                    tls_enabled: false
                    auth_token_path: "~/.provisioning/tokens/oci"
                }
                modules: {
                    providers: $providers
                    taskservs: $taskservs
                    clusters: $clusters
                }
            }
        }
    }
}

# Validate migration
export def validate-migration [
    workspace_name: string
]
{
    log info $"Validating migration for workspace: ($workspace_name)"

    let config = (get-config)

    # Check if workspace uses OCI
    let uses_oci = ($config.dependencies?.extensions?.source_type? == "oci")

    if not $uses_oci {
        return {
            valid: false
            error: "Workspace not configured for OCI"
        }
    }

    # Check if all extensions are accessible
    let modules = ($config.dependencies.extensions.modules)

    mut accessible = []
    mut inaccessible = []

    # Check providers
    if ($modules.providers? | is-not-empty) {
        for provider in $modules.providers {
            let parts = ($provider | parse "oci://{registry}/{namespace}/{name}:{version}")

            if not ($parts | is-empty) {
                let reg = ($parts | first | get registry)
                let ns = ($parts | first | get namespace)
                let name = ($parts | first | get name)
                let version = ($parts | first | get version)

                if (artifact-exists $reg $ns $name $version) {
                    $accessible = ($accessible | append $name)
                } else {
                    $inaccessible = ($inaccessible | append $name)
                }
            }
        }
    }

    # Check taskservs
    if ($modules.taskservs? | is-not-empty) {
        for taskserv in $modules.taskservs {
            let parts = ($taskserv | parse "oci://{registry}/{namespace}/{name}:{version}")

            if not ($parts | is-empty) {
                let reg = ($parts | first | get registry)
                let ns = ($parts | first | get namespace)
                let name = ($parts | first | get name)
                let version = ($parts | first | get version)

                if (artifact-exists $reg $ns $name $version) {
                    $accessible = ($accessible | append $name)
                } else {
                    $inaccessible = ($inaccessible | append $name)
                }
            }
        }
    }

    {
        valid: ($inaccessible | is-empty)
        accessible: $accessible
        inaccessible: $inaccessible
        total: (($accessible | length) + ($inaccessible | length))
    }
}

# Rollback migration
export def rollback-migration [
    workspace_name: string
] -> nothing {
    log warning $"Rolling back migration for workspace: ($workspace_name)"

    let config = (get-config)

    let workspace_path = ($config.paths.base | path join ".." $"workspace_($workspace_name)")
    let config_path = ($workspace_path | path join "config" "provisioning.yaml")
    let backup_path = ($config_path + ".backup")

    if not ($backup_path | path exists) {
        error make {
            msg: "No backup found. Cannot rollback."
        }
    }

    # Restore backup
    cp $backup_path $config_path

    log info $"✓ Configuration restored from backup"
    log info $"  Backup preserved at: ($backup_path)"
}

# Generate migration report
export def migration-report [
    workspace_name: string
] -> nothing {
    log info $"Generating migration report for: ($workspace_name)"

    let validation = (validate-migration $workspace_name)

    print "\n=== Migration Report ==="
    print $"Workspace: ($workspace_name)"
    print $"Valid: ($validation.valid)"
    print $"Total Extensions: ($validation.total)"
    print $"Accessible: ($validation.accessible | length)"
    print $"Inaccessible: ($validation.inaccessible | length)"

    if not ($validation.inaccessible | is-empty) {
        print "\nInaccessible Extensions:"
        for ext in $validation.inaccessible {
            print $"  - ($ext)"
        }
    }

    print "\n"
}
