package web

import (
	"context"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"gitea.dooplex.hu/admin/felhom-controller/internal/agentapi"
)

// fakeMemAgent is the memAgent seam for handler tests. AgentVersion drives the capability gate
// (version fast-path); NetVerifyStatus is the SupportProber stub.
type fakeMemAgent struct {
	info      agentapi.GuestMemoryInfo
	resizeRes agentapi.MemoryResizeResult
	resizeErr error
	ver       string
	calls     int
}

func (f *fakeMemAgent) GuestMemory(context.Context) (agentapi.GuestMemoryInfo, error) {
	return f.info, nil
}
func (f *fakeMemAgent) ResizeMemory(_ context.Context, _ int64) (agentapi.MemoryResizeResult, error) {
	f.calls++
	return f.resizeRes, f.resizeErr
}
func (f *fakeMemAgent) NetVerifyStatus(context.Context) (agentapi.NetVerifyStatus, error) {
	return agentapi.NetVerifyStatus{Phase: "none"}, nil
}
func (f *fakeMemAgent) AgentVersion() string { return f.ver }

func postResize(t *testing.T, s *Server, body string) *httptest.ResponseRecorder {
	t.Helper()
	r := httptest.NewRequest(http.MethodPost, "/api/system/memory/resize", strings.NewReader(body))
	w := httptest.NewRecorder()
	s.ServeSystemAPI(w, r)
	return w
}

// Success → 200 with the Hungarian old→new message.
func TestMemoryResize_Success(t *testing.T) {
	s := testServer(t)
	agent := &fakeMemAgent{ver: "0.90.0", resizeRes: agentapi.MemoryResizeResult{OldMB: 8192, NewMB: 12288}}
	s.memAgentFn = func() (memAgent, error) { return agent, nil }

	w := postResize(t, s, `{"memory_mb":12288}`)
	if w.Code != http.StatusOK {
		t.Fatalf("resize = %d (%s), want 200", w.Code, w.Body.String())
	}
	if !strings.Contains(w.Body.String(), "A memória átméretezése megtörtént: 8192 MB → 12288 MB.") {
		t.Errorf("success message missing: %s", w.Body.String())
	}
	if agent.calls != 1 {
		t.Errorf("ResizeMemory calls = %d, want 1", agent.calls)
	}
}

// below_usage_floor refusal → 412, the mapped Hungarian message, and the machine code (agent English
// never shown raw).
func TestMemoryResize_BelowUsageFloor(t *testing.T) {
	s := testServer(t)
	agent := &fakeMemAgent{
		ver: "0.90.0",
		resizeErr: &agentapi.MemoryRefusedError{
			Code:   "below_usage_floor",
			Bounds: agentapi.GuestMemoryInfo{UsageMB: 3000, FloorMB: 3512, MinMB: 2048, MaxMB: 14336},
			Msg:    "requested 3300 MB is too close to current usage 3000 MB (floor 3512 MB)",
		},
	}
	s.memAgentFn = func() (memAgent, error) { return agent, nil }

	w := postResize(t, s, `{"memory_mb":3300}`)
	if w.Code != http.StatusPreconditionFailed {
		t.Fatalf("below_usage_floor = %d (%s), want 412", w.Code, w.Body.String())
	}
	body := w.Body.String()
	if !strings.Contains(body, `"code":"below_usage_floor"`) {
		t.Errorf("machine code missing: %s", body)
	}
	if !strings.Contains(body, "túl közel van a jelenlegi felhasználáshoz (3000 MB)") {
		t.Errorf("mapped Hungarian message missing: %s", body)
	}
	// The agent's raw English must NEVER surface.
	if strings.Contains(body, "requested 3300 MB is too close") {
		t.Errorf("agent English leaked to the customer: %s", body)
	}
}

// A pre-0.90 agent (version < MinAgent) → the capability gate refuses up front with agent_outdated,
// and ResizeMemory is NEVER called.
func TestMemoryResize_AgentOutdated(t *testing.T) {
	s := testServer(t)
	agent := &fakeMemAgent{ver: "0.89.0"}
	s.memAgentFn = func() (memAgent, error) { return agent, nil }

	w := postResize(t, s, `{"memory_mb":12288}`)
	if w.Code != http.StatusPreconditionFailed {
		t.Fatalf("outdated agent = %d (%s), want 412", w.Code, w.Body.String())
	}
	if !strings.Contains(w.Body.String(), `"code":"agent_outdated"`) {
		t.Errorf("agent_outdated code missing: %s", w.Body.String())
	}
	if !strings.Contains(w.Body.String(), "rendszerfrissítése szükséges") {
		t.Errorf("outdated Hungarian note missing: %s", w.Body.String())
	}
	if agent.calls != 0 {
		t.Errorf("ResizeMemory called %d times on a gated request — must be 0", agent.calls)
	}
}
