#!/usr/bin/env nu

# Create Full Distribution - Complete Packaging Workflow
# Creates both full distributions (nushell + plugins) and bin archives (plugins only)
#
# Usage:
#   create_full_distribution.nu                # Current platform
#   create_full_distribution.nu --all-platforms # All platforms
#   create_full_distribution.nu --bin-only      # Only create bin archives

use lib/common_lib.nu *

# Main entry point
def main [
    --all-platforms               # Create packages for all platforms
    --bin-only                    # Only create bin archives
    --checksums                   # Generate SHA256 checksums
    --verify                      # Verify packages after creation
] {
    # CRITICAL: Ensure we're in the repository root directory
    let current_dir = $env.PWD

    # Check if we're already in the right directory
    let is_repo_root = (
        ("nu_plugin_auth" | path exists) and
        ("nushell" | path exists) and
        ("scripts" | path exists)
    )

    if not $is_repo_root {
        # Try to find the repository root by looking for the marker files
        let script_parent = $current_dir
        let potential_root = ($script_parent | path dirname)

        if not (($potential_root | path join "nu_plugin_auth" | path exists) and
                ($potential_root | path join "nushell" | path exists)) {
            log_error $"❌ Cannot find repository root from current directory: ($current_dir)"
            log_error "Expected to find: nu_plugin_* directories and nushell/ directory"
            exit 1
        }

        try {
            cd $potential_root
            log_info $"✅ Changed directory to repository root: ($potential_root)"
        } catch {
            log_error $"Failed to change to repository root: ($potential_root)"
            exit 1
        }
    } else {
        log_info $"✅ Already in repository root: ($current_dir)"
    }

    print_banner

    if not $bin_only {
        # Phase 1: Collect binaries
        print "╔══════════════════════════════════════════════════════════╗"
        print "║  Phase 1: Collect Binaries                               ║"
        print "╚══════════════════════════════════════════════════════════╝"

        collect_binaries $all_platforms
    }

    # Phase 2: Create full distribution packages
    if not $bin_only {
        print ""
        print "╔══════════════════════════════════════════════════════════╗"
        print "║  Phase 2: Create Full Distribution Packages              ║"
        print "╚══════════════════════════════════════════════════════════╝"

        create_distribution_packages $all_platforms $checksums
    }

    # Phase 3: Create bin archives
    print ""
    print "╔══════════════════════════════════════════════════════════╗"
    print "║  Phase 3: Create Bin Archives                            ║"
    print "╚══════════════════════════════════════════════════════════╝"

    create_bin_archives

    # Phase 4: Verification
    if $verify {
        print ""
        print "╔══════════════════════════════════════════════════════════╗"
        print "║  Phase 4: Verify Packages                                ║"
        print "╚══════════════════════════════════════════════════════════╝"

        verify_packages
    }

    # Final summary
    generate_distribution_summary $all_platforms $bin_only
}

# Print banner
def print_banner [] {
    print $"
(ansi blue)╔════════════════════════════════════════════════════════════╗
║                                                            ║
║     📦 Complete Distribution Creator 📦                    ║
║                                                            ║
║     Creates:                                               ║
║       • Full distribution packages \(nu + plugins\)          ║
║       • Bin archives \(plugins only\)                        ║
║       • Checksums and manifests                            ║
║                                                            ║
╚════════════════════════════════════════════════════════════╝(ansi reset)
"
}

# Collect binaries for distribution
def collect_binaries [all_platforms: bool] {
    log_info "📥 Collecting binaries..."

    if ("./scripts/collect_full_binaries.nu" | path exists) {
        # Use built nu binary, not system nu (which may be broken)
        let built_nu = "./nushell/target/release/nu"
        let nu_bin = if ($built_nu | path exists) { $built_nu } else { "nu" }

        let result = (do {
            if $all_platforms {
                ^$nu_bin ./scripts/collect_full_binaries.nu --all-platforms --force
            } else {
                ^$nu_bin ./scripts/collect_full_binaries.nu --force
            }
        } | complete)

        print $result.stdout

        if ($result.stderr | is-not-empty) {
            print $result.stderr
        }

        if $result.exit_code == 0 {
            log_success "Binaries collected successfully"
        } else {
            log_error $"Failed to collect binaries \(exit code: ($result.exit_code)\)"
            if ($result.stderr | is-not-empty) {
                log_error $result.stderr
            }
            exit 1
        }
    } else {
        log_warn "collect_full_binaries.nu not found, using fallback..."
        collect_binaries_fallback
    }
}

