#!/usr/bin/env nu

# Release rollback tool - rollback problematic releases
#
# Rollbacks:
# - Git tags and releases
# - Package manager entries
# - Container registry tags
# - Distribution artifacts
# - Notification corrections
# - Version reversions

use std log

def main [
    --release-version: string                  # Release version to rollback (required)
    --rollback-scope: string = "complete"     # Rollback scope: git, github, packages, containers, notifications, complete
    --reason: string = ""                      # Reason for rollback (for documentation)
    --replacement-version: string = ""         # Replacement version to recommend (optional)
    --notify-users = true                      # Send rollback notifications to users
    --cleanup-artifacts = true                 # Clean up distribution artifacts
    --dry-run = false                          # Show what would be rolled back without doing it
    --force                                   # Force rollback even with warnings
    --verbose                                   # Enable verbose logging
]
{

    if $release_version == "" {
        log error "Release version is required for rollback"
        exit 1
    }

    let repo_root = ($env.PWD | path dirname | path dirname | path dirname)

    let rollback_scopes = if $rollback_scope == "complete" {
        ["git", "github", "packages", "containers", "notifications"]
    } else {
        ($rollback_scope | split row "," | each { str trim })
    }

    let rollback_config = {
        release_version: $release_version
        scopes: $rollback_scopes
        reason: (if $reason == "" { "Release rollback requested" } else { $reason })
        replacement_version: $replacement_version
        notify_users: $notify_users
        cleanup_artifacts: $cleanup_artifacts
        dry_run: $dry_run
        force: $force
        verbose: $verbose
        repo_root: $repo_root
    }

    log info $"Starting release rollback with config: ($rollback_config)"

    # Validate rollback request
    let validation_result = validate_rollback_request $rollback_config

    if $validation_result.status != "valid" and not $rollback_config.force {
        log error $"Rollback validation failed: ($validation_result.reason)"
        if ($validation_result.warnings | length) > 0 {
            log warning "Warnings:"
            for warning in $validation_result.warnings {
                log warning $"  - ($warning)"
            }
        }
        exit 1
    }

    # Confirm rollback if not forced
    if not $rollback_config.force and not $rollback_config.dry_run {
        let confirmation = (input $"Are you sure you want to rollback release v($rollback_config.release_version)? [y/N] ")
        if not ($confirmation | str downcase | str starts-with "y") {
            log info "Rollback cancelled by user"
            exit 0
        }
    }

    # Execute rollback for each scope
    let rollback_results = $rollback_config.scopes | each {|scope|
        execute_rollback_scope $scope $rollback_config
    }

    # Send rollback notifications if requested
    let notification_result = if $rollback_config.notify_users and not $rollback_config.dry_run {
        send_rollback_notifications $rollback_config $rollback_results
    } else {
        { status: "skipped", reason: "notifications disabled or dry run" }
    }

    # Generate rollback report
    let rollback_report = generate_rollback_report $rollback_config $rollback_results $notification_result

    let summary = {
        rollback_version: $rollback_config.release_version
        total_scopes: ($rollback_config.scopes | length)
        successful_rollbacks: ($rollback_results | where status == "success" | length)
        failed_rollbacks: ($rollback_results | where status == "failed" | length)
        skipped_rollbacks: ($rollback_results | where status == "skipped" | length)
        notification_result: $notification_result
        rollback_config: $rollback_config
        results: $rollback_results
        report: $rollback_report
    }

    if $summary.failed_rollbacks > 0 {
        log error $"Release rollback completed with ($summary.failed_rollbacks) failures"
        exit 1
    } else {
        if $rollback_config.dry_run {
            log info $"Dry run completed - would rollback ($summary.total_scopes) scopes"
        } else {
            log info $"Release rollback completed successfully - ($summary.successful_rollbacks) scopes rolled back"
        }
    }

    return $summary
}

