# Workspace Migration Framework
# Handles workspace migrations between versions with backups and rollback

use std log
use ../user/config.nu *
use version.nu *

# Migration strategy definitions
export def get-migration-strategies [] {
    {
        # Migration from no metadata to 2.0.5
        "unknown_to_2.0.5": {
            name: "Initialize metadata"
            description: "Add metadata tracking to existing workspace"
            from: "unknown"
            to: "2.0.5"
            breaking: false
            auto_migrate: true
        }

        # Migration from 2.0.0 to 2.0.5
        "2.0.0_to_2.0.5": {
            name: "Workspace switching update"
            description: "Add workspace switching support and metadata"
            from: "2.0.0"
            to: "2.0.5"
            breaking: false
            auto_migrate: true
        }

        # Future migration template
        "2.0.5_to_3.0.0": {
            name: "Major version update"
            description: "Migrate to v3.0.0 with breaking changes"
            from: "2.0.5"
            to: "3.0.0"
            breaking: true
            auto_migrate: false
        }
    }
}

# Find applicable migration path
export def find-migration-path [
    from_version: string
    to_version: string
] {
    let strategies = (get-migration-strategies)

    mut path = []

    # Handle unknown version (needs metadata initialization)
    if $from_version == "unknown" {
        $path = ($path | append {
            strategy: "unknown_to_2.0.5"
            details: $strategies.unknown_to_2.0.5
        })
        return $path
    }

    # Direct migration available
    let strategy_key = $"($from_version)_to_($to_version)"
    if ($strategy_key in $strategies) {
        $path = ($path | append {
            strategy: $strategy_key
            details: ($strategies | get $strategy_key)
        })
        return $path
    }

    # Multi-step migration needed (for now, return empty - can be enhanced)
    return $path
}

# Create workspace backup
export def create-workspace-backup [
    workspace_path: string
    backup_reason: string
] {
    let workspace_name = ($workspace_path | path basename)
    let timestamp = (date now | format date "%Y%m%d_%H%M%S")
    let backup_name = $"($workspace_name)_backup_($timestamp)"

    let backup_dir = ($workspace_path | path dirname | path join ".workspace_backups")

    # Create backup directory
    if not ($backup_dir | path exists) {
        mkdir $backup_dir
    }

    let backup_path = ($backup_dir | path join $backup_name)

    print $"(ansi cyan)Creating backup...(ansi reset)"

    # Attempt the backup operation
    let backup_result = (do {
        # Copy workspace to backup
        cp -r $workspace_path $backup_path

        # Create backup metadata
        let backup_metadata = {
            original_path: $workspace_path
            backup_path: $backup_path
            timestamp: $timestamp
            reason: $backup_reason
            created: (date now | format date "%Y-%m-%dT%H:%M:%SZ")
        }

        $backup_metadata | to yaml | save -f ($backup_path | path join ".backup_info.yaml")

        {success: true, backup_path: $backup_path, metadata: $backup_metadata}
    } | complete)

    if $backup_result.exit_code == 0 {
        print $"(ansi green)✓(ansi reset) Backup created: [$backup_path]"
        $backup_result.stdout
    } else {
        print $"(ansi red)✗(ansi reset) Backup failed: ($backup_result.stderr)"
        {
            success: false
            error: $backup_result.stderr
        }
    }
}

# Migrate workspace metadata (unknown to 2.0.5)
export def migrate-unknown-to-2_0_5 [
    workspace_path: string
    workspace_name: string
] {
    print $"(ansi cyan)Migrating workspace to version 2.0.5...(ansi reset)"

    let result = (do {
        # Initialize metadata
        let metadata = (init-workspace-metadata $workspace_path $workspace_name)

        # Add migration record
        add-migration-record $workspace_path "unknown" "2.0.5" "metadata_initialization" true "Initial metadata creation"

        {
            success: true
            message: "Workspace migrated to version 2.0.5"
            metadata: $metadata
        }
    } | complete)

    if $result.exit_code == 0 {
        $result.stdout
    } else {
        {
            success: false
            error: $result.stderr
            message: "Migration failed"
        }
    }
}