# Fallback binary collection
def collect_binaries_fallback [] {
    let platform = detect_platform

    log_info $"Collecting for platform: ($platform)"

    # Create distribution directory
    let dist_dir = $"./distribution/($platform)"
    ensure_dir $dist_dir

    # Copy nushell binary
    if ("./nushell/target/release/nu" | path exists) {
        cp ./nushell/target/release/nu $dist_dir
        log_success "Copied nushell binary"
    } else {
        log_error "Nushell binary not found. Build it first with: just build-nushell"
        exit 1
    }

    # Copy system plugins
    let system_plugins = (try { ls ./nushell/target/release/nu_plugin_* } catch { [] } | where type == file)
    for plugin in $system_plugins {
        cp $plugin.name $dist_dir
    }

    if ($system_plugins | length) > 0 {
        log_success $"Copied ($system_plugins | length) system plugins"
    }

    # Copy custom plugins
    let custom_plugins = (try { ls nu_plugin_*/target/release/nu_plugin_* } catch { [] } | where type == file)
    for plugin in $custom_plugins {
        cp $plugin.name $dist_dir
    }

    if ($custom_plugins | length) > 0 {
        log_success $"Copied ($custom_plugins | length) custom plugins"
    }

    let total = 1 + ($system_plugins | length) + ($custom_plugins | length)
    log_success $"Total binaries collected: ($total)"
}

# Create distribution packages
def create_distribution_packages [
    all_platforms: bool
    checksums: bool
] {
    log_info "📦 Creating distribution packages..."

    if ("./scripts/create_distribution_packages.nu" | path exists) {
        # Use project binary for consistency
        let built_nu = "./nushell/target/release/nu"
        let nu_bin = if ($built_nu | path exists) { $built_nu } else { "nu" }

        let result = (do {
            if $all_platforms and $checksums {
                ^$nu_bin ./scripts/create_distribution_packages.nu --all-platforms --checksums
            } else if $all_platforms {
                ^$nu_bin ./scripts/create_distribution_packages.nu --all-platforms
            } else if $checksums {
                ^$nu_bin ./scripts/create_distribution_packages.nu --checksums
            } else {
                ^$nu_bin ./scripts/create_distribution_packages.nu
            }
        } | complete)

        print $result.stdout

        if $result.exit_code == 0 {
            log_success "Distribution packages created"
        }
    } else {
        log_warn "create_distribution_packages.nu not found, using justfile..."

        let pack_result = (do {
            if $all_platforms {
                just pack-full-all
            } else {
                just pack-full
            }
        } | complete)

        if $pack_result.exit_code == 0 {
            log_success "Packages created via justfile"
        }
    }
}