# Validate rollback request
def validate_rollback_request [rollback_config: record]
{
    let warnings = []
    let errors = []

    cd $rollback_config.repo_root

    # Check if release version exists
    let tag_name = $"v($rollback_config.release_version)"
    let tag_result = (do { ^git tag -l $tag_name } | complete)
    let tag_exists = if $tag_result.exit_code == 0 { $tag_result.stdout | str trim } else { "" }

    if $tag_exists == "" {
        $errors = ($errors | append $"Release tag ($tag_name) does not exist")
    }

    # Check if this is the latest release
    let latest_result = (do { ^git describe --tags --abbrev=0 } | complete)
    let latest_tag = if $latest_result.exit_code == 0 { $latest_result.stdout | str trim } else { "" }

    if $latest_tag != $tag_name {
        $warnings = ($warnings | append $"Rolling back ($tag_name) which is not the latest release (latest: ($latest_tag))")
    }

    # Check repository state
    let status_result = (git status --porcelain | complete)
    if $status_result.exit_code == 0 and ($status_result.stdout | str trim) != "" {
        $warnings = ($warnings | append "Repository has uncommitted changes")
    }

    # Check GitHub CLI availability for GitHub scope
    if "github" in $rollback_config.scopes {
        let gh_check = (do { ^gh --version } | complete)
        if $gh_check.exit_code != 0 {
            $warnings = ($warnings | append "GitHub CLI not available - GitHub rollback will be skipped")
        }
    }

    let status = if ($errors | length) > 0 {
        "invalid"
    } else if ($warnings | length) > 0 {
        "warning"
    } else {
        "valid"
    }

    {
        status: $status
        errors: $errors
        warnings: $warnings
        reason: (if ($errors | length) > 0 { ($errors | str join "; ") } else { "" })
    }
}

# Execute rollback for specific scope
def execute_rollback_scope [
    scope: string
    rollback_config: record
]
{
    log info $"Rolling back scope: ($scope)"

    let start_time = (date now)

    match $scope {
        "git" => { rollback_git_release $rollback_config }
        "github" => { rollback_github_release $rollback_config }
        "packages" => { rollback_package_releases $rollback_config }
        "containers" => { rollback_container_releases $rollback_config }
        "notifications" => { rollback_notifications $rollback_config }
        _ => {
            log warning $"Unknown rollback scope: ($scope)"
            {
                scope: $scope
                status: "failed"
                reason: "unknown scope"
                duration: ((date now) - $start_time)
            }
        }
    }
}

# Rollback Git release (tag and commits)
def rollback_git_release [rollback_config: record]
{
    log info "Rolling back Git release..."

    let start_time = (date now)
    cd $rollback_config.repo_root

    if $rollback_config.dry_run {
        return {
            scope: "git"
            status: "success"
            tag_name: $"v($rollback_config.release_version)"
            dry_run: true
            duration: ((date now) - $start_time)
        }
    }

    let tag_name = $"v($rollback_config.release_version)"
    let actions_taken = []

    # Delete local tag
    let delete_local = (do { git tag -d $tag_name } | complete)
    let actions_taken = if $delete_local.exit_code == 0 {
        ($actions_taken | append "deleted local tag")
    } else {
        $actions_taken
    }

    # Delete remote tag
    let delete_remote = (do { git push --delete origin $tag_name } | complete)
    let actions_taken = if $delete_remote.exit_code == 0 {
        ($actions_taken | append "deleted remote tag")
    } else {
        log warning $"Failed to delete remote tag: ($delete_remote.stderr)"
        $actions_taken
    }

    # Note: We don't automatically reset commits as that could be destructive
    # Users should manually handle commit rollbacks if needed

    {
        scope: "git"
        status: "success"
        tag_name: $tag_name
        actions_taken: $actions_taken
        duration: ((date now) - $start_time)
    }
}

# Rollback GitHub release
def rollback_github_release [rollback_config: record]
{
    log info "Rolling back GitHub release..."

    let start_time = (date now)

    # Check GitHub CLI availability
    let gh_check = (do { ^gh --version } | complete)
    if $gh_check.exit_code != 0 {
        return {
            scope: "github"
            status: "skipped"
            reason: "GitHub CLI not available"
            duration: ((date now) - $start_time)
        }
    }

    if $rollback_config.dry_run {
        return {
            scope: "github"
            status: "success"
            release_tag: $"v($rollback_config.release_version)"
            dry_run: true
            duration: ((date now) - $start_time)
        }
    }

    let tag_name = $"v($rollback_config.release_version)"

    cd $rollback_config.repo_root

    # Check if GitHub release exists
    let release_check = (do { gh release view $tag_name } | complete)
    if $release_check.exit_code != 0 {
        return {
            scope: "github"
            status: "skipped"
            reason: $"GitHub release ($tag_name) does not exist"
            duration: ((date now) - $start_time)
        }
    }

    # Delete GitHub release
    let delete_result = (do { gh release delete $tag_name --yes } | complete)

    if $delete_result.exit_code == 0 {
        log info $"Successfully deleted GitHub release: ($tag_name)"

        {
            scope: "github"
            status: "success"
            release_tag: $tag_name
            duration: ((date now) - $start_time)
        }
    } else {
        {
            scope: "github"
            status: "failed"
            reason: $delete_result.stderr
            release_tag: $tag_name
            duration: ((date now) - $start_time)
        }
    }
}

