#!/usr/bin/env nu
# Pack - Create distribution bundles from built binaries
#
# Usage:
#   nu scripts/provisioning/pack.nu --target x86_64-linux --output dist/
#   nu scripts/provisioning/pack.nu --target aarch64-macos --format tar.gz
#

use common/platform.nu *
use common/manifest.nu *
use common/validate.nu *

# Configuration constants
def get-config-file [] {
    "configs/provisioning.toml"
}

def get-project-dir [] {
    pwd
}

def show-help [] {
    let help_text = "
syntaxis Pack - Create distribution bundles

USAGE:
  nu scripts/provisioning/pack.nu [OPTIONS]

OPTIONS:
  --target <TARGET>     Target triple (e.g., x86_64-unknown-linux-gnu)
                        Default: current platform
  --format <FORMAT>     Package format: tar.gz, zip, deb, rpm
                        Default: tar.gz
  --output <DIR>        Output directory for bundles
                        Default: dist/
  --list-targets        Show available targets
  --verify              Verify bundle integrity after creation
  --dry-run             Show what would be packaged without creating
  --help                Show this help message

EXAMPLES:
  # Create bundle for current platform
  nu scripts/provisioning/pack.nu

  # Create bundle for Linux x86_64
  nu scripts/provisioning/pack.nu --target x86_64-unknown-linux-gnu

  # Create ZIP bundle for macOS ARM64
  nu scripts/provisioning/pack.nu --target aarch64-apple-darwin --format zip

  # Dry run to preview bundling
  nu scripts/provisioning/pack.nu --dry-run
"
    print $help_text
}

def build-binaries [target: string] {
    print ("🔨 Building binaries for " + $target + "...")

    try {
        ^cargo build --release --target $target --workspace --exclude syntaxis-dashboard | lines | each { |line| print ("  " + $line) }
        print "✅ Build complete"
    } catch {
        let msg = "Build failed for target " + $target
        error make {msg: $msg}
    }
}

def collect-artifacts [target: string] {
    print "📦 Collecting artifacts..."

    let config = (open --raw (get-config-file) | from toml)
    let project_dir_val = (get-project-dir)
    let build_dir = ($project_dir_val | path join "target" $target "release")

    let binaries = (
        $config.artifacts.binaries
        | each { |bin_info|
            let binary_name = if (((sys host).name) == "Windows") {
                $bin_info.name + ".exe"
            } else {
                $bin_info.name
            }

            let binary_path = ($build_dir | path join $binary_name)
            if (($binary_path | path exists)) {
                print ("  ✅ Binary: " + $binary_name)
                {
                    name: $bin_info.name
                    path: $binary_path
                    description: $bin_info.description
                }
            } else {
                print ("  ❌ Binary not found: " + $binary_name)
                null
            }
        }
        | where { |item| (not ($item == null)) }
    )

    let configs = (
        $config.artifacts.configs
        | each { |config_path|
            let full_path = ($project_dir_val | path join $config_path)
            if (($full_path | path exists)) {
                print ("  ✅ Config: " + $config_path)
                {
                    name: ($config_path | path basename)
                    path: $full_path
                }
            } else {
                null
            }
        }
        | where { |item| (not ($item == null)) }
    )

    let docs = (
        $config.artifacts.docs
        | each { |doc_path|
            let full_path = ($project_dir_val | path join $doc_path)
            if (($full_path | path exists)) {
                print ("  ✅ Doc: " + $doc_path)
                {
                    name: ($doc_path | path basename)
                    path: $full_path
                }
            } else {
                null
            }
        }
        | where { |item| (not ($item == null)) }
    )

    let scripts = (
        $config.artifacts.scripts
        | each { |script_path|
            let full_path = ($project_dir_val | path join $script_path)
            if (($full_path | path exists)) {
                print ("  ✅ Script: " + $script_path)
                {
                    name: ($script_path | path basename)
                    path: $full_path
                }
            } else {
                null
            }
        }
        | where { |item| (not ($item == null)) }
    )

    let wrappers = (
        if (($config.artifacts | get -o wrapper_scripts) != null) {
            $config.artifacts.wrapper_scripts
            | each { |wrapper_path|
                let full_path = ($project_dir_val | path join $wrapper_path)
                if (($full_path | path exists)) {
                    print ("  ✅ Wrapper: " + $wrapper_path)
                    {
                        name: ($wrapper_path | path basename)
                        path: $full_path
                    }
                } else {
                    null
                }
            }
            | where { |item| (not ($item == null)) }
        } else {
            []
        }
    )

    {
        binaries: $binaries
        configs: $configs
        docs: $docs
        scripts: $scripts
        wrappers: $wrappers
    }
}

