#!/usr/bin/env nu

# Migrate images between registries

export def main [
    --source-registry: string = "localhost:5000"
    --source-type: string = "zot"
    --dest-registry: string = "localhost:5001"
    --dest-type: string = "harbor"
    --namespaces: list<string> = []
    --dry-run
    --use-skopeo
] {
    print "🔄 Migrating registry images..."
    print $"   Source: ($source_registry) \(($source_type)\)"
    print $"   Destination: ($dest_registry) \(($dest_type)\)"

    if $dry_run {
        print "   Mode: DRY RUN (no actual migration)"
    }

    print ""

    # Get source catalog
    print "📋 Fetching source catalog..."
    let source_repos = get-registry-repos $source_registry

    if ($source_repos | is-empty) {
        print "❌ No repositories found in source registry"
        return
    }

    print $"   Found ($source_repos | length) repositories"

    # Filter by namespaces if specified
    let repos_to_migrate = if ($namespaces | is-empty) {
        $source_repos
    } else {
        $source_repos | where { |repo|
            let ns = ($repo | split row "/" | first)
            $ns in $namespaces
        }
    }

    print $"   Will migrate ($repos_to_migrate | length) repositories\n"

    # Migrate each repository
    mut success = 0
    mut failed = 0

    for repo in $repos_to_migrate {
        print $"Migrating: ($repo)"

        # Get tags for repository
        let tags = get-repo-tags $source_registry $repo

        if ($tags | is-empty) {
            print "  ⚠️  No tags found, skipping"
            continue
        }

        print $"  Tags: ($tags | str join ', ')"

        # Migrate each tag
        for tag in $tags {
            let source_image = $"($source_registry)/($repo):($tag)"
            let dest_image = $"($dest_registry)/($repo):($tag)"

            if $dry_run {
                print $"  [DRY RUN] Would migrate: ($source_image) → ($dest_image)"
                $success = ($success + 1)
            } else {
                if $use_skopeo {
                    # Use skopeo for migration
                    if (migrate-with-skopeo $source_image $dest_image) {
                        print $"  ✓ Migrated: ($tag)"
                        $success = ($success + 1)
                    } else {
                        print $"  ✗ Failed: ($tag)"
                        $failed = ($failed + 1)
                    }
                } else {
                    # Use Docker for migration
                    if (migrate-with-docker $source_image $dest_image) {
                        print $"  ✓ Migrated: ($tag)"
                        $success = ($success + 1)
                    } else {
                        print $"  ✗ Failed: ($tag)"
                        $failed = ($failed + 1)
                    }
                }
            }
        }

        print ""
    }

    # Summary
    print "=" * 60
    print $"Migration Summary:"
    print $"  Successfully migrated: ($success) images"
    print $"  Failed: ($failed) images"

    if $dry_run {
        print "\nℹ This was a dry run. Use without --dry-run to actually migrate."
    }

    if $failed > 0 {
        print "\n⚠️  Some migrations failed. Check logs above."
    } else {
        print "\n✅ All migrations completed successfully!"
    }
}

def get-registry-repos [registry_url: string] -> list {
    let result = (do {
        http get $"http://($registry_url)/v2/_catalog"
    } | complete)

    if $result.exit_code != 0 {
        return []
    }

    let catalog = ($result.stdout | from json)
    $catalog.repositories
}

def get-repo-tags [registry_url: string, repo: string] -> list {
    let result = (do {
        http get $"http://($registry_url)/v2/($repo)/tags/list"
    } | complete)

    if $result.exit_code != 0 {
        return []
    }

    let tags_data = ($result.stdout | from json)
    $tags_data.tags | default []
}

def migrate-with-docker [source: string, dest: string] -> bool {
    # Pull from source
    let pull_result = (^docker pull $source | complete)
    if $pull_result.exit_code != 0 {
        print $"    Error pulling: ($pull_result.stderr)"
        return false
    }

    # Tag for destination
    let tag_result = (^docker tag $source $dest | complete)
    if $tag_result.exit_code != 0 {
        print $"    Error tagging: ($tag_result.stderr)"
        return false
    }

    # Push to destination
    let push_result = (^docker push $dest | complete)
    if $push_result.exit_code != 0 {
        print $"    Error pushing: ($push_result.stderr)"
        return false
    }

    # Cleanup local image
    ^docker rmi $source | ignore
    ^docker rmi $dest | ignore

    return true
}

