/*
 * Copyright 2023 CloudWeGo Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package descriptor

import (
	"bytes"
	"net/http"
	"net/url"
	"reflect"
	"testing"

	"github.com/cloudwego/kitex/internal/test"
)

func TestJSONBody(t *testing.T) {
	data := []byte(`{"name":"foo","age":18}`)
	r, err := createHTTPRequest(data)
	if err != nil {
		t.Fatal(err)
	}
	test.Assert(t, r.GetHeader("Content-Type") == "application/json")
	test.Assert(t, r.GetCookie("token") == "some_token")
	test.Assert(t, r.GetQuery("id") == "123")
	test.Assert(t, r.GetParam("param1") == "value1")
	test.Assert(t, reflect.DeepEqual(r.GetBody(), data))
	test.Assert(t, r.GetMethod() == "POST")
	test.Assert(t, r.GetPath() == "/users")
	test.Assert(t, r.GetHost() == "localhost:8080")
	test.Assert(t, r.GetMapBody("name") == `foo`)
	test.Assert(t, r.GetMapBody("age") == "18")
	test.Assert(t, r.GetPostForm("key1") == "val1")
	test.Assert(t, r.GetUri() == "http://localhost:8080/users?id=123", r.GetUri())

	resp := NewHTTPResponse()
	resp.SetStatusCode(200)
	resp.SetHeader("Content-Type", "application/json")
	resp.SetCookie("token", "some-token")
	resp.SetRawBody(data)
	test.Assert(t, resp.StatusCode == 200)
	test.Assert(t, resp.Header.Get("Content-Type") == "application/json")
	test.Assert(t, resp.ContentType == "application/json")
	test.Assert(t, reflect.DeepEqual(resp.RawBody, data))
}

func TestMapBody(t *testing.T) {
	r, err := createHTTPRequest([]byte(``))
	if err != nil {
		t.Fatal(err)
	}
	err = r.initializeBodyMap()
	test.Assert(t, err != nil)
	test.Assert(t, r.GetMapBody("test") == "")
	r, err = createHTTPRequest([]byte(`{"name":"foo","age":18}`))
	if err != nil {
		t.Fatal(err)
	}
	test.Assert(t, r.bodyMap == nil)
	err = r.initializeBodyMap()
	test.Assert(t, err == nil)
	test.Assert(t, r.bodyMap != nil)
	test.Assert(t, r.GetMapBody("name") == `foo`)
	test.Assert(t, r.GetMapBody("test") == "")
}

func TestGetQuery(t *testing.T) {
	r, err := createHTTPRequest([]byte(`{"name":"foo","age":18}`))
	if err != nil {
		t.Fatal(err)
	}
	test.Assert(t, r.query == nil)
	test.Assert(t, r.GetQuery("id") == "123")
	test.Assert(t, r.query != nil)
}

func TestGetCookie(t *testing.T) {
	r, err := createHTTPRequest([]byte(`{"name":"foo","age":18}`))
	if err != nil {
		t.Fatal(err)
	}
	test.Assert(t, r.cookies == nil)
	test.Assert(t, r.GetCookie("token") == "some_token")
	test.Assert(t, r.cookies != nil)
}

func TestNilURL(t *testing.T) {
	r, err := createHTTPRequestWithNoURL([]byte(""))
	if err != nil {
		t.Fatal(err)
	}
	test.Assert(t, r.Request.URL == nil)
	test.Assert(t, r.GetQuery("") == "")
	test.Assert(t, r.GetPath() == "")
	test.Assert(t, r.GetUri() == "")
}

func createHTTPRequestWithNoURL(data []byte) (*HTTPRequest, error) {
	req, err := http.NewRequest("POST", "", bytes.NewBuffer(data))
	if err != nil {
		return nil, err
	}
	req.URL = nil
	param := Param{
		Key:   "param1",
		Value: "value1",
	}
	pslice := []Param{param}
	params := Params{
		params: pslice,
	}
	r := &HTTPRequest{
		Request: req,
		RawBody: data,
		Params:  &params,
	}
	return r, nil
}

func createHTTPRequest(data []byte) (*HTTPRequest, error) {
	req, err := http.NewRequest("POST", "http://localhost:8080/users?id=123", bytes.NewBuffer(data))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")
	cookie := &http.Cookie{
		Name:  "token",
		Value: "some_token",
	}
	req.AddCookie(cookie)
	req.URL.Path = "/users"
	req.PostForm = url.Values{
		"key1": {"val1", "val2"},
	}
	param := Param{
		Key:   "param1",
		Value: "value1",
	}
	pslice := []Param{param}
	params := Params{
		params: pslice,
	}
	r := &HTTPRequest{
		Request: req,
		RawBody: data,
		Params:  &params,
	}
	return r, nil
}