# Create bin archives (plugin-only) - directly from built plugins
def create_bin_archives [] {
    log_info "📦 Creating bin archives (plugin-only)..."

    # Create output directory
    if not ("./bin_archives" | path exists) {
        mkdir ./bin_archives
    }

    # Get version for naming
    let version = try {
        open ./nushell/Cargo.toml | get package.version
    } catch {
        "unknown"
    }

    # Get actually built platforms from distribution directory
    let built_platforms = try {
        ls ./distribution/*/nu | each {|p| $p.name | path dirname | path basename}
    } catch {
        []
    }

    # If no platforms found, skip Phase 3
    if ($built_platforms | length) == 0 {
        log_warn "No platforms found in distribution directory - skipping plugin archive creation"
        return
    }

    # Create archive for each built platform
    for platform in $built_platforms {
        let archive_name = $"plugins-only-($version)-($platform).tar.gz"
        let archive_path = (pwd | append "bin_archives" | path join | append $archive_name | path join)

        # Create temporary directory for archive contents
        let temp_dir = (pwd | append "bin_archives" | path join | append $"plugins-temp-($version)-($platform)" | path join)
        mkdir $temp_dir

        # Collect all built plugin binaries (both custom and workspace plugins)
        log_info $"📦 Collecting plugins for ($platform)..."

        # Get custom plugin binaries from nu_plugin_*/target/release/
        let custom_plugins = (
            try {
                ls nu_plugin_*/target/release/nu_plugin_*
                | where type == "file"
                | where ($it.name | str ends-with ".d") == false  # Exclude .d dependency files
            } catch {
                []
            }
        )

        # Get workspace plugin binaries from nushell/target/release/
        let workspace_plugins = (
            try {
                ls nushell/target/release/nu_plugin_*
                | where type == "file"
                | where ($it.name | str ends-with ".d") == false  # Exclude .d dependency files
            } catch {
                []
            }
        )

        # Combine both lists
        let plugins_to_copy = ($custom_plugins | append $workspace_plugins)

        let plugin_count = ($plugins_to_copy | length)

        if $plugin_count > 0 {
            log_success $"Collected ($plugin_count) plugins"

            # Copy plugins to temp directory
            try {
                $plugins_to_copy | each {|p|
                    cp $p.name $temp_dir
                }
            } catch {
                log_error "Failed to copy plugins to temp directory"
            }

            # Create archive using absolute path (tar -C changes directory safely)
            log_info "Creating archive..."
            let tar_result = (do {
                tar -C $temp_dir -czf $archive_path .
            } | complete)

            if $tar_result.exit_code == 0 {
                if ($archive_path | path exists) {
                    let size = (ls $archive_path | get 0.size) / 1024 / 1024
                    log_success $"Archive created: ($archive_name) - (($size | into int))MB"
                } else {
                    log_error $"Archive file exists but cannot be accessed: ($archive_path)"
                }
            } else {
                log_error $"Failed to create archive. Exit code: ($tar_result.exit_code)"
                if ($tar_result.stderr | is-not-empty) {
                    log_error $"Tar error: ($tar_result.stderr)"
                }
            }
        } else {
            log_warn $"No plugins found to package for ($platform)"
        }

        # Clean up temp directory
        if ($temp_dir | path exists) {
            try {
                rm -r $temp_dir
            } catch {
                log_warn "Could not clean up temporary directory"
            }
        }
    }
}

# Verify packages
def verify_packages [] {
    log_info "✅ Verifying packages..."

    # Check distribution packages
    if ("./distribution/packages" | path exists) {
        let packages = (try { ls ./distribution/packages/*.tar.gz ./distribution/packages/*.zip } catch { [] })

        if ($packages | length) > 0 {
            log_success $"Found ($packages | length) distribution packages"

            for pkg in $packages {
                let name = $pkg.name | path basename
                let size = $pkg.size / 1024 / 1024

                if $size < 1 {
                    log_error $"  ✗ ($name): Too small (($size | into int)MB)"
                } else if $size > 500 {
                    log_warn $"  ⚠  ($name): Very large (($size | into int)MB)"
                } else {
                    log_success $"  ✓ ($name): (($size | into int)MB)"
                }
            }
        } else {
            log_warn "No distribution packages found"
        }
    }

    # Check bin archives
    if ("./bin_archives" | path exists) {
        let archives = (try { ls ./bin_archives/*.tar.gz } catch { [] })

        if ($archives | length) > 0 {
            log_success $"Found ($archives | length) bin archives"
        } else {
            log_warn "No bin archives found"
        }
    }

    # Verify checksums if present
    if ("./distribution/packages/checksums.txt" | path exists) {
        log_info "Verifying checksums..."

        let check_result = (do {
            cd distribution/packages
            shasum -c checksums.txt
        } | complete)

        if $check_result.exit_code == 0 {
            log_success "All checksums verified"
        } else {
            log_error "Checksum verification failed"
        }
    }
}

# Generate distribution summary
def generate_distribution_summary [
    all_platforms: bool
    bin_only: bool
] {
    log_info "\n📊 Distribution Summary\n"

    # Get nushell version
    let version = if ("./nushell/Cargo.toml" | path exists) {
        open ./nushell/Cargo.toml | get package.version
    } else {
        "unknown"
    }

    log_success $"Nushell version: ($version)"

    # Count distribution packages
    if not $bin_only and ("./distribution/packages" | path exists) {
        let packages = (try { ls ./distribution/packages/*.tar.gz ./distribution/packages/*.zip } catch { [] })

        if ($packages | length) > 0 {
            log_success $"Distribution packages: ($packages | length)"

            let total_size = ($packages | get size | math sum) / 1024 / 1024
            log_info $"Total size: (($total_size | into int))MB"
        }
    }

    # Count bin archives
    if ("./bin_archives" | path exists) {
        let archives = (try { ls ./bin_archives/*.tar.gz } catch { [] })

        if ($archives | length) > 0 {
            log_success $"Bin archives: ($archives | length)"

            let total_size = ($archives | get size | math sum) / 1024 / 1024
            log_info $"Total size: (($total_size | into int))MB"
        }
    }

    # List platforms
    if not $bin_only and ("./distribution" | path exists) {
        let platforms = (try { ls ./distribution/*/nu } catch { [] } | each {|p| $p.name | path dirname | path basename})

        if ($platforms | length) > 0 {
            log_info $"\nPlatforms built: ($platforms | str join ', ')"

            # Next steps - use actual built platforms
            log_info "\n📝 Next Steps:"
            log_info "  1. Register plugins:"
            # Show example for first built platform
            let first_platform = $platforms | first 1 | get 0
            log_info $"     cd distribution/($first_platform) && nu register-plugins.nu"
        }
    } else {
        # If bin_only, no next steps for distribution
        log_info "\n📝 Next Steps:"
    }

    if not $bin_only {
        # Continue with other next steps
    }

    log_info "  2. Upload to release:"
    log_info "     gh release create v($version) distribution/packages/*"

    log_info "  3. Update documentation:"
    log_info "     Update README.md with new version"
}

