/*
 * 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 server

import (
	"context"
	"errors"
	"testing"
	"time"

	"github.com/golang/mock/gomock"

	"github.com/cloudwego/kitex/internal/mocks"
	mock_remote "github.com/cloudwego/kitex/internal/mocks/remote"
	internal_server "github.com/cloudwego/kitex/internal/server"
	"github.com/cloudwego/kitex/internal/test"
	"github.com/cloudwego/kitex/pkg/acl"
	"github.com/cloudwego/kitex/pkg/rpcinfo"
)

// TestWithServerBasicInfo tests the creation of a server with basic info
func TestWithServerBasicInfo(t *testing.T) {
	svcName := "svcNameWithServerBasicInfo"
	svr := NewServer(WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{
		ServiceName: svcName,
	}))
	iSvr := svr.(*server)
	test.Assert(t, iSvr.opt.Svr.ServiceName == svcName, iSvr.opt.Svr.ServiceName)
}

// TestWithServerBasicInfo tests the creation of a server with ACLRules option
func TestACLRulesOption(t *testing.T) {
	var rules []acl.RejectFunc
	rules = append(rules, func(ctx context.Context, request interface{}) error {
		return nil
	})
	rules = append(rules, func(ctx context.Context, request interface{}) error {
		return nil
	})

	svr, _ := NewTestServer(WithACLRules(rules...))
	err := svr.RegisterService(mocks.ServiceInfo(), mocks.MyServiceHandler())
	test.Assert(t, err == nil, err)
	time.AfterFunc(100*time.Millisecond, func() {
		err := svr.Stop()
		test.Assert(t, err == nil, err)
	})
	err = svr.Run()
	test.Assert(t, err == nil, err)
	iSvr := svr.(*server)
	test.Assert(t, len(iSvr.opt.ACLRules) == 2, len(iSvr.opt.ACLRules))
	test.Assert(t, iSvr.opt.ACLRules[0] != nil)
	test.Assert(t, iSvr.opt.ACLRules[1] != nil)
}

// TestProxyOptionPanic tests the creation of a server with Proxy option,if the proxy is not nil, should panic
func TestProxyOptionPanic(t *testing.T) {
	o := internal_server.NewOptions(nil)
	o.Proxy = &proxyMock{}

	opts := []Option{
		WithServerBasicInfo(&rpcinfo.EndpointBasicInfo{
			ServiceName: "svcName",
		}),
		WithProxy(&proxyMock{}),
	}

	test.Panic(t, func() {
		internal_server.ApplyOptions(opts, o)
	})
}

type myLimitReporter struct{}

func (m *myLimitReporter) ConnOverloadReport() {
}

func (m *myLimitReporter) QPSOverloadReport() {
}

// TestLimitReporterOption tests the creation of a server with LimitReporter option
func TestLimitReporterOption(t *testing.T) {
	my := &myLimitReporter{}
	svr, _ := NewTestServer(WithLimitReporter(my))
	err := svr.RegisterService(mocks.ServiceInfo(), mocks.MyServiceHandler())
	test.Assert(t, err == nil, err)
	time.AfterFunc(100*time.Millisecond, func() {
		err := svr.Stop()
		test.Assert(t, err == nil, err)
	})
	err = svr.Run()
	test.Assert(t, err == nil, err)
	iSvr := svr.(*server)
	test.Assert(t, iSvr.opt.Limit.LimitReporter != nil)
	test.DeepEqual(t, iSvr.opt.Limit.LimitReporter, my)
}

// TestGenericOptionPanic tests the creation of a server with RemoteOpt.PayloadCodec,if the generic is nil, should panic
func TestGenericOptionPanic(t *testing.T) {
	test.Panic(t, func() {
		NewServer(WithGeneric(nil))
	})
}

// TestBoundHandlerOptionPanic tests the creation of a server with remote.BoundHandler option,if the bound handler is nil, should panic
func TestBoundHandlerOptionPanic(t *testing.T) {
	test.Panic(t, func() {
		NewServer(WithBoundHandler(nil))
	})
}

// TestWithBoundHandler tests the creation of a server with remote.BoundHandler option, if there are duplicate bound handlers, avoid creating an option
func TestWithBoundHandler(t *testing.T) {
	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	mockInboundHandler := mock_remote.NewMockInboundHandler(ctrl)
	opts := internal_server.NewOptions([]internal_server.Option{WithBoundHandler(mockInboundHandler)})
	test.Assert(t, len(opts.RemoteOpt.Outbounds) == 0)
	test.Assert(t, len(opts.RemoteOpt.Inbounds) == 1)

	opts = internal_server.NewOptions([]internal_server.Option{WithBoundHandler(mockInboundHandler), WithBoundHandler(mockInboundHandler)})
	test.Assert(t, len(opts.RemoteOpt.Outbounds) == 0)
	test.Assert(t, len(opts.RemoteOpt.Inbounds) == 1)

	mockInboundHandler2 := mock_remote.NewMockInboundHandler(ctrl)
	opts = internal_server.NewOptions([]internal_server.Option{WithBoundHandler(mockInboundHandler), WithBoundHandler(mockInboundHandler2)})
	test.Assert(t, len(opts.RemoteOpt.Outbounds) == 0)
	test.Assert(t, len(opts.RemoteOpt.Inbounds) == 1)

	mockOutboundHandler := mock_remote.NewMockOutboundHandler(ctrl)
	opts = internal_server.NewOptions([]internal_server.Option{WithBoundHandler(mockOutboundHandler)})
	test.Assert(t, len(opts.RemoteOpt.Outbounds) == 1)
	test.Assert(t, len(opts.RemoteOpt.Inbounds) == 0)

	opts = internal_server.NewOptions(
		[]internal_server.Option{WithBoundHandler(mockOutboundHandler), WithBoundHandler(mockOutboundHandler)})
	test.Assert(t, len(opts.RemoteOpt.Outbounds) == 1)
	test.Assert(t, len(opts.RemoteOpt.Inbounds) == 0)

	mockOutboundHandler2 := mock_remote.NewMockOutboundHandler(ctrl)
	opts = internal_server.NewOptions([]internal_server.Option{
		WithBoundHandler(mockOutboundHandler), WithBoundHandler(mockOutboundHandler2), WithBoundHandler(mockOutboundHandler),
	})
	test.Assert(t, len(opts.RemoteOpt.Outbounds) == 1)
	test.Assert(t, len(opts.RemoteOpt.Inbounds) == 0)

	mockDuplexBoundHandler := mock_remote.NewMockDuplexBoundHandler(ctrl)
	opts = internal_server.NewOptions([]internal_server.Option{
		WithBoundHandler(mockDuplexBoundHandler), WithBoundHandler(mockDuplexBoundHandler),
	})
	test.Assert(t, len(opts.RemoteOpt.Outbounds) == 1)
	test.Assert(t, len(opts.RemoteOpt.Inbounds) == 1)
}

// TestExitSignalOption tests the creation of a server with ExitSignal option
func TestExitSignalOption(t *testing.T) {
	stopSignal := make(chan error, 1)
	stopErr := errors.New("stop signal")
	svr, _ := NewTestServer(WithExitSignal(func() <-chan error {
		return stopSignal
	}))
	err := svr.RegisterService(mocks.ServiceInfo(), mocks.MyServiceHandler())
	test.Assert(t, err == nil, err)
	time.AfterFunc(100*time.Millisecond, func() {
		stopSignal <- stopErr
	})
	err = svr.Run()
	test.Assert(t, err == stopErr, err)
}
