#!/usr/bin/env nu

# Pack Distribution Script
# Creates distribution archives for built plugins with multi-platform support

# Load environment variables from env file
def load_env_vars [] {
    let env_file = "env"
    if ($env_file | path exists) {
        let content = open $env_file | lines
        | where ($it | str trim | str length) > 0
        | where not ($it | str starts-with "#")
        | each {|line|
            if ($line | str contains "=") {
                let parts = ($line | split row "=")
                if ($parts | length) >= 2 {
                    let key = ($parts | get 0 | str replace "export " "" | str trim)
                    let raw_value = ($parts | get 1 | str trim)
                    # Handle bash-style default values like ${VAR:-default}
                    let value = if ($raw_value | str starts-with "${") and ($raw_value | str contains ":-") {
                        # Extract default value from ${VAR:-default}
                        let parts = ($raw_value | str replace "${" "" | str replace "}" "" | split row ":-")
                        if ($parts | length) >= 2 {
                            $parts | get 1
                        } else {
                            $raw_value
                        }
                    } else {
                        $raw_value
                    }
                    {$key: $value}
                } else {
                    {}
                }
            } else {
                {}
            }
        }
        | reduce -f {} {|item, acc|
            if ($item | is-empty) {
                $acc
            } else {
                $acc | merge $item
            }
        }

        $content
    } else {
        {
            APP_NAME: "nushell-plugins",
            TARGET_PATH: "distribution",
            INSTALL_FILE: "install_nu_plugins.nu",
            INSTALL_BIN_PATH: "/usr/local/bin",
            ARCHIVE_DIR_PATH: "/tmp",
            BIN_ARCHIVES_DIR_PATH: "bin_archives"
        }
    }
}

# Load build targets configuration
def load_targets_config [] {
    if ("etc/build_targets.toml" | path exists) {
        open etc/build_targets.toml
    } else {
        {targets: {}}
    }
}

# Get system architecture info
def get_system_info [] {
    let arch = match (^uname -m) {
        "x86_64" => "amd64",
        "aarch64" => "arm64",
        $arch if ($arch | str starts-with "arm") => "arm64",
        $other => $other
    }

    let platform = match (^uname -s | str downcase) {
        $os if ($os | str contains "linux") => "linux",
        $os if ($os | str contains "darwin") => "darwin",
        $os if ($os | str contains "windows") => "windows",
        $other => $other
    }

    {platform: $platform, arch: $arch, full: $"($platform)-($arch)"}
}

# Get available distribution platforms
def get_available_platforms [base_path: string] {
    let config = load_targets_config

    mut platforms = []

    # Check for single platform in base directory
    let base_plugins = glob ($base_path | path join "nu_plugin_*") | where ($it | path type) == "file"
    if ($base_plugins | length) > 0 {
        let host_info = get_system_info
        $platforms = ($platforms | append {
            name: "host",
            platform_name: $host_info.full,
            path: $base_path,
            plugin_count: ($base_plugins | length),
            description: "Host platform distribution"
        })
    }

    # Check for platform-specific subdirectories
    if ($config.targets | transpose name config | length) > 0 {
        for target in ($config.targets | transpose name config) {
            let platform_path = ($base_path | path join $target.name)
            if ($platform_path | path exists) {
                let platform_plugins = glob ($platform_path | path join "nu_plugin_*") | where ($it | path type) == "file"
                if ($platform_plugins | length) > 0 {
                    $platforms = ($platforms | append {
                        name: $target.name,
                        platform_name: $target.config.platform_name,
                        path: $platform_path,
                        plugin_count: ($platform_plugins | length),
                        description: $target.config.description,
                        archive_format: $target.config.archive_format
                    })
                }
            }
        }
    }

    $platforms
}

# Generate checksums for a file
def generate_checksums [file_path: string] {
    let sha256 = (open $file_path | hash sha256)
    let size = (ls $file_path | get 0.size)

    {
        file: ($file_path | path basename),
        sha256: $sha256,
        size: $size
    }
}

# Check if target directory has required files
def validate_target [target_path: string] {
    let required_files = ["LICENSE", "README"]
    let optional_files = ["env"]
    let plugin_files = (ls $target_path | where name =~ "nu_plugin_" and type == "file" | get name)

    mut issues = []

    if ($plugin_files | length) == 0 {
        $issues = ($issues | append "No plugin binaries found")
    }

    for file in $required_files {
        if not (($target_path | path join $file) | path exists) {
            $issues = ($issues | append $"Required file missing: ($file)")
        }
    }

    {valid: (($issues | length) == 0), issues: $issues, plugin_count: ($plugin_files | length)}
}

