package secrets

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
)

func TestFetchNodeReturnsData(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.URL.Path {
		case "/v1/kv/data/atlas/nodes/n1":
			w.Header().Set("Content-Type", "application/json")
			_ = json.NewEncoder(w).Encode(map[string]any{
				"data": map[string]any{
					"data": map[string]any{
						"atlas_password": "atlas-pw",
						"root_password":  "root-pw",
						"k3s_token":      "t1",
						"cloud_init":     "ci",
					},
				},
			})
		default:
			http.NotFound(w, r)
		}
	}))
	defer srv.Close()

	c := &Client{Addr: srv.URL, Token: "tok"}
	sec, err := c.FetchNode(context.Background(), "n1")
	if err != nil {
		t.Fatalf("fetch: %v", err)
	}
	if sec.AtlasPassword != "atlas-pw" || sec.RootPassword != "root-pw" || sec.K3sToken != "t1" || sec.CloudInit != "ci" {
		t.Fatalf("unexpected secrets: %+v", sec)
	}
}

func TestApproRoleLogin(t *testing.T) {
	loginCalled := false
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.URL.Path {
		case "/v1/auth/approle/login":
			loginCalled = true
			w.Header().Set("Content-Type", "application/json")
			_ = json.NewEncoder(w).Encode(map[string]any{
				"auth": map[string]any{
					"client_token": "newtoken",
				},
			})
		case "/v1/kv/data/atlas/nodes/n1":
			if r.Header.Get("X-Vault-Token") != "newtoken" {
				t.Fatalf("missing token after approle login")
			}
			w.Header().Set("Content-Type", "application/json")
			_ = json.NewEncoder(w).Encode(map[string]any{
				"data": map[string]any{
					"data": map[string]any{},
				},
			})
		default:
			http.NotFound(w, r)
		}
	}))
	defer srv.Close()

	c := &Client{Addr: srv.URL, RoleID: "r", SecretID: "s", Client: srv.Client()}
	if _, err := c.FetchNode(context.Background(), "n1"); err != nil {
		t.Fatalf("fetch with approle: %v", err)
	}
	if !loginCalled {
		t.Fatalf("approle login not called")
	}
}

func TestLoginIfNeededNoopWithToken(t *testing.T) {
	c := &Client{Addr: "http://example.invalid", Token: "existing"}
	if err := c.LoginIfNeeded(context.Background()); err != nil {
		t.Fatalf("LoginIfNeeded: %v", err)
	}
	if c.Token != "existing" {
		t.Fatalf("token unexpectedly changed")
	}
}

func TestNewFromEnvPopulatesCredentials(t *testing.T) {
	t.Setenv("VAULT_ADDR", "http://vault.example")
	t.Setenv("VAULT_TOKEN", "tok")
	t.Setenv("VAULT_ROLE_ID", "role")
	t.Setenv("VAULT_SECRET_ID", "secret")
	c := NewFromEnv()
	if c.Addr != "http://vault.example" || c.Token != "tok" || c.RoleID != "role" || c.SecretID != "secret" {
		t.Fatalf("unexpected env client: %+v", c)
	}
}

func TestFetchNodeAndLoginErrorBranches(t *testing.T) {
	srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		switch r.URL.Path {
		case "/v1/auth/approle/login":
			http.Error(w, "denied", http.StatusForbidden)
		case "/v1/kv/data/atlas/nodes/missing":
			http.Error(w, "down", http.StatusInternalServerError)
		default:
			http.NotFound(w, r)
		}
	}))
	defer srv.Close()

	c := &Client{Addr: srv.URL, RoleID: "r", SecretID: "s", Client: srv.Client()}
	if _, err := c.FetchNode(context.Background(), "missing"); err == nil {
		t.Fatal("expected approle login failure")
	}
	c = &Client{Addr: srv.URL, Token: "tok", Client: srv.Client()}
	if _, err := c.FetchNode(context.Background(), "missing"); err == nil {
		t.Fatal("expected fetch error for 500 response")
	}
}
