#!/usr/bin/env nu

# Nushell Auto-Updater - Nushell version
# Downloads latest nushell release from GitHub, extracts, and manages symlinks
# Usage: nu update_nushell.nu [command]

# Configuration
let GITHUB_API_URL = "https://api.github.com/repos/nushell/nushell/releases/latest"
let GITHUB_RELEASES_URL = "https://github.com/nushell/nushell/releases/download"
let NUSHELL_SYMLINK = "nushell"

# Function to show usage
def show_usage [] {
    print $"(ansi blue)Nushell Auto-Updater(ansi reset)"
    print ""
    print "Usage: nu update_nushell.nu [command]"
    print ""
    print "Commands:"
    print $"  (ansi green)check(ansi reset)     Check for updates without installing"
    print $"  (ansi green)update(ansi reset)    Download and install latest version"
    print $"  (ansi green)status(ansi reset)    Show current installation status"
    print $"  (ansi green)help, -h(ansi reset) Show this help message"
    print ""
    print "If no command is specified, 'update' is assumed."
    print ""
}

# Function to get latest version from GitHub API
def get_latest_version [] {
    try {
        http get $GITHUB_API_URL | get tag_name
    } catch {
        null
    }
}

# Function to check if nushell directory is a git repository
def is_git_repository [nushell_dir: string] {
    ($nushell_dir | path join ".git" "config" | path exists)
}

# Function to get git repository status with origin information
def get_git_status [nushell_dir: string] {
    if not (is_git_repository $nushell_dir) {
        return null
    }

    try {
        cd $nushell_dir
        let current_commit = (^git rev-parse --short HEAD | str trim)
        let current_branch = (^git branch --show-current | str trim)
        let remote_status = (^git status --porcelain=v1 | lines | length)
        let origin_url = (^git remote get-url origin | str trim)
        cd ..

        {
            branch: $current_branch,
            commit: $current_commit,
            changes: $remote_status,
            origin: $origin_url
        }
    } catch {
        null
    }
}

# Function to get currently installed version
def get_current_version [] {
    try {
        if ($NUSHELL_SYMLINK | path exists) and (($NUSHELL_SYMLINK | path type) == "symlink") {
            let target = $NUSHELL_SYMLINK | path expand | path basename
            # Check if target is a git repository
            if (is_git_repository $target) {
                "git-repo"
            } else {
                let version_match = ($target | parse "nushell-{version}")
                if ($version_match | length) > 0 {
                    $version_match.0.version
                } else {
                    null
                }
            }
        } else if ($NUSHELL_SYMLINK | path exists) and (($NUSHELL_SYMLINK | path type) == "dir") and (is_git_repository $NUSHELL_SYMLINK) {
            # Handle case where nushell is a directory (git repository)
            "git-repo"
        } else {
            null
        }
    } catch {
        null
    }
}

# Function to get current directory name
def get_current_directory [] {
    try {
        if ($NUSHELL_SYMLINK | path exists) and (($NUSHELL_SYMLINK | path type) == "symlink") {
            $NUSHELL_SYMLINK | path expand | path basename
        } else if ($NUSHELL_SYMLINK | path exists) and (($NUSHELL_SYMLINK | path type) == "dir") {
            # Return the directory name itself if it's a directory
            $NUSHELL_SYMLINK
        } else {
            null
        }
    } catch {
        null
    }
}

# Function to check installation status
def check_status [] {
    print $"(ansi blue)Nushell Installation Status(ansi reset)"
    print ""

    let current_version = get_current_version
    let current_dir = get_current_directory

    # Handle git repository case
    if $current_version == "git-repo" {
        print $"(ansi cyan)Git Repository Detected(ansi reset)"
        print $"Currently installed: (ansi cyan)Git repository(ansi reset)"

        let git_status = get_git_status $current_dir
        if ($git_status | is-not-empty) {
            print $"Repository: (ansi cyan)($current_dir)(ansi reset)"
            print $"Origin: (ansi cyan)($git_status.origin)(ansi reset)"
            print $"Branch: (ansi cyan)($git_status.branch)(ansi reset)"
            print $"Commit: (ansi cyan)($git_status.commit)(ansi reset)"
            if $git_status.changes > 0 {
                print $"Status: (ansi yellow)($git_status.changes) uncommitted changes(ansi reset)"
            } else {
                print $"Status: (ansi green)Clean working directory(ansi reset)"
            }
        }
        print ""
        print $"(ansi yellow)⚠️  Git Repository Management(ansi reset)"
        print "This nushell installation is a git repository."
        print $"Use (ansi cyan)git pull(ansi reset) to update instead of this script."
        print $"To switch to release versions, remove the current directory and use (ansi cyan)nu update_nushell.nu update(ansi reset)"
        return 3
    }

    let latest_version = get_latest_version

    if ($latest_version | is-empty) {
        print $"(ansi red)Error: Could not fetch latest version from GitHub(ansi reset)"
        print "Please check your internet connection and try again."
        return 1
    }

    print $"Latest available version: (ansi green)($latest_version)(ansi reset)"

    if ($current_version | is-empty) {
        print $"Currently installed: (ansi yellow)None(ansi reset)"
        print $"Status: (ansi yellow)Fresh installation needed(ansi reset)"
        return 2
    } else {
        print $"Currently installed: (ansi cyan)($current_version)(ansi reset)"

        if $current_version == $latest_version {
            print $"Status: (ansi green)Up to date(ansi reset)"
            return 0
        } else {
            print $"Status: (ansi yellow)Update available(ansi reset)"
            return 2
        }
    }
}

