package api_test

import (
	"net/http"
	"testing"
)

// TestConfigSetCRUD exercises create → get → version → apply over HTTP, using
// the stub data source's writable "setpoint" signal as the apply target.
func TestConfigManagerEndToEnd(t *testing.T) {
	srv, teardown := setup(t)
	defer teardown()

	// Create a config set targeting the stub's writable setpoint.
	setBody := map[string]any{
		"name": "Stub PSU",
		"parameters": []map[string]any{
			{"key": "sp", "ds": "stub", "signal": "setpoint", "type": "float64", "default": 42.0, "mandatory": true, "min": 0.0, "max": 100.0},
		},
	}
	resp := postJSON(t, srv, "/api/v1/config/sets", setBody)
	assertStatus(t, resp, http.StatusCreated)
	var set struct {
		ID      string `json:"id"`
		Version int    `json:"version"`
	}
	readJSON(t, resp, &set)
	if set.ID == "" || set.Version != 1 {
		t.Fatalf("unexpected created set: %+v", set)
	}

	// List sets includes it.
	resp = get(t, srv, "/api/v1/config/sets")
	assertStatus(t, resp, http.StatusOK)
	var sets []map[string]any
	readJSON(t, resp, &sets)
	if len(sets) != 1 {
		t.Fatalf("want 1 set, got %d", len(sets))
	}

	// Create an instance assigning a concrete value.
	instBody := map[string]any{
		"name":   "nominal",
		"setId":  set.ID,
		"values": map[string]any{"sp": 73.0},
	}
	resp = postJSON(t, srv, "/api/v1/config/instances", instBody)
	assertStatus(t, resp, http.StatusCreated)
	var inst struct {
		ID string `json:"id"`
	}
	readJSON(t, resp, &inst)
	if inst.ID == "" {
		t.Fatal("instance missing id")
	}

	// Apply writes the value to the target signal.
	resp = postJSON(t, srv, "/api/v1/config/instances/"+inst.ID+"/apply", nil)
	assertStatus(t, resp, http.StatusOK)
	var res struct {
		Applied int `json:"applied"`
		Failed  int `json:"failed"`
		Entries []struct {
			Signal string `json:"signal"`
			OK     bool   `json:"ok"`
		} `json:"entries"`
	}
	readJSON(t, resp, &res)
	if res.Applied != 1 || res.Failed != 0 {
		t.Fatalf("apply summary: applied=%d failed=%d", res.Applied, res.Failed)
	}
	if len(res.Entries) != 1 || res.Entries[0].Signal != "setpoint" || !res.Entries[0].OK {
		t.Errorf("unexpected apply entries: %+v", res.Entries)
	}

	// Delete the set soft-deletes it.
	resp = deleteReq(t, srv, "/api/v1/config/sets/"+set.ID)
	assertStatus(t, resp, http.StatusNoContent)
	resp = get(t, srv, "/api/v1/config/sets/"+set.ID)
	assertStatus(t, resp, http.StatusNotFound)
}

// TestConfigInstanceRejectsInvalidValue verifies server-side validation against
// the bound set (value above the parameter's max).
func TestConfigInstanceRejectsInvalidValue(t *testing.T) {
	srv, teardown := setup(t)
	defer teardown()

	resp := postJSON(t, srv, "/api/v1/config/sets", map[string]any{
		"name": "S",
		"parameters": []map[string]any{
			{"key": "sp", "ds": "stub", "signal": "setpoint", "type": "float64", "max": 10.0},
		},
	})
	assertStatus(t, resp, http.StatusCreated)
	var set struct {
		ID string `json:"id"`
	}
	readJSON(t, resp, &set)

	resp = postJSON(t, srv, "/api/v1/config/instances", map[string]any{
		"name":   "bad",
		"setId":  set.ID,
		"values": map[string]any{"sp": 999.0},
	})
	assertStatus(t, resp, http.StatusBadRequest)
}