# Create archive
def create_archive [target_path: string, archive_path: string, env_vars: record] {
    # Copy env file to target temporarily
    if ("env" | path exists) {
        cp env ($target_path | path join "env")
    }

    # Get plugin files using glob
    let plugin_files = glob ($target_path | path join "nu_plugin_*") | where ($it | path type) == "file"

    let metadata_files = [
        ($target_path | path join $env_vars.INSTALL_FILE),
        ($target_path | path join "LICENSE"),
        ($target_path | path join "README"),
        ($target_path | path join "env")
    ]

    # Filter metadata files that actually exist and combine with plugin files
    let existing_metadata_files = $metadata_files | where ($it | path exists)
    let existing_files = $plugin_files | append $existing_metadata_files

    if ($existing_files | length) == 0 {
        error make {msg: "No files found to archive"}
    }

    print $"📦 Creating archive with ($existing_files | length) files..."

    try {
        # Use tar to create the archive
        tar czf $archive_path ...$existing_files

        # Clean up temporary env file
        if (($target_path | path join "env") | path exists) {
            rm ($target_path | path join "env")
        }

        let archive_size = ls $archive_path | get 0.size
        print $"✅ Archive created: ($archive_path) (($archive_size))"

        true
    } catch {|err|
        # Clean up on failure
        if ($"($target_path)/env" | path exists) {
            rm $"($target_path)/env"
        }
        error make {msg: $"Failed to create archive: ($err.msg)"}
    }
}