def create-bundle [target: string, format: string, output_dir: path, artifacts: record] {
    let format_msg = "🎁 Creating " + $format + " bundle for " + $target + "..."
    print $format_msg

    let config = (open --raw (get-config-file) | from toml)
    let version = "0.1.0"
    let project_dir_val = (get-project-dir)
    let bundle_name = "syntaxis-v" + $version + "-" + $target

    # Create temporary staging directory
    let staging_dir = ($project_dir_val | path join ".provisioning-staging" $bundle_name)
    ^mkdir -p ($staging_dir | path dirname)
    ^mkdir -p $staging_dir

    # Create bundle subdirectories
    ^mkdir -p ($staging_dir | path join "bin")
    ^mkdir -p ($staging_dir | path join "configs")
    ^mkdir -p ($staging_dir | path join "docs")

    # Copy binaries
    print "  📋 Copying binaries..."
    $artifacts.binaries | each { |bin|
        ^cp $bin.path ($staging_dir | path join "bin" $bin.name)
        print ("    ✓ " + $bin.name)
    }

    # Copy configs
    print "  ⚙️  Copying configs..."
    $artifacts.configs | each { |cfg|
        let dest = if (($cfg.name | str starts-with "database-")) {
            ($staging_dir | path join "configs" $cfg.name)
        } else {
            let subdir = ($cfg.path | path dirname | path basename)
            ^mkdir -p ($staging_dir | path join "configs" $subdir)
            ($staging_dir | path join "configs" $subdir $cfg.name)
        }
        ^cp $cfg.path $dest
        print ("    ✓ " + $cfg.name)
    }

    # Copy docs (special handling for root-level docs)
    print "  📖 Copying documentation..."
    $artifacts.docs | each { |doc|
        let dest = if ($doc.name == "BUNDLE_README.md") {
            # BUNDLE_README.md becomes README.md at root
            ^cp $doc.path ($staging_dir | path join "README.md")
            print ("    ✓ README.md (from BUNDLE_README.md)")
        } else if (($doc.name == "QUICK_START.md") or ($doc.name == "INSTALL.md")) {
            # QUICK_START and INSTALL go to root for easy access
            ^cp $doc.path ($staging_dir | path join $doc.name)
            print ("    ✓ " + $doc.name + " (root level)")
        } else {
            # Other docs go in docs/ folder
            ^cp $doc.path ($staging_dir | path join "docs" $doc.name)
            print ("    ✓ " + $doc.name + " (docs/)")
        }
    }

    # Copy scripts to root of bundle
    print "  🔧 Copying installation scripts..."
    $artifacts.scripts | each { |script|
        let dest = ($staging_dir | path join $script.name)
        ^cp $script.path $dest
        ^chmod +x $dest
        print ("    ✓ " + $script.name)
    }

    # Copy wrapper scripts to scripts/ directory in bundle
    if (($artifacts.wrappers | length) > 0) {
        print "  🔄 Copying wrapper scripts..."
        ^mkdir -p ($staging_dir | path join "scripts")
        $artifacts.wrappers | each { |wrapper|
            let dest = ($staging_dir | path join "scripts" $wrapper.name)
            ^cp $wrapper.path $dest
            ^chmod +x $dest
            print ("    ✓ " + $wrapper.name)
        }
    }

    # Create manifest
    print "  📋 Creating manifest..."
    let binary_names = ($artifacts.binaries | each { |b| $b.name })
    let config_names = ($artifacts.configs | each { |c| $c.name })
    let doc_names = ($artifacts.docs | each { |d| $d.name })
    let script_names = ($artifacts.scripts | each { |s| $s.name })

    let manifest = (create $version $target $format --binaries $binary_names --configs $config_names --docs $doc_names --scripts $script_names)

    # Generate checksums for all files
    let staging_files = (
        glob ($staging_dir | path join "**" "*")
        | where { |f| (($f | path type) == "file") }
    )

    # Calculate SHA256 checksums for all files in the bundle
    print "  📋 Generating checksums..."
    let checksums_output = (
        ^find $staging_dir -type f -print0 | ^xargs -0 sha256sum
        | split row "\n"
        | where { |line| (($line | str length) > 0) }
    )

    # Build checksums as empty dict for now (will be populated from checksums file)
    let checksums = {}

    let manifest_with_checksums = (add-checksums $manifest $checksums)
    (save $manifest_with_checksums ($staging_dir | path join "manifest.toml"))

    # Create output directory
    ^mkdir -p $output_dir

    # Create the actual bundle archive
    let bundle_path = match $format {
        "tar.gz" => {
            let tar_path = ($output_dir | path join ($bundle_name + ".tar.gz"))
            let tar_msg = "  📦 Creating TAR.GZ: " + ($tar_path | into string)
            print $tar_msg
            ^tar -czf $tar_path -C ($staging_dir | path dirname) $bundle_name
            $tar_path
        }
        "zip" => {
            let zip_path = ($output_dir | path join ($bundle_name + ".zip"))
            let zip_msg = "  📦 Creating ZIP: " + ($zip_path | into string)
            print $zip_msg
            ^zip -r $zip_path ($staging_dir | path join "*") -C $staging_dir
            $zip_path
        }
        _ => {
            let msg = "Unsupported format: " + $format
error make {msg: $msg}
        }
    }

    # Save checksums file alongside bundle
    let checksums_file = ($output_dir | path join ($bundle_name + ".checksums.toml"))

    # Build TOML checksums file from sha256sum output
    let checksums_toml_content = (
        "[checksums]\n" +
        (
            $checksums_output
            | each {|line|
                # Parse sha256sum output: "hash  filename"
                let line_trimmed = ($line | str trim)
                if (($line_trimmed | str length) > 0) {
                    # Extract hash (first 64 characters) and file path (everything after two spaces)
                    let hash = ($line_trimmed | str substring 0..64)
                    let file_path = ($line_trimmed | str substring 66.. | str trim)
                    if (($file_path | str length) > 0) {
                        let rel_path = (try { $file_path | path relative-to $staging_dir } catch { $file_path })
                        "\"" + ($rel_path | into string) + "\" = \"" + ($hash | str trim) + "\"\n"
                    } else {
                        ""
                    }
                } else {
                    ""
                }
            }
            | str join ""
        )
    )

    $checksums_toml_content | ^tee $checksums_file

    # Cleanup staging directory
    ^rm -rf $staging_dir

    print ("✅ Bundle created: " + ($bundle_path | into string))
    print ("✅ Checksums saved: " + ($checksums_file | into string))

    {
        bundle: $bundle_path
        checksums: $checksums_file
        size_mb: (((ls $bundle_path | get size.0) | into int) / (1024 * 1024) | math round --precision 2)
    }
}