# Detect current platform
def detect_platform []: nothing -> string {
    let host_info = sys host
    let os = $host_info.name
    let arch = (sys cpu | get 0.name)

    if $os == "Darwin" {
        if $arch == "aarch64" {
            "darwin-arm64"
        } else {
            "darwin-x86_64"
        }
    } else if $os == "Linux" {
        if $arch == "aarch64" {
            "linux-arm64"
        } else {
            "linux-x86_64"
        }
    } else if $os == "Windows" {
        "windows-x86_64"
    } else {
        $"($os | str downcase)-($arch)"
    }
}

# Ensure directory exists
def ensure_dir [dir: string] {
    if not ($dir | path exists) {
        mkdir $dir
    }
}

# Quick command to rebuild and redistribute
def "main rebuild" [] {
    log_info "🔄 Rebuild and redistribute workflow\n"

    # Build nushell
    log_info "Building nushell..."
    let build_result = (do {
        just build-nushell
    } | complete)

    if $build_result.exit_code != 0 {
        log_error "Nushell build failed"
        exit 1
    }

    # Build plugins
    log_info "Building plugins..."
    let plugins_result = (do {
        just build
    } | complete)

    if $plugins_result.exit_code != 0 {
        log_warn "Some plugins failed to build"
    }

    # Create distributions
    main --checksums
}

# Show distribution status
def "main status" [] {
    log_info "📊 Distribution Status\n"

    # Check if nushell is built
    if ("./nushell/target/release/nu" | path exists) {
        let version_result = (do {
            ./nushell/target/release/nu -c "version | get version"
        } | complete)

        log_success $"Nushell binary: (str trim $version_result.stdout)"
    } else {
        log_warn "Nushell binary: Not built"
    }

    # Check distribution directory
    if ("./distribution" | path exists) {
        let platforms = (try { ls ./distribution/*/nu } catch { [] })

        if ($platforms | length) > 0 {
            log_success $"Distribution platforms: ($platforms | length)"

            for p in $platforms {
                let platform = $p.name | path dirname | path basename
                print $"  • ($platform)"
            }
        } else {
            log_warn "Distribution: No binaries collected"
        }
    } else {
        log_warn "Distribution: Not created"
    }

    # Check packages
    if ("./distribution/packages" | path exists) {
        let packages = (try { ls ./distribution/packages/*.tar.gz ./distribution/packages/*.zip } catch { [] })

        if ($packages | length) > 0 {
            log_success $"Packages: ($packages | length) created"
        } else {
            log_warn "Packages: None created"
        }
    }

    # Check bin archives
    if ("./bin_archives" | path exists) {
        let archives = (try { ls ./bin_archives/*.tar.gz } catch { [] })

        if ($archives | length) > 0 {
            log_success $"Bin archives: ($archives | length) created"
        } else {
            log_warn "Bin archives: None created"
        }
    }
}