# Rollback package releases (Homebrew, APT, etc.)
def rollback_package_releases [rollback_config: record]
{
    log info "Rolling back package releases..."

    let start_time = (date now)

    if $rollback_config.dry_run {
        return {
            scope: "packages"
            status: "success"
            packages_affected: ["homebrew", "apt", "yum"]
            dry_run: true
            duration: ((date now) - $start_time)
        }
    }

    # Package rollback would involve:
    # 1. Reverting Homebrew formula to previous version
    # 2. Removing packages from APT/YUM repositories
    # 3. Updating package indexes

    log warning "Package rollback not fully implemented - would revert package manager entries"

    {
        scope: "packages"
        status: "skipped"
        reason: "not fully implemented"
        packages_affected: []
        duration: ((date now) - $start_time)
    }
}

# Rollback container releases
def rollback_container_releases [rollback_config: record]
{
    log info "Rolling back container releases..."

    let start_time = (date now)

    if $rollback_config.dry_run {
        return {
            scope: "containers"
            status: "success"
            images_affected: [$"provisioning:($rollback_config.release_version)"]
            dry_run: true
            duration: ((date now) - $start_time)
        }
    }

    # Container rollback would involve:
    # 1. Removing specific version tags
    # 2. Updating 'latest' tag to previous version
    # 3. Cleaning up registry entries

    log warning "Container rollback not fully implemented - would remove container tags"

    {
        scope: "containers"
        status: "skipped"
        reason: "not fully implemented"
        images_affected: []
        duration: ((date now) - $start_time)
    }
}

# Rollback notifications (send corrections)
def rollback_notifications [rollback_config: record]
{
    log info "Rolling back notifications..."

    let start_time = (date now)

    if $rollback_config.dry_run {
        return {
            scope: "notifications"
            status: "success"
            correction_message: $"Release v($rollback_config.release_version) has been rolled back"
            dry_run: true
            duration: ((date now) - $start_time)
        }
    }

    # Notification rollback would involve:
    # 1. Sending correction messages to all notification channels
    # 2. Updating RSS feeds with rollback notice
    # 3. Posting rollback announcements

    log warning "Notification rollback not fully implemented - would send correction messages"

    {
        scope: "notifications"
        status: "skipped"
        reason: "not fully implemented"
        duration: ((date now) - $start_time)
    }
}

# Send rollback notifications to users
def send_rollback_notifications [
    rollback_config: record
    rollback_results: list
]
{
    log info "Sending rollback notifications..."

    let rollback_message = generate_rollback_message $rollback_config $rollback_results

    # This would use the notification system to send rollback alerts
    log warning "Rollback notifications not fully implemented - would send user alerts"

    {
        status: "skipped"
        reason: "not fully implemented"
        message: $rollback_message
    }
}

# Generate rollback message for notifications
def generate_rollback_message [
    rollback_config: record
    rollback_results: list
]
{
    let successful_scopes = ($rollback_results | where status == "success" | get scope)
    let replacement_text = if $rollback_config.replacement_version != "" {
        $" Please use version ($rollback_config.replacement_version) instead."
    } else {
        ""
    }

    $"🚨 Release Rollback Notice

Release v($rollback_config.release_version) has been rolled back due to: ($rollback_config.reason)

Affected areas: ($successful_scopes | str join ', ')

($replacement_text)

We apologize for any inconvenience. Please ensure you're using a stable version.

The Provisioning Team"
}

