package proxmox

import (
	"context"
	"io"
	"net/http"
	"testing"
)

// PoolAddVMID issues PUT /pools/{pool} with vms={vmid} and treats an already-member error as success.
func TestPoolAddVMID_PUTShape(t *testing.T) {
	var gotMethod, gotPath, gotVMS string
	d := &mockDoer{fn: func(r *http.Request) (*http.Response, error) {
		gotMethod = r.Method
		gotPath = r.URL.Path
		body, _ := io.ReadAll(r.Body) // the client encodes url.Values into the request body
		for _, kv := range splitAmp(string(body)) {
			if k, v, ok := cut(kv, "="); ok && k == "vms" {
				gotVMS = v
			}
		}
		return jsonResp(http.StatusOK, `{"data":null}`), nil
	}}
	c := newTestClient(d)
	if err := c.PoolAddVMID(context.Background(), "felhom", 9201); err != nil {
		t.Fatalf("PoolAddVMID: %v", err)
	}
	if gotMethod != http.MethodPut {
		t.Errorf("method = %q, want PUT", gotMethod)
	}
	if gotPath != "/api2/json/pools/felhom" {
		t.Errorf("path = %q, want /api2/json/pools/felhom", gotPath)
	}
	if gotVMS != "9201" {
		t.Errorf("vms param = %q, want 9201", gotVMS)
	}
}

// Idempotent: an "already in pool" error from PVE is swallowed as success.
func TestPoolAddVMID_IdempotentOnAlreadyMember(t *testing.T) {
	d := &mockDoer{fn: func(_ *http.Request) (*http.Response, error) {
		return jsonResp(http.StatusInternalServerError, `{"data":null,"errors":{"vms":"VM 9201 is already in pool 'felhom'"}}`), nil
	}}
	c := newTestClient(d)
	if err := c.PoolAddVMID(context.Background(), "felhom", 9201); err != nil {
		t.Fatalf("already-member should be idempotent success, got: %v", err)
	}
}

// A real failure (e.g. a 403 with no "already") is surfaced, not swallowed.
func TestPoolAddVMID_RealErrorSurfaces(t *testing.T) {
	d := &mockDoer{fn: func(_ *http.Request) (*http.Response, error) {
		return jsonResp(http.StatusForbidden, `Permission check failed (/pool/felhom, Pool.Allocate)`), nil
	}}
	c := newTestClient(d)
	if err := c.PoolAddVMID(context.Background(), "felhom", 9201); err == nil {
		t.Fatal("expected a real 403 to surface, got nil")
	}
}

func TestPoolAddVMID_Validation(t *testing.T) {
	c := newTestClient(&mockDoer{fn: func(_ *http.Request) (*http.Response, error) {
		t.Fatal("should not issue a request on bad input")
		return nil, nil
	}})
	if err := c.PoolAddVMID(context.Background(), "", 9201); err == nil {
		t.Error("empty pool should error")
	}
	if err := c.PoolAddVMID(context.Background(), "felhom", 0); err == nil {
		t.Error("zero vmid should error")
	}
}

// tiny helpers (avoid pulling strings.Split into an assertion path that could mask a bug)
func splitAmp(s string) []string {
	var out []string
	cur := ""
	for _, r := range s {
		if r == '&' {
			out = append(out, cur)
			cur = ""
			continue
		}
		cur += string(r)
	}
	return append(out, cur)
}

func cut(s, sep string) (string, string, bool) {
	for i := 0; i+len(sep) <= len(s); i++ {
		if s[i:i+len(sep)] == sep {
			return s[:i], s[i+len(sep):], true
		}
	}
	return s, "", false
}