# Function to download file
def download_file [url: string, filename: string] {
    try {
        print $"(ansi blue)Downloading from: ($url)(ansi reset)"
        http get $url | save $filename
        true
    } catch { |e|
        print $"(ansi red)Error downloading file: ($e.msg)(ansi reset)"
        false
    }
}

# Function to download and extract nushell
def download_and_extract [version: string] {
    let filename = $"nushell-($version)-source.tar.gz"
    let url = $"($GITHUB_RELEASES_URL)/($version)/($filename)"
    let target_dir = $"nushell-($version)"

    print $"(ansi blue)Downloading nushell ($version)...(ansi reset)"

    # Download the file
    if not (download_file $url $filename) {
        return false
    }

    if not ($filename | path exists) {
        print $"(ansi red)Error: Download failed - file not found(ansi reset)"
        return false
    }

    print $"(ansi blue)Extracting ($filename)...(ansi reset)"

    # Extract the tar.gz file
    try {
        ^tar -xzf $filename
    } catch { |e|
        print $"(ansi red)Error: Failed to extract ($filename): ($e.msg)(ansi reset)"
        try { rm $filename } catch { }
        return false
    }

    # Check if extraction created the expected directory
    if not ($target_dir | path exists) {
        print $"(ansi red)Error: Expected directory ($target_dir) not found after extraction(ansi reset)"
        try { rm $filename } catch { }
        return false
    }

    print $"(ansi green)Successfully downloaded and extracted nushell ($version)(ansi reset)"
    true
}

# Function to clean up old installations
def cleanup_old_installation [old_dir: string, old_version: string] {
    if not ($old_dir | is-empty) and ($old_dir | path exists) {
        print $"(ansi blue)Removing old installation: ($old_dir)(ansi reset)"
        try {
            rm -rf $old_dir
        } catch { |e|
            print $"(ansi yellow)Warning: Could not remove old directory ($old_dir): ($e.msg)(ansi reset)"
        }
    }

    # Remove old tar.gz files
    let old_tarball = $"nushell-($old_version)-source.tar.gz"
    if ($old_tarball | path exists) {
        print $"(ansi blue)Removing old tarball: ($old_tarball)(ansi reset)"
        try {
            rm $old_tarball
        } catch { |e|
            print $"(ansi yellow)Warning: Could not remove old tarball ($old_tarball): ($e.msg)(ansi reset)"
        }
    }
}

# Function to create symlink
def create_symlink [target_dir: string] {
    # Remove existing symlink
    if ($NUSHELL_SYMLINK | path exists) {
        if (($NUSHELL_SYMLINK | path type) == "symlink") {
            try { rm $NUSHELL_SYMLINK } catch { }
        } else {
            print $"(ansi red)Error: ($NUSHELL_SYMLINK) exists but is not a symlink(ansi reset)"
            return false
        }
    }

    # Create new symlink
    try {
        ^ln -s $target_dir $NUSHELL_SYMLINK
        print $"(ansi green)Created symlink: ($NUSHELL_SYMLINK) -> ($target_dir)(ansi reset)"
        true
    } catch { |e|
        print $"(ansi red)Error: Failed to create symlink: ($e.msg)(ansi reset)"
        false
    }
}

