/*
Copyright (c) 2026 Huawei Technologies Co., Ltd.
openFuyao is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
         http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/

package remote

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

// testData is a simple struct for testing generic functions.
type testData struct {
	Name  string `json:"name"`
	Value int    `json:"value"`
}

const (
	testExpectedValue     = 100
	testCustomTimeoutSec  = 10
	testHTTPClientTimeout = 5
	testRetryCount        = 3
	testFluentTimeoutSec  = 15
	testFluentRetryCount  = 5
)

func setupTestHTTPServer(handler http.HandlerFunc) (*httptest.Server, *httpClient) {
	ts := httptest.NewTLSServer(handler)
	httpClient := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{
				InsecureSkipVerify: true, // Skip TLS verification for test
			},
		},
	}
	client := NewHTTPClientBuilder(ts.URL).WithHTTPClient(httpClient).Build()
	return ts, client
}

func mockPostSuccessHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusCreated)
	resp := ubseResponse[testData]{
		Code: 0,
		Msg:  "success",
		Data: testData{Name: "test", Value: testExpectedValue},
	}
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		panic(err) // panic in test is acceptable
	}
}

func mockPostHTTPErrorHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusInternalServerError)
	if _, err := w.Write([]byte("internal server error")); err != nil {
		panic(err) // panic in test is acceptable
	}
}

func mockPostBusinessErrorHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusCreated)
	resp := ubseResponse[testData]{
		Code: 1,
		Msg:  "volume already exists",
	}
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		panic(err) // panic in test is acceptable
	}
}

func mockPostInvalidJSONHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusCreated)
	if _, err := w.Write([]byte("invalid json")); err != nil {
		panic(err) // panic in test is acceptable
	}
}

func mockHTTPDeleteSuccessHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusOK)
	resp := ubseResponse[testData]{
		Code: 0,
		Msg:  "success",
	}
	if err := json.NewEncoder(w).Encode(resp); err != nil {
		panic(err) // panic in test is acceptable
	}
}

func mockHTTPDeleteErrorHandler(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(http.StatusInternalServerError)
	if _, err := w.Write([]byte("internal server error")); err != nil {
		panic(err) // panic in test is acceptable
	}
}

func TestPost(t *testing.T) {
	t.Run("成功场景", func(t *testing.T) {
		t.Run("返回正确解析的数据", func(t *testing.T) {
			ts, client := setupTestHTTPServer(mockPostSuccessHandler)
			defer ts.Close()
			body := testData{Name: "request", Value: 50}
			result, err := Post[testData](client, context.Background(), "/test", body)
			require.NoError(t, err)
			assert.Equal(t, "test", result.Name)
			assert.Equal(t, testExpectedValue, result.Value)
		})
	})
	t.Run("失败场景", func(t *testing.T) {
		t.Run("HTTP状态码错误", func(t *testing.T) {
			ts, client := setupTestHTTPServer(mockPostHTTPErrorHandler)
			defer ts.Close()
			body := testData{Name: "request", Value: 50}
			_, err := Post[testData](client, context.Background(), "/test", body)
			require.Error(t, err)
			ubseErr, ok := err.(*UBSEResp)
			require.True(t, ok)
			assert.Equal(t, http.StatusInternalServerError, ubseErr.HTTPStatus)
		})
		t.Run("UBSE业务错误码", func(t *testing.T) {
			ts, client := setupTestHTTPServer(mockPostBusinessErrorHandler)
			defer ts.Close()
			body := testData{Name: "request", Value: 50}
			_, err := Post[testData](client, context.Background(), "/test", body)
			require.Error(t, err)
			ubseErr, ok := err.(*UBSEResp)
			require.True(t, ok)
			assert.Equal(t, 1, ubseErr.Code)
			assert.Contains(t, ubseErr.Msg, "already exists")
		})
		t.Run("JSON解析错误", func(t *testing.T) {
			ts, client := setupTestHTTPServer(mockPostInvalidJSONHandler)
			defer ts.Close()
			body := testData{Name: "request", Value: 50}
			_, err := Post[testData](client, context.Background(), "/test", body)
			require.Error(t, err)
			assert.Contains(t, err.Error(), "unmarshal")
		})
	})
}

func TestDelete(t *testing.T) {
	t.Run("成功场景", func(t *testing.T) {
		t.Run("返回成功响应", func(t *testing.T) {
			ts, client := setupTestHTTPServer(mockHTTPDeleteSuccessHandler)
			defer ts.Close()
			result, err := Delete[testData](client, context.Background(), "/test/vol-1")
			require.NoError(t, err)
			assert.NotNil(t, result)
		})
	})
	t.Run("失败场景", func(t *testing.T) {
		t.Run("HTTP状态码错误", func(t *testing.T) {
			ts, client := setupTestHTTPServer(mockHTTPDeleteErrorHandler)
			defer ts.Close()
			_, err := Delete[testData](client, context.Background(), "/test/vol-1")
			require.Error(t, err)
			ubseErr, ok := err.(*UBSEResp)
			require.True(t, ok)
			assert.Equal(t, http.StatusInternalServerError, ubseErr.HTTPStatus)
		})
	})
}

func TestHTTPClientBuilder(t *testing.T) {
	t.Run("默认配置", func(t *testing.T) {
		client := NewHTTPClientBuilder("https://test.com").Build()
		assert.NotNil(t, client)
	})
	t.Run("自定义超时", func(t *testing.T) {
		client := NewHTTPClientBuilder("https://test.com").
			WithTimeout(testCustomTimeoutSec * time.Second).
			Build()
		assert.NotNil(t, client)
	})
	t.Run("自定义HTTPClient", func(t *testing.T) {
		customClient := &http.Client{Timeout: testHTTPClientTimeout * time.Second}
		client := NewHTTPClientBuilder("https://test.com").
			WithHTTPClient(customClient).
			Build()
		assert.NotNil(t, client)
	})
	t.Run("自定义重试次数", func(t *testing.T) {
		client := NewHTTPClientBuilder("https://test.com").
			WithRetry(testRetryCount).
			Build()
		assert.NotNil(t, client)
	})
	t.Run("Fluent链式调用", func(t *testing.T) {
		customClient := &http.Client{Timeout: testFluentTimeoutSec * time.Second}
		client := NewHTTPClientBuilder("https://test.com").
			WithTimeout(testFluentTimeoutSec * time.Second).
			WithRetry(testFluentRetryCount).
			WithHTTPClient(customClient).
			Build()
		assert.NotNil(t, client)
	})
}

func TestUBSEError(t *testing.T) {
	t.Run("带HTTP状态的错误信息", func(t *testing.T) {
		err := &UBSEResp{HTTPStatus: 500, Code: 1, Msg: "test error"}
		assert.Contains(t, err.Error(), "http_status=500")
		assert.Contains(t, err.Error(), "code=1")
	})
	t.Run("不带HTTP状态的错误信息", func(t *testing.T) {
		err := &UBSEResp{Code: 1, Msg: "test error"}
		assert.Contains(t, err.Error(), "code=1")
		assert.NotContains(t, err.Error(), "http_status")
	})
}