# Migrate workspace from 2.0.0 to 2.0.5
export def migrate-2_0_0-to-2_0_5 [
    workspace_path: string
    workspace_name: string
] {
    print $"(ansi cyan)Migrating workspace from 2.0.0 to 2.0.5...(ansi reset)"

    let result = (do {
        # Check if metadata exists
        let metadata_path = (get-workspace-metadata-path $workspace_path)

        if ($metadata_path | path exists) {
            # Update existing metadata
            let metadata = (load-workspace-metadata $workspace_path)
            add-migration-record $workspace_path "2.0.0" "2.0.5" "version_update" true "Updated to workspace switching support"
        } else {
            # Create new metadata
            let metadata = (init-workspace-metadata $workspace_path $workspace_name)
            add-migration-record $workspace_path "2.0.0" "2.0.5" "metadata_creation" true "Added metadata tracking"
        }

        {
            success: true
            message: "Workspace migrated to version 2.0.5"
        }
    } | complete)

    if $result.exit_code == 0 {
        $result.stdout
    } else {
        {
            success: false
            error: $result.stderr
            message: "Migration failed"
        }
    }
}

# Execute migration strategy
export def execute-migration [
    workspace_path: string
    workspace_name: string
    strategy: record
] {
    print ""
    print $"(ansi green_bold)Migration Strategy:(ansi reset) ($strategy.details.name)"
    print $"(ansi cyan)Description:(ansi reset) ($strategy.details.description)"
    print $"(ansi cyan)From:(ansi reset) ($strategy.details.from) → (ansi cyan)To:(ansi reset) ($strategy.details.to)"
    print ""

    match $strategy.strategy {
        "unknown_to_2.0.5" => {
            migrate-unknown-to-2_0_5 $workspace_path $workspace_name
        }

        "2.0.0_to_2.0.5" => {
            migrate-2_0_0-to-2_0_5 $workspace_path $workspace_name
        }

        _ => {
            return {
                success: false
                error: $"Unknown migration strategy: ($strategy.strategy)"
            }
        }
    }
}

# Main workspace migration function
export def migrate-workspace [
    workspace_name: string
    --skip-backup (-s)       # Skip backup creation
    --force (-f)             # Force migration without confirmation
    --target-version: string # Target version (default: current system version)
] {
    print ""
    print $"(ansi green_bold)Workspace Migration(ansi reset)"
    print ""

    # Validate workspace exists
    if not (validate-workspace-exists $workspace_name) {
        return {
            success: false
            error: $"Workspace '($workspace_name)' not found in registry"
        }
    }

    # Get workspace path
    let workspace_path = (get-workspace-path $workspace_name)

    if not ($workspace_path | path exists) {
        return {
            success: false
            error: $"Workspace directory not found: ($workspace_path)"
        }
    }

    print $"(ansi cyan)Workspace:(ansi reset) ($workspace_name)"
    print $"(ansi cyan)Path:(ansi reset) ($workspace_path)"
    print ""

    # Load current metadata
    let metadata = (load-workspace-metadata $workspace_path)
    let current_version = $metadata.version.provisioning
    let system_version = (get-system-version)
    let target = ($target_version | default $system_version)

    print $"(ansi cyan)Current version:(ansi reset) ($current_version)"
    print $"(ansi cyan)Target version:(ansi reset) ($target)"
    print ""

    # Find migration path
    let migration_path = (find-migration-path $current_version $target)

    if ($migration_path | is-empty) {
        if $current_version == $target {
            return {
                success: true
                message: "Workspace is already at target version"
                no_migration_needed: true
            }
        } else {
            return {
                success: false
                error: $"No migration path found from ($current_version) to ($target)"
            }
        }
    }

    # Check for breaking changes
    let has_breaking = ($migration_path | any { |m| $m.details.breaking })

    if $has_breaking {
        print $"(ansi yellow_bold)⚠ Warning: This migration includes breaking changes(ansi reset)"
        print ""
    }

    # Confirm migration
    if not $force {
        print $"(ansi yellow)This will migrate the workspace from ($current_version) to ($target)(ansi reset)"

        if not $skip_backup {
            print $"(ansi cyan)A backup will be created before migration.(ansi reset)"
        }

        print ""

        let response = (input "Continue with migration? (y/N): ")

        if $response != "y" and $response != "Y" {
            print $"(ansi yellow)Migration cancelled(ansi reset)"
            return {
                success: false
                cancelled: true
            }
        }

        print ""
    }

    # Create backup
    mut backup_result = {success: false}

    if not $skip_backup {
        $backup_result = (create-workspace-backup $workspace_path "pre_migration")

        if not $backup_result.success {
            print $"(ansi red)✗(ansi reset) Cannot proceed without backup"
            return {
                success: false
                error: "Backup creation failed"
                backup_error: $backup_result.error
            }
        }

        print ""
    }

    # Execute migrations
    mut migration_results = []

    for migration in $migration_path {
        let result = (execute-migration $workspace_path $workspace_name $migration)

        $migration_results = ($migration_results | append $result)

        if not $result.success {
            print ""
            print $"(ansi red_bold)✗ Migration failed(ansi reset)"
            print $"(ansi red)Error:(ansi reset) ($result.error)"
            print ""

            if ($backup_result != null) {
                print $"(ansi cyan)Backup available at:(ansi reset) ($backup_result.backup_path)"
                print $"(ansi cyan)You can restore manually if needed.(ansi reset)"
            }

            print ""

            return {
                success: false
                error: $result.error
                backup: $backup_result
                partial_results: $migration_results
            }
        }

        print $"(ansi green)✓(ansi reset) ($migration.details.name) completed"
    }

    print ""
    print $"(ansi green_bold)✓ Migration completed successfully(ansi reset)"
    print ""

    return {
        success: true
        message: $"Workspace migrated from ($current_version) to ($target)"
        backup: $backup_result
        migrations: $migration_results
    }
}