# Function to update git repository
def update_git_repository [] {
    let current_dir = get_current_directory

    if ($current_dir | is-empty) or not (is_git_repository $current_dir) {
        print $"(ansi red)Error: Git repository not found or invalid(ansi reset)"
        return 1
    }

    print $"(ansi blue)Updating Git Repository(ansi reset)"
    print $"Repository: (ansi cyan)($current_dir)(ansi reset)"

    # Show origin and branch info
    let git_status = get_git_status $current_dir
    if ($git_status | is-not-empty) {
        print $"Origin: (ansi cyan)($git_status.origin)(ansi reset)"
        print $"Branch: (ansi cyan)($git_status.branch)(ansi reset)"
    }
    print ""

    try {
        cd $current_dir

        # Check if there are uncommitted changes
        let changes = (^git status --porcelain=v1 | lines | length)
        if $changes > 0 {
            print $"(ansi yellow)Warning: ($changes) uncommitted changes detected(ansi reset)"
            print "Stashing changes before update..."
            let stash_message = $"Auto-stash before nushell update (date now | format date '%Y-%m-%d %H:%M:%S')"
            ^git stash push -m $stash_message
        }

        # Update repository
        print $"(ansi blue)Fetching latest changes...(ansi reset)"
        ^git fetch origin

        print $"(ansi blue)Pulling latest changes...(ansi reset)"
        ^git pull origin

        # Restore stashed changes if any
        if $changes > 0 {
            print $"(ansi blue)Restoring stashed changes...(ansi reset)"
            try {
                ^git stash pop
            } catch {
                print $"(ansi yellow)Warning: Failed to restore stashed changes(ansi reset)"
                print "Your changes are saved in the stash. Use 'git stash list' to see them."
            }
        }

        cd ..

        print ""
        print $"(ansi green)✓ Git repository updated successfully!(ansi reset)"
        let new_commit = (^git -C $current_dir rev-parse --short HEAD | str trim)
        print $"Current commit: (ansi cyan)($new_commit)(ansi reset)"

        0
    } catch { |e|
        print $"(ansi red)Error updating git repository: ($e.msg)(ansi reset)"
        try { cd .. } catch { }
        1
    }
}

# Function to perform update
def perform_update [force_update: bool = false] {
    print $"(ansi blue)Nushell Auto-Updater(ansi reset)"
    print ""

    # Check status first
    let status_result = check_status

    match $status_result {
        0 => {
            if not $force_update {
                print ""
                print $"(ansi green)Nushell is already up to date!(ansi reset)"
                return 0
            }
            print ""
            print $"(ansi yellow)Forcing update even though current version is latest...(ansi reset)"
        }
        1 => { return 1 }
        2 => {
            print ""
            print $"(ansi yellow)Proceeding with installation/update...(ansi reset)"
        }
        3 => {
            # Git repository detected
            print ""
            print $"(ansi blue)Git repository detected. Running git update...(ansi reset)"
            print ""
            return (update_git_repository)
        }
    }

    print ""

    # Get versions
    let latest_version = get_latest_version
    let current_version = get_current_version
    let current_dir = get_current_directory

    # Download and extract
    if not (download_and_extract $latest_version) {
        return 1
    }

    let new_dir = $"nushell-($latest_version)"
    let new_tarball = $"nushell-($latest_version)-source.tar.gz"

    # Create symlink
    if not (create_symlink $new_dir) {
        print $"(ansi red)Installation failed at symlink creation(ansi reset)"
        return 1
    }

    # Clean up old installation (only if different from new one)
    if (not ($current_version | is-empty)) and ($current_version != $latest_version) {
        cleanup_old_installation $current_dir $current_version
    }

    # Remove the new tarball (keep the extracted directory)
    if ($new_tarball | path exists) {
        try { rm $new_tarball } catch { }
    }

    print ""
    print $"(ansi green)✓ Nushell installation completed successfully!(ansi reset)"
    print $"Version: (ansi cyan)($latest_version)(ansi reset)"
    print $"Location: (ansi cyan)(pwd)/($new_dir)(ansi reset)"
    print $"Symlink: (ansi cyan)(pwd)/($NUSHELL_SYMLINK)(ansi reset)"

    0
}

# Main function
def main [command?: string] {
    let cmd = ($command | default "update")

    match $cmd {
        "check" => { check_status }
        "update" => { perform_update }
        "force" => { perform_update true }
        "status" => { check_status }
        "help" | "-h" | "--help" => { show_usage }
        _ => {
            print $"(ansi red)Error: Unknown command '($cmd)'(ansi reset)"
            print ""
            show_usage
        }
    }
}

# Export functions for use
export def "nushell-updater check" [] { check_status }
export def "nushell-updater update" [] { perform_update }
export def "nushell-updater force" [] { perform_update true }
export def "nushell-updater status" [] { check_status }
export def "nushell-updater help" [] { show_usage }

# Main entry point function
export def "nushell-updater" [command?: string] {
    main ($command | default "help")
}

# When run as script, don't execute main automatically
# Use the exported functions instead