# Main function
def main [
    --target (-t): string = ""              # Override target path
    --platform (-p): string = ""           # Specific platform to package
    --all-platforms (-a)                   # Package all available platforms
    --output (-o): string = ""              # Override output archive path
    --archive-dir: string = ""              # Override archive directory
    --force (-f)                           # Force overwrite existing archive
    --list (-l)                            # List what would be archived
    --list-platforms                       # List available platforms
    --checksums                            # Generate checksums file
] {
    # Ensure we're in the repository root directory
    if not ("nu_plugin_clipboard" | path exists) {
        error make {msg: "Please run this script from the nushell-plugins repository root directory"}
    }

    print "📦 Nushell Plugin Distribution Packager"

    # Load environment variables
    let env_vars = load_env_vars
    let base_target_path = if ($target | str length) > 0 { $target } else { $env_vars.TARGET_PATH }
    let archive_dir_path = if ($archive_dir | str length) > 0 { $archive_dir } else { $env_vars.BIN_ARCHIVES_DIR_PATH? | default "bin_archives" }

    # Validate base target directory
    if not ($base_target_path | path exists) {
        print $"❌ Target directory not found: ($base_target_path)"
        print "💡 Run 'just collect' first to collect plugins"
        exit 1
    }

    # Get available platforms
    let available_platforms = get_available_platforms $base_target_path

    # List platforms mode
    if $list_platforms {
        if ($available_platforms | length) == 0 {
            print "❓ No platforms found for packaging"
            print "💡 Run 'just collect --all-platforms' first"
        } else {
            print "📊 Available platforms for packaging:"
            for platform in $available_platforms {
                let format = $platform | get archive_format? | default "tar.gz"
                print $"  ✅ ($platform.name) ($platform.platform_name): ($platform.plugin_count) plugins → .($format)"
                print $"      Path: ($platform.path)"
                print $"      ($platform.description)"
            }
        }
        return
    }

    # Determine platforms to package
    let platforms_to_package = if $all_platforms {
        $available_platforms
    } else if ($platform | str length) > 0 {
        let selected = $available_platforms | where name == $platform
        if ($selected | length) == 0 {
            print $"❌ Platform not found: ($platform)"
            print $"💡 Available platforms: ($available_platforms | get name | str join ', ')"
            exit 1
        }
        $selected
    } else {
        # Default to host platform or first available
        let host_platform = $available_platforms | where name == "host"
        if ($host_platform | length) > 0 {
            $host_platform
        } else {
            $available_platforms | first 1
        }
    }

    if ($platforms_to_package | length) == 0 {
        print "❓ No platforms to package"
        print "💡 Run 'just collect' first to collect plugins"
        exit 1
    }

    print $"🎯 Platforms to package: ($platforms_to_package | length)"
    for platform in $platforms_to_package {
        print $"  - ($platform.name): ($platform.plugin_count) plugins"
    }

    # Create archive directory if needed
    if not ($archive_dir_path | path exists) {
        mkdir $archive_dir_path
        print $"📁 Created archive directory: ($archive_dir_path)"
    }

    # List mode
    if $list {
        print "\n📋 Files that would be archived:"
        for platform in $platforms_to_package {
            print $"\n($platform.name) ($platform.platform_name):"
            let files = ls $platform.path | where type == "file"
            for file in $files {
                print $"  📄 ($file.name) (($file.size))"
            }
        }
        return
    }

    # Package each platform
    mut created_archives = []
    mut total_size = 0

    for platform in $platforms_to_package {
        let platform_name = $platform.platform_name
        let platform_path = $platform.path
        let archive_format = $platform | get archive_format? | default "tar.gz"

        # Validate platform directory
        let validation = validate_target $platform_path
        if not $validation.valid {
            print $"❌ Platform directory validation failed for ($platform.name):"
            for issue in $validation.issues {
                print $"  - ($issue)"
            }
            continue
        }

        # Create archive name (plugins-only format)
        let version = try {
            open ./nushell/Cargo.toml | get package.version
        } catch {
            "unknown"
        }
        let archive_name = $"plugins-only-($version)-($platform_name).($archive_format)"
        let archive_path = if ($output | str length) > 0 and ($platforms_to_package | length) == 1 {
            $output
        } else {
            $"($archive_dir_path)/($archive_name)"
        }

        # Check if archive already exists
        if ($archive_path | path exists) and not $force {
            print $"⚠️  Archive already exists: ($archive_path)"
            if ($platforms_to_package | length) == 1 {
                let confirm = input "Overwrite? [y/N]: "
                if ($confirm | str downcase) != "y" {
                    print "❌ Skipping ($platform.name)"
                    continue
                }
            } else {
                print "🔄 Overwriting existing archive (use --force to skip this check)"
            }
        }

        print $"\n📦 Creating archive for ($platform.name) ($platform_name)..."
        print $"📁 Source: ($platform_path)"
        print $"📦 Archive: ($archive_path)"

        try {
            create_archive $platform_path $archive_path $env_vars
            let archive_size = ls $archive_path | get 0.size
            $total_size = $total_size + ($archive_size | into int)

            $created_archives = ($created_archives | append {
                platform: $platform.name,
                platform_name: $platform_name,
                archive_path: $archive_path,
                size: $archive_size,
                plugin_count: $platform.plugin_count
            })

            print $"✅ Created archive for ($platform.name): ($archive_size)"
        } catch {|err|
            print $"❌ Failed to create archive for ($platform.name): ($err.msg)"
        }
    }

    # Generate checksums if requested
    if $checksums and ($created_archives | length) > 0 {
        print "\n🔐 Generating checksums..."
        let checksums_file = $"($archive_dir_path)/checksums.txt"

        mut checksum_data = []
        for archive in $created_archives {
            let checksum_info = generate_checksums $archive.archive_path
            $checksum_data = ($checksum_data | append $checksum_info)
            print $"  ✅ ($checksum_info.file): ($checksum_info.sha256)"
        }

        # Write checksums file
        let checksums_content = ($checksum_data | each {|item|
            $"($item.sha256)  ($item.file)"
        } | str join "\n")

        $checksums_content | save $checksums_file
        print $"💾 Checksums saved to: ($checksums_file)"
    }

    # Summary
    print $"\n🎉 Distribution packaging completed!"
    print $"📁 Archive directory: ($archive_dir_path)"
    print $"📦 Archives created: ($created_archives | length)"
    print $"📏 Total size: ($total_size | into filesize)"

    if ($created_archives | length) > 0 {
        print "\n✅ Created archives:"
        for archive in $created_archives {
            print $"  - ($archive.platform_name): ($archive.size) ($archive.plugin_count) plugins"
            print $"    📦 ($archive.archive_path)"
        }
    }

    print "\n💡 Next steps:"
    if ($created_archives | length) > 1 {
        print "  1. Test archives: tar -tzf bin_archives/*.tar.gz"
        print "  2. Upload to GitHub Releases or distribution server"
        print "  3. Update installation documentation"
    } else if ($created_archives | length) == 1 {
        let archive = $created_archives.0
        print $"  1. Test archive: tar -tzf ($archive.archive_path)"
        print $"  2. Distribute: Upload or copy to target systems"
        print $"  3. Install: Extract and run the installation script"
    }
}

if ($env.NUSHELL_EXECUTION_CONTEXT? | default "" | str contains "run") {
    main
}