# List available workspace backups
export def list-workspace-backups [
    workspace_name?: string
] {
    let workspace_path = if ($workspace_name | is-not-empty) {
        get-workspace-path $workspace_name
    } else {
        null
    }

    let backup_base_dir = if ($workspace_path | is-not-empty) {
        $workspace_path | path dirname | path join ".workspace_backups"
    } else {
        # List all backups in common locations
        return []
    }

    if not ($backup_base_dir | path exists) {
        return []
    }

    ls $backup_base_dir
    | where ($it.type == "dir")
    | each { |backup|
        let info_file = ($backup.name | path join ".backup_info.yaml")

        if ($info_file | path exists) {
            let info = (open $info_file)
            {
                name: ($backup.name | path basename)
                path: $backup.name
                created: $info.created
                reason: $info.reason
                size: $backup.size
            }
        } else {
            {
                name: ($backup.name | path basename)
                path: $backup.name
                created: "unknown"
                reason: "unknown"
                size: $backup.size
            }
        }
    }
}

# Restore workspace from backup
export def restore-workspace-from-backup [
    backup_path: string
    --force (-f)  # Force restore without confirmation
] {
    if not ($backup_path | path exists) {
        return {
            success: false
            error: $"Backup not found: ($backup_path)"
        }
    }

    # Load backup info
    let info_file = ($backup_path | path join ".backup_info.yaml")

    if not ($info_file | path exists) {
        return {
            success: false
            error: "Backup metadata not found - cannot restore safely"
        }
    }

    let info = (open $info_file)
    let original_path = $info.original_path

    print ""
    print $"(ansi green_bold)Restore Workspace from Backup(ansi reset)"
    print ""
    print $"(ansi cyan)Backup:(ansi reset) ($backup_path)"
    print $"(ansi cyan)Original path:(ansi reset) ($original_path)"
    print $"(ansi cyan)Created:(ansi reset) ($info.created)"
    print $"(ansi cyan)Reason:(ansi reset) ($info.reason)"
    print ""

    # Confirm restore
    if not $force {
        print $"(ansi yellow)⚠ This will replace the current workspace at:(ansi reset)"
        print $"  ($original_path)"
        print ""

        let response = (input "Continue with restore? (y/N): ")

        if $response != "y" and $response != "Y" {
            print $"(ansi yellow)Restore cancelled(ansi reset)"
            return {
                success: false
                cancelled: true
            }
        }

        print ""
    }

    let result = (do {
        # Remove current workspace
        if ($original_path | path exists) {
            rm -rf $original_path
        }

        # Restore from backup
        cp -r $backup_path $original_path

        # Remove backup metadata from restored workspace
        let restored_info = ($original_path | path join ".backup_info.yaml")
        if ($restored_info | path exists) {
            rm $restored_info
        }

        {
            success: true
            restored_path: $original_path
        }
    } | complete)

    if $result.exit_code == 0 {
        print $"(ansi green)✓(ansi reset) Workspace restored from backup"
        print ""
        $result.stdout
    } else {
        print $"(ansi red)✗(ansi reset) Restore failed: ($result.stderr)"

        {
            success: false
            error: $result.stderr
        }
    }
}