# Generate rollback report
def generate_rollback_report [
    rollback_config: record
    rollback_results: list
    notification_result: record
]
{
    let report = {
        timestamp: (date now)
        rollback_version: $rollback_config.release_version
        reason: $rollback_config.reason
        replacement_version: $rollback_config.replacement_version
        summary: {
            total_scopes: ($rollback_results | length)
            successful: ($rollback_results | where status == "success" | length)
            failed: ($rollback_results | where status == "failed" | length)
            skipped: ($rollback_results | where status == "skipped" | length)
        }
        scopes: $rollback_results
        notifications: $notification_result
        configuration: $rollback_config
    }

    # Save report to file
    let report_file = ($rollback_config.repo_root | path join $"rollback-report-($rollback_config.release_version).json")
    $report | to json | save $report_file
    log info $"Rollback report saved to: ($report_file)"

    return $report
}

# Show rollback status and options
def "main status" [release_version: string = ""] {
    let repo_root = ($env.PWD | path dirname | path dirname | path dirname)
    cd $repo_root

    if $release_version == "" {
        # Show general rollback status
        let latest_result = (do { ^git describe --tags --abbrev=0 } | complete)
        let latest_tag = if $latest_result.exit_code == 0 { $latest_result.stdout | str trim } else { "none" }

        let recent_result = (do { ^git tag -l --sort=-version:refname | head -5 } | complete)
        let recent_tags = if $recent_result.exit_code == 0 { $recent_result.stdout | lines } else { [] }

        let gh_avail = (do { ^gh --version } | complete)

        return {
            repository: $repo_root
            latest_release: $latest_tag
            recent_releases: $recent_tags
            rollback_scopes: ["git", "github", "packages", "containers", "notifications"]
            tools_available: {
                git: true
                github_cli: ($gh_avail.exit_code == 0)
            }
        }
    } else {
        # Show status for specific release
        let tag_name = $"v($release_version)"
        let tag_result = (do { ^git tag -l $tag_name } | complete)
        let tag_exists = if $tag_result.exit_code == 0 { $tag_result.stdout | str trim } else { "" }

        let gh_check = (do { ^gh --version } | complete)
        let github_release_exists = if $gh_check.exit_code == 0 {
            let release_check = (do { ^gh release view $tag_name } | complete)
            $release_check.exit_code == 0
        } else {
            false
        }

        return {
            release_version: $release_version
            tag_name: $tag_name
            git_tag_exists: ($tag_exists != "")
            github_release_exists: $github_release_exists
            rollback_possible: ($tag_exists != "" or $github_release_exists)
        }
    }
}

# List recent releases that can be rolled back
def "main list" [] {
    let repo_root = ($env.PWD | path dirname | path dirname | path dirname)
    cd $repo_root

    let tags_result = (do { ^git tag -l --sort=-version:refname | head -10 } | complete)
    let tags = if $tags_result.exit_code == 0 { $tags_result.stdout | lines } else { [] }

    let gh_avail_check = (do { ^gh --version } | complete)
    let gh_available = ($gh_avail_check.exit_code == 0)

    $tags | each {|tag|
        let release_exists = if $gh_available {
            let release_check = (do { ^gh release view $tag } | complete)
            $release_check.exit_code == 0
        } else {
            false
        }

        let tag_date_result = (do { ^git log -1 --format=%cd --date=short $tag } | complete)
        let tag_date = if $tag_date_result.exit_code == 0 { $tag_date_result.stdout | str trim } else { "unknown" }

        {
            tag: $tag
            version: ($tag | str replace "^v" "")
            date: $tag_date
            github_release: $release_exists
            can_rollback: true
        }
    }
}

# Emergency rollback (interactive)
def "main emergency" [release_version: string] {
    log warning $"🚨 EMERGENCY ROLLBACK for v($release_version)"

    print "This will immediately rollback the release with minimal checks."
    print "Use this only in critical situations (security issues, broken releases, etc.)"
    print ""

    let confirmation = (input $"Type 'EMERGENCY ROLLBACK' to confirm: ")

    if $confirmation != "EMERGENCY ROLLBACK" {
        log info "Emergency rollback cancelled"
        exit 0
    }

    # Execute emergency rollback with force
    main $release_version --rollback-scope complete --force --notify-users --reason "Emergency rollback" --verbose
}
