package upgrade

import (
	"fmt"
	"time"

	"github.com/DeBrosOfficial/network/pkg/cli/remotessh"
	"github.com/DeBrosOfficial/network/pkg/inspector"
)

// RemoteUpgrader handles rolling upgrades across remote nodes.
type RemoteUpgrader struct {
	flags *Flags
}

// NewRemoteUpgrader creates a new remote upgrader.
func NewRemoteUpgrader(flags *Flags) *RemoteUpgrader {
	return &RemoteUpgrader{flags: flags}
}

// Execute runs the remote rolling upgrade.
func (r *RemoteUpgrader) Execute() error {
	nodes, err := remotessh.LoadEnvNodes(r.flags.Env)
	if err != nil {
		return err
	}

	cleanup, err := remotessh.PrepareNodeKeys(nodes)
	if err != nil {
		return err
	}
	defer cleanup()

	// Filter to single node if specified
	if r.flags.NodeFilter != "" {
		nodes = remotessh.FilterByIP(nodes, r.flags.NodeFilter)
		if len(nodes) == 0 {
			return fmt.Errorf("node %s not found in %s environment", r.flags.NodeFilter, r.flags.Env)
		}
	}

	fmt.Printf("Rolling upgrade: %s (%d nodes, %ds delay)\n\n", r.flags.Env, len(nodes), r.flags.Delay)

	// Print execution plan
	for i, node := range nodes {
		fmt.Printf("  %d. %s (%s)\n", i+1, node.Host, node.Role)
	}
	fmt.Println()

	for i, node := range nodes {
		fmt.Printf("[%d/%d] Upgrading %s (%s)...\n", i+1, len(nodes), node.Host, node.Role)

		if err := r.upgradeNode(node); err != nil {
			return fmt.Errorf("upgrade failed on %s: %w\nStopping rollout — remaining nodes not upgraded", node.Host, err)
		}

		fmt.Printf("  ✓ %s upgraded\n", node.Host)

		// Wait between nodes (except after the last one)
		if i < len(nodes)-1 && r.flags.Delay > 0 {
			fmt.Printf("  Waiting %ds before next node...\n\n", r.flags.Delay)
			time.Sleep(time.Duration(r.flags.Delay) * time.Second)
		}
	}

	fmt.Printf("\n✓ Rolling upgrade complete (%d nodes)\n", len(nodes))
	return nil
}

// upgradeNode runs `orama node upgrade --restart` on a single remote node,
// forwarding the per-node flags the operator passed locally (--nameserver,
// --force, --skip-checks) so the remote orchestrator sees the same intent.
// Without this forwarding, the remote command would always use the saved
// preference, silently dropping operator overrides on the floor.
func (r *RemoteUpgrader) upgradeNode(node inspector.Node) error {
	sudo := remotessh.SudoPrefix(node)
	cmd := fmt.Sprintf("%sorama node upgrade --restart", sudo)

	// Tri-state pointer flag: forward only when explicitly set locally.
	// nil = "honor saved preference on the remote" — don't pass anything.
	if r.flags.Nameserver != nil {
		if *r.flags.Nameserver {
			cmd += " --nameserver"
		} else {
			cmd += " --nameserver=false"
		}
	}

	// Plain booleans: forward when true. False is the default everywhere
	// so no need to send `=false` explicitly.
	if r.flags.Force {
		cmd += " --force"
	}
	if r.flags.SkipChecks {
		cmd += " --skip-checks"
	}

	return remotessh.RunSSHStreaming(node, cmd)
}
