package tools

import (
	"bytes"
	"io"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"

	"cloud.o-forge.io/core/oc-lib/tools"
)

func TestMethodString(t *testing.T) {
	tests := []struct {
		method   tools.METHOD
		expected string
	}{
		{tools.GET, "GET"},
		{tools.PUT, "PUT"},
		{tools.POST, "POST"},
		{tools.POSTCHECK, "POST"},
		{tools.DELETE, "DELETE"},
		{tools.STRICT_INTERNAL_GET, "INTERNALGET"},
		{tools.STRICT_INTERNAL_PUT, "INTERNALPUT"},
		{tools.STRICT_INTERNAL_POST, "INTERNALPOST"},
		{tools.STRICT_INTERNAL_DELETE, "INTERNALDELETE"},
	}

	for _, test := range tests {
		if test.method.String() != test.expected {
			t.Errorf("Expected %s, got %s", test.expected, test.method.String())
		}
	}
}

func TestToMethod(t *testing.T) {
	method := tools.ToMethod("INTERNALPUT")
	if method != tools.STRICT_INTERNAL_PUT {
		t.Errorf("Expected STRICT_INTERNAL_PUT, got %v", method)
	}

	defaultMethod := tools.ToMethod("INVALID")
	if defaultMethod != tools.GET {
		t.Errorf("Expected default GET, got %v", defaultMethod)
	}
}

func TestEnumIndex(t *testing.T) {
	if tools.GET.EnumIndex() != 0 {
		t.Errorf("Expected index 0 for GET, got %d", tools.GET.EnumIndex())
	}
}

func TestCallGet(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`"ok"`))
	}))
	defer ts.Close()

	caller := &tools.HTTPCaller{}
	body, err := caller.CallGet(ts.URL, "/test", "application/json")
	if err != nil || string(body) != `"ok"` {
		t.Errorf("Expected body to be ok, got %s", string(body))
	}
}

func TestCallPost(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		io.Copy(w, r.Body)
	}))
	defer ts.Close()

	caller := &tools.HTTPCaller{}
	body, err := caller.CallPost(ts.URL, "/post", map[string]string{"key": "val"})
	if err != nil || !strings.Contains(string(body), "key") {
		t.Errorf("POST failed, body: %s", string(body))
	}
}

func TestCallPut(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		io.Copy(w, r.Body)
	}))
	defer ts.Close()

	caller := &tools.HTTPCaller{}
	body, err := caller.CallPut(ts.URL, "/put", map[string]interface{}{"foo": "bar"})
	if err != nil || !strings.Contains(string(body), "foo") {
		t.Errorf("PUT failed, body: %s", string(body))
	}
}

func TestCallDelete(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`deleted`))
	}))
	defer ts.Close()

	caller := &tools.HTTPCaller{}
	body, err := caller.CallDelete(ts.URL, "/delete")
	if err != nil || string(body) != "deleted" {
		t.Errorf("DELETE failed, body: %s", string(body))
	}
}

func TestCallRaw(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
	}))
	defer ts.Close()

	caller := &tools.HTTPCaller{}
	resp, err := caller.CallRaw("POST", ts.URL, "/", map[string]interface{}{"a": 1}, "application/json", true)
	if err != nil || resp.StatusCode != http.StatusOK {
		t.Errorf("CallRaw failed")
	}
}

func TestCallForm(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.Method != http.MethodPost {
			t.Errorf("Expected POST, got %s", r.Method)
		}
	}))
	defer ts.Close()

	caller := &tools.HTTPCaller{}
	form := url.Values{}
	form.Set("foo", "bar")

	_, err := caller.CallForm("POST", ts.URL, "/form", form, "application/x-www-form-urlencoded", true)
	if err != nil {
		t.Errorf("CallForm error: %v", err)
	}
}

func TestStoreResp(t *testing.T) {
	resp := &http.Response{
		Header:     http.Header{},
		StatusCode: 200,
		Body:       io.NopCloser(bytes.NewBuffer([]byte("body content"))),
	}

	caller := &tools.HTTPCaller{}
	err := caller.StoreResp(resp)
	if err != nil {
		t.Errorf("StoreResp failed: %v", err)
	}

	if string(caller.LastResults["body"].([]byte)) != "body content" {
		t.Errorf("Expected body content")
	}
}

func TestNewHTTPCaller(t *testing.T) {
	c := tools.NewHTTPCaller(nil)
	if c.Disabled != false {
		t.Errorf("Expected Disabled false")
	}
}

func TestGetUrls(t *testing.T) {
	urls := map[tools.DataType]map[tools.METHOD]string{}
	c := tools.NewHTTPCaller(urls)
	if c.GetUrls() == nil {
		t.Errorf("GetUrls returned nil")
	}
}

func TestDeepCopy(t *testing.T) {
	original := tools.NewHTTPCaller(nil)
	copy := tools.HTTPCaller{}
	err := original.DeepCopy(copy)
	if err != nil {
		t.Errorf("DeepCopy failed: %v", err)
	}
}

func TestCallPost_InvalidJSON(t *testing.T) {
	caller := &tools.HTTPCaller{}
	_, err := caller.CallPost("http://invalid", "/post", func() {})
	if err == nil {
		t.Error("Expected error when marshaling unsupported type")
	}
}

func TestCallPut_ErrorOnNewRequest(t *testing.T) {
	caller := &tools.HTTPCaller{}
	_, err := caller.CallPut("http://[::1]:namedport", "/put", nil)
	if err == nil {
		t.Error("Expected error from invalid URL")
	}
}

func TestCallGet_Error(t *testing.T) {
	caller := &tools.HTTPCaller{}
	_, err := caller.CallGet("http://[::1]:namedport", "/bad", "application/json")
	if err == nil {
		t.Error("Expected error from invalid URL")
	}
}

func TestCallDelete_Error(t *testing.T) {
	caller := &tools.HTTPCaller{}
	_, err := caller.CallDelete("http://[::1]:namedport", "/bad")
	if err == nil {
		t.Error("Expected error from invalid URL")
	}
}

func TestCallRaw_Error(t *testing.T) {
	caller := &tools.HTTPCaller{}
	_, err := caller.CallRaw("POST", "http://[::1]:namedport", "/raw", nil, "application/json", false)
	if err == nil {
		t.Error("Expected error from invalid URL")
	}
}

func TestCallForm_Error(t *testing.T) {
	caller := &tools.HTTPCaller{}
	_, err := caller.CallForm("POST", "http://[::1]:namedport", "/form", url.Values{}, "application/json", false)
	if err == nil {
		t.Error("Expected error from invalid URL")
	}
}