package bootstrap

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"os"
	"path/filepath"
	"testing"
)

func TestResolveGeneratesAndPersistsOnFirstRun(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "hecate.bootstrap.json")

	b, err := Resolve(path, "")
	if err != nil {
		t.Fatalf("Resolve: %v", err)
	}
	// ControlPlaneSecretKey is 32 random bytes base64-encoded → 44 chars
	// (with std-base64 padding) so secrets.NewAESGCMCipher can decode it
	// to exactly 32 bytes.
	if len(b.ControlPlaneSecretKey) != 44 {
		t.Errorf("ControlPlaneSecretKey length = %d, want 44 (base64 of 32 bytes)", len(b.ControlPlaneSecretKey))
	}

	raw, err := os.ReadFile(path)
	if err != nil {
		t.Fatalf("read: %v", err)
	}
	var disk Bootstrap
	if err := json.Unmarshal(raw, &disk); err != nil {
		t.Fatalf("decode: %v", err)
	}
	if disk != b {
		t.Errorf("disk content does not match returned: disk=%+v in-mem=%+v", disk, b)
	}
}

func TestResolveReusesExistingFile(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "hecate.bootstrap.json")

	first, err := Resolve(path, "")
	if err != nil {
		t.Fatalf("first Resolve: %v", err)
	}
	second, err := Resolve(path, "")
	if err != nil {
		t.Fatalf("second Resolve: %v", err)
	}
	if second != first {
		t.Errorf("values changed across runs: first=%+v second=%+v", first, second)
	}
}

func TestResolveEnvOverridesFile(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "hecate.bootstrap.json")

	original, err := Resolve(path, "")
	if err != nil {
		t.Fatalf("seed Resolve: %v", err)
	}

	overrideSecret := testBootstrapKey(0xab)
	b, err := Resolve(path, overrideSecret)
	if err != nil {
		t.Fatalf("Resolve with env override: %v", err)
	}
	if b.ControlPlaneSecretKey != overrideSecret {
		t.Errorf("env secret didn't override; got %q", b.ControlPlaneSecretKey)
	}
	if b == original {
		t.Error("override should have changed values from initial seed")
	}

	raw, err := os.ReadFile(path)
	if err != nil {
		t.Fatalf("read: %v", err)
	}
	var disk Bootstrap
	if err := json.Unmarshal(raw, &disk); err != nil {
		t.Fatalf("decode: %v", err)
	}
	if disk != b {
		t.Errorf("file not updated to env values: %+v", disk)
	}
}

func TestResolveCreatesParentDirectory(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "nested", "deeper", "hecate.bootstrap.json")

	if _, err := Resolve(path, ""); err != nil {
		t.Fatalf("Resolve with missing parent dir: %v", err)
	}
	if _, err := os.Stat(path); err != nil {
		t.Errorf("expected file at %s, got %v", path, err)
	}
}

func TestResolveRejectsCorruptFile(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "hecate.bootstrap.json")
	if err := os.WriteFile(path, []byte("{not json"), 0o600); err != nil {
		t.Fatalf("write fixture: %v", err)
	}
	if _, err := Resolve(path, ""); err == nil {
		t.Error("expected error on corrupt JSON, got nil")
	}
}

func TestResolveRejectsInvalidEnvSecret(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "hecate.bootstrap.json")

	if _, err := Resolve(path, "not-base64"); err == nil {
		t.Fatal("Resolve() error = nil, want invalid env secret error")
	}
}

func TestResolveRejectsWrongLengthEnvSecret(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "hecate.bootstrap.json")

	shortKey := base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{0xab}, 31))
	if _, err := Resolve(path, shortKey); err == nil {
		t.Fatal("Resolve() error = nil, want wrong-length env secret error")
	}
}

func TestResolveRejectsInvalidPersistedSecret(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "hecate.bootstrap.json")
	b := Bootstrap{
		ControlPlaneSecretKey: "not-base64",
	}
	raw, err := json.Marshal(b)
	if err != nil {
		t.Fatalf("marshal fixture: %v", err)
	}
	if err := os.WriteFile(path, raw, 0o600); err != nil {
		t.Fatalf("write fixture: %v", err)
	}

	if _, err := Resolve(path, ""); err == nil {
		t.Fatal("Resolve() error = nil, want invalid persisted secret error")
	}
}

func TestResolveRejectsWrongLengthPersistedSecret(t *testing.T) {
	dir := t.TempDir()
	path := filepath.Join(dir, "hecate.bootstrap.json")
	b := Bootstrap{
		ControlPlaneSecretKey: base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{0xab}, 31)),
	}
	raw, err := json.Marshal(b)
	if err != nil {
		t.Fatalf("marshal fixture: %v", err)
	}
	if err := os.WriteFile(path, raw, 0o600); err != nil {
		t.Fatalf("write fixture: %v", err)
	}

	if _, err := Resolve(path, ""); err == nil {
		t.Fatal("Resolve() error = nil, want wrong-length persisted secret error")
	}
}

func testBootstrapKey(fill byte) string {
	return base64.StdEncoding.EncodeToString(bytes.Repeat([]byte{fill}, 32))
}

func writeBootstrapFixture(t *testing.T, path, key string, mode os.FileMode) {
	t.Helper()
	b := Bootstrap{
		ControlPlaneSecretKey: key,
	}
	raw, err := json.Marshal(b)
	if err != nil {
		t.Fatalf("marshal fixture: %v", err)
	}
	if err := os.WriteFile(path, raw, mode); err != nil {
		t.Fatalf("write fixture: %v", err)
	}
}
