package bootstrap

import (
	"os"
	"strings"
	"testing"
)

// RefreshConfig re-pulls the hub yaml and OVERWRITES controller.yaml, re-merging local_api from
// bootstrap.json. Unlike MaybeIngest it is not first-boot-gated — it clobbers an existing config (the
// hub is the source of truth for controller.yaml).
func TestRefreshConfig_RePullsAndMerges(t *testing.T) {
	dir := t.TempDir()
	_, cfgPath := writeBootstrap(t, dir, goodBootstrapV2)

	// Pre-existing (stale) controller.yaml that must be overwritten.
	if err := os.WriteFile(cfgPath, []byte("customer:\n  id: cust-8200\n  name: OLD\n"), 0o600); err != nil {
		t.Fatal(err)
	}

	var calls int
	var gotID, gotPass string
	pull := func(hubURL, customerID, pass string) (string, error) {
		calls++
		gotID, gotPass = customerID, pass
		return hubYAML, nil
	}

	if err := RefreshConfig(cfgPath, testLogger(), pull); err != nil {
		t.Fatalf("RefreshConfig: %v", err)
	}
	if calls != 1 {
		t.Errorf("pull called %d times, want 1", calls)
	}
	if gotID != "cust-8200" || gotPass != "five-word-passphrase-here" {
		t.Errorf("pull args = (%q,%q), want (cust-8200, five-word-passphrase-here)", gotID, gotPass)
	}

	out, err := os.ReadFile(cfgPath)
	if err != nil {
		t.Fatal(err)
	}
	s := string(out)
	if strings.Contains(s, "OLD") {
		t.Errorf("controller.yaml still contains stale content:\n%s", s)
	}
	if !strings.Contains(s, "CUSTKEY_FROM_HUB") {
		t.Errorf("controller.yaml missing hub-pulled api_key:\n%s", s)
	}
	// local_api re-merged from bootstrap.json (the hub yaml lacks it).
	if !strings.Contains(s, "PERGUESTTOKEN") || !strings.Contains(s, "192.168.0.162:8443") {
		t.Errorf("controller.yaml missing re-merged local_api:\n%s", s)
	}
}

// Fail-safe: a failed pull leaves the existing controller.yaml UNCHANGED and returns an error (the
// caller then keeps the current config and does not restart).
func TestRefreshConfig_FailedPullLeavesConfigUnchanged(t *testing.T) {
	dir := t.TempDir()
	_, cfgPath := writeBootstrap(t, dir, goodBootstrapV2)
	original := "customer:\n  id: cust-8200\n  name: CURRENT\n"
	if err := os.WriteFile(cfgPath, []byte(original), 0o600); err != nil {
		t.Fatal(err)
	}

	pull := func(hubURL, customerID, pass string) (string, error) {
		return "", ErrPullTransient // hub unreachable
	}
	if err := RefreshConfig(cfgPath, testLogger(), pull); err == nil {
		t.Fatal("RefreshConfig should return an error on a failed pull")
	}
	out, _ := os.ReadFile(cfgPath)
	if string(out) != original {
		t.Errorf("controller.yaml changed despite failed pull:\n%s", string(out))
	}
}

// Fail-safe: no bootstrap.json (e.g. a wizard-configured guest) → error, nothing written.
func TestRefreshConfig_NoBootstrapErrors(t *testing.T) {
	dir := t.TempDir()
	cfgPath := dir + "/controller.yaml"
	t.Setenv("FELHOM_BOOTSTRAP_PATH", dir+"/does-not-exist.json")

	called := false
	pull := func(hubURL, customerID, pass string) (string, error) { called = true; return hubYAML, nil }
	if err := RefreshConfig(cfgPath, testLogger(), pull); err == nil {
		t.Fatal("RefreshConfig should error when bootstrap.json is absent")
	}
	if called {
		t.Error("pull should not be called when bootstrap.json is absent")
	}
	if _, err := os.Stat(cfgPath); !os.IsNotExist(err) {
		t.Error("controller.yaml should not have been written")
	}
}