# Main entry point
def main [--target: string = "", --format: string = "tar.gz", --output: path = "dist/", --list-targets, --verify, --dry-run, --help] {
    if $help {
        show-help
        return
    }

    # Validate config
    let config_validation = (validate-config (get-config-file))
    if (not ($config_validation.valid)) {
        let errors = ($config_validation.errors | str join ", ")
        let msg = "Invalid provisioning config: " + $errors
error make {msg: $msg}
    }

    # List available targets
    if $list_targets {
        print "\nAvailable targets:"
        list-targets (get-config-file) | each { |t|
            let enabled_mark = if ($t.enabled == true) { "✓" } else { "✗" }
            print ("  " + $enabled_mark + " " + $t.target)
            print ("     Description: " + $t.description)
            print ("     Formats: " + $t.formats)
        }
        return
    }

    # Determine target
    let selected_target = if ($target == "") {
        current-target
    } else {
        $target
    }

    # Validate target
    if (not (is-target-enabled $selected_target (get-config-file))) {
        let msg = "Target not available or disabled: " + $selected_target
error make {msg: $msg}
    }

    # Show configuration
    print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    print "📦 syntaxis Pack Configuration"
    print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    let desc = (target-description $selected_target (get-config-file))
    print ("Target:     " + $selected_target + " - " + $desc)
    print ("Format:     " + $format)
    print ("Output:     " + ($output | into string))
    print ("Dry Run:    " + ($dry_run | into string))
    print ""

    if $dry_run {
        print "🔍 DRY RUN MODE - No bundles will be created"
        return
    }

    # Build binaries
    build-binaries $selected_target

    # Collect artifacts
    let artifacts = (collect-artifacts $selected_target)

    # Create bundle
    let result = (create-bundle $selected_target $format $output $artifacts)

    # Verify if requested
    if $verify {
        print ""
        print "🔍 Verifying bundle integrity..."
        let bundle_validation = (validate-bundle-structure $result.bundle)
        if (not ($bundle_validation.valid)) {
            let errors = ($bundle_validation.errors | str join ", ")
            let msg = "Bundle validation failed: " + $errors
error make {msg: $msg}
        }
        print "✅ Bundle verification passed"
    }

    # Summary
    print ""
    print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    print "📦 Bundle Summary"
    print "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    print ("Bundle:     " + ($result.bundle | path basename))
    print ("Size:       " + ($result.size_mb | into string) + " MB")
    print ("Checksums:  " + ($result.checksums | path basename))
    print ""
    print "Next steps:"
    print "  1. Share the bundle with your team"
    print "  2. On target machine, extract: tar -xzf <bundle>.tar.gz"
    print "  3. Install: nu <bundle>/install.sh"
    print ""
}