def migrate-with-skopeo [source: string, dest: string] -> bool {
    # Check if skopeo is available
    let skopeo_check = (^skopeo --version | complete)
    if $skopeo_check.exit_code != 0 {
        print "    Error: skopeo not found"
        return false
    }

    # Use skopeo copy
    let copy_result = (^skopeo copy \
        $"docker://($source)" \
        $"docker://($dest)" \
        --insecure-policy | complete)

    if $copy_result.exit_code != 0 {
        print $"    Error: ($copy_result.stderr)"
        return false
    }

    return true
}

# Sync specific namespace
export def "sync namespace" [
    namespace: string
    --source-registry: string = "localhost:5000"
    --dest-registry: string = "localhost:5001"
    --dry-run
] {
    main --source-registry $source_registry \
         --dest-registry $dest_registry \
         --namespaces [$namespace] \
         --dry-run=$dry_run
}

# Sync all namespaces
export def "sync all" [
    --source-registry: string = "localhost:5000"
    --dest-registry: string = "localhost:5001"
    --dry-run
] {
    let namespaces = [
        "provisioning-extensions"
        "provisioning-kcl"
        "provisioning-platform"
        "provisioning-test"
    ]

    for ns in $namespaces {
        print $"\n🔄 Syncing namespace: ($ns)"
        sync namespace $ns --source-registry $source_registry \
                           --dest-registry $dest_registry \
                           --dry-run=$dry_run
    }
}

# Backup registry to tar files
export def "backup to-tar" [
    --registry-url: string = "localhost:5000"
    --output-dir: string = "./registry-backup"
    --namespaces: list<string> = []
] {
    print "💾 Backing up registry to tar files..."
    print $"   Registry: ($registry_url)"
    print $"   Output: ($output_dir)\n"

    # Create output directory
    mkdir $output_dir

    # Get repositories
    let repos = get-registry-repos $registry_url

    let repos_to_backup = if ($namespaces | is-empty) {
        $repos
    } else {
        $repos | where { |repo|
            let ns = ($repo | split row "/" | first)
            $ns in $namespaces
        }
    }

    print $"Backing up ($repos_to_backup | length) repositories...\n"

    for repo in $repos_to_backup {
        print $"Backing up: ($repo)"

        let tags = get-repo-tags $registry_url $repo

        for tag in $tags {
            let image = $"($registry_url)/($repo):($tag)"
            let filename = $"($output_dir)/($repo | str replace '/' '_')_($tag).tar"

            # Pull image
            ^docker pull $image | ignore

            # Save to tar
            let save_result = (^docker save -o $filename $image | complete)

            if $save_result.exit_code == 0 {
                print $"  ✓ Saved: ($filename)"
            } else {
                print $"  ✗ Failed: ($tag)"
            }
        }
    }

    print "\n✅ Backup complete!"
}

# Restore registry from tar files
export def "restore from-tar" [
    --input-dir: string = "./registry-backup"
    --registry-url: string = "localhost:5000"
] {
    print "📥 Restoring registry from tar files..."
    print $"   Input: ($input_dir)"
    print $"   Registry: ($registry_url)\n"

    # Find all tar files
    let tar_files = (ls $"($input_dir)/*.tar" | get name)

    print $"Found ($tar_files | length) tar files\n"

    for tar_file in $tar_files {
        print $"Restoring: ($tar_file)"

        # Load from tar
        let load_result = (^docker load -i $tar_file | complete)

        if $load_result.exit_code != 0 {
            print $"  ✗ Failed to load"
            continue
        }

        # Extract image name from docker load output
        # Format: "Loaded image: localhost:5000/repo:tag"
        let image = ($load_result.stdout | str trim | split row ": " | last)

        # Retag if needed and push
        let target_image = $"($registry_url)/($image | split row '/' | skip 1 | str join '/')"

        ^docker tag $image $target_image | ignore
        let push_result = (^docker push $target_image | complete)

        if $push_result.exit_code == 0 {
            print $"  ✓ Restored and pushed"
        } else {
            print $"  ✗ Failed to push"
        }

        # Cleanup
        ^docker rmi $image | ignore
        ^docker rmi $target_image | ignore
    }

    print "\n✅ Restore complete!"
}
