/*
 * Copyright 2021 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 test

import (
	"bytes"
	"context"
	"io"
	"net"
	"net/http"
	"os"
	"testing"
	"time"

	"github.com/jhump/protoreflect/dynamic"
	"google.golang.org/protobuf/proto"

	"github.com/cloudwego/kitex/client/callopt"
	"github.com/cloudwego/kitex/client/genericclient"
	"github.com/cloudwego/kitex/internal/test"
	"github.com/cloudwego/kitex/pkg/generic"
	"github.com/cloudwego/kitex/pkg/generic/descriptor"
	"github.com/cloudwego/kitex/pkg/generic/httppb_test/idl"
	"github.com/cloudwego/kitex/server"
)

func TestRun(t *testing.T) {
	t.Run("testThriftNormalEcho", testThriftNormalEcho)
}

func initThriftClientByIDL(t *testing.T, addr, idl, pbIdl string) genericclient.Client {
	p, err := generic.NewThriftFileProvider(idl)
	test.Assert(t, err == nil)
	pbf, err := os.Open(pbIdl)
	test.Assert(t, err == nil)
	pbContent, err := io.ReadAll(pbf)
	test.Assert(t, err == nil)
	pbf.Close()
	pbp, err := generic.NewPbContentProvider(pbIdl, map[string]string{pbIdl: string(pbContent)})
	test.Assert(t, err == nil)
	g, err := generic.HTTPPbThriftGeneric(p, pbp)
	test.Assert(t, err == nil)
	cli := newGenericClient("destServiceName", g, addr)
	test.Assert(t, err == nil)
	return cli
}

func initThriftServer(t *testing.T, address string, handler generic.Service) server.Server {
	addr, _ := net.ResolveTCPAddr("tcp", address)
	p, err := generic.NewThriftFileProvider("./idl/echo.thrift")
	test.Assert(t, err == nil)
	g, err := generic.MapThriftGeneric(p)
	test.Assert(t, err == nil)
	svr := newGenericServer(g, addr, handler)
	test.Assert(t, err == nil)
	test.WaitServerStart(addr.String())
	return svr
}

func testThriftNormalEcho(t *testing.T) {
	addr := test.GetLocalAddress()
	svr := initThriftServer(t, addr, new(GenericServiceEchoImpl))
	cli := initThriftClientByIDL(t, addr, "./idl/echo.thrift", "./idl/echo.proto")
	url := "http://example.com/Echo"

	// []byte value for field
	body := &idl.Message{
		Tiny:  3,
		Large: 1 << 40,
		Tenum: idl.TestEnum_First,
		Str:   "test string",
		Elems: map[string]*idl.Elem{
			"key1": {Ok: true},
			"key2": {Ok: false},
		},
		Els: []*idl.Elem{{Ok: true}},
	}
	data, err := proto.Marshal(body)
	if err != nil {
		panic(err)
	}
	req, err := http.NewRequest(http.MethodGet, url, bytes.NewBuffer(data))
	if err != nil {
		panic(err)
	}
	customReq, err := generic.FromHTTPPbRequest(req)
	if err != nil {
		t.Fatal(err)
	}
	resp, err := cli.GenericCall(context.Background(), "", customReq, callopt.WithRPCTimeout(100*time.Second))
	test.Assert(t, err == nil, err)
	gr, ok := resp.(*generic.HTTPResponse)
	test.Assert(t, ok)
	test.Assert(t, gr.ContentType == descriptor.MIMEApplicationProtobuf)
	pbresp := gr.GeneralBody.(*dynamic.Message)

	res := &idl.Message{}
	err = pbresp.ConvertTo(res)
	test.Assert(t, err == nil)

	test.Assert(t, body.Tiny == res.Tiny)
	test.Assert(t, body.Large == res.Large)
	test.Assert(t, body.Tenum == res.Tenum)
	test.Assert(t, body.Str == res.Str)
	test.Assert(t, body.Elems["key1"].Ok == res.Elems["key1"].Ok)
	test.Assert(t, body.Elems["key2"].Ok == res.Elems["key2"].Ok)
	test.Assert(t, body.Els[0].Ok == res.Els[0].Ok)

	svr.Stop()
}
