/*
 * Copyright 2022 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 gonet

import (
	"context"
	"errors"
	"net"
	"testing"

	"github.com/cloudwego/kitex/internal/mocks"
	mocksremote "github.com/cloudwego/kitex/internal/mocks/remote"
	"github.com/cloudwego/kitex/internal/test"
	"github.com/cloudwego/kitex/pkg/remote"
	"github.com/cloudwego/kitex/pkg/rpcinfo"
)

// TestOnActive test server_handler OnActive success
func TestOnActive(t *testing.T) {
	conn := newMockNetConn(mocks.Conn{RemoteAddrFunc: func() (r net.Addr) {
		return addr
	}})
	pl := remote.NewTransPipeline(svrTransHdlr)
	svrTransHdlr.SetPipeline(pl)
	if setter, ok := svrTransHdlr.(remote.InvokeHandleFuncSetter); ok {
		setter.SetInvokeHandleFunc(func(ctx context.Context, req, resp interface{}) (err error) {
			return nil
		})
	}

	ctx := context.Background()
	_, err := svrTransHdlr.OnActive(ctx, conn)
	test.Assert(t, err == nil, err)
}

// TestOnRead test server_handler OnRead success
func TestOnRead(t *testing.T) {
	// 1. prepare mock data
	// var isWriteBufFlushed bool
	// var isReaderBufReleased bool
	var isInvoked bool
	conn := newMockNetConn(mocks.Conn{RemoteAddrFunc: func() (r net.Addr) {
		return addr
	}})

	if setter, ok := svrTransHdlr.(remote.InvokeHandleFuncSetter); ok {
		setter.SetInvokeHandleFunc(func(ctx context.Context, req, resp interface{}) (err error) {
			isInvoked = true
			return nil
		})
	}
	remote.NewTransPipeline(svrTransHdlr)

	// 2. test
	ctx := context.Background()
	ctx, err := svrTransHdlr.OnActive(ctx, conn)
	test.Assert(t, err == nil, err)

	err = svrTransHdlr.OnRead(ctx, conn)
	test.Assert(t, err == nil, err)
	test.Assert(t, isInvoked)
}

// TestInvokeErr test server_handler invoke err
func TestInvokeErr(t *testing.T) {
	// 1. prepare mock data
	var isInvoked bool
	conn := newMockNetConn(mocks.Conn{RemoteAddrFunc: func() (r net.Addr) {
		return addr
	}})
	conn.IsActiveFunc = func() (r bool) {
		return true
	}
	remote.NewTransPipeline(svrTransHdlr)

	// mock invoke err
	if setter, ok := svrTransHdlr.(remote.InvokeHandleFuncSetter); ok {
		setter.SetInvokeHandleFunc(func(ctx context.Context, req, resp interface{}) (err error) {
			isInvoked = true
			return errors.New("mock invoke test")
		})
	}

	// 2. test
	ctx := context.Background()
	ctx, err := svrTransHdlr.OnActive(ctx, conn)
	test.Assert(t, err == nil, err)

	err = svrTransHdlr.OnRead(ctx, conn)
	test.Assert(t, err == nil, err)
	test.Assert(t, isInvoked)
}

// TestPanicAfterRead test server_handler not panic after read
func TestPanicAfterRead(t *testing.T) {
	// 1. prepare mock data
	var isInvoked bool
	var isClosed bool
	conn := newMockNetConn(mocks.Conn{
		RemoteAddrFunc: func() (r net.Addr) {
			return addr
		},
		CloseFunc: func() (e error) {
			isClosed = true
			return nil
		},
	})
	conn.IsActiveFunc = func() (r bool) {
		return true
	}

	// pipeline nil panic
	svrTransHdlr.SetPipeline(nil)

	// 2. test
	ctx := context.Background()
	ctx, err := svrTransHdlr.OnActive(ctx, conn)
	test.Assert(t, err == nil, err)

	err = svrTransHdlr.OnRead(ctx, conn)
	test.Assert(t, err != nil, err)
	test.Assert(t, !isInvoked)
	test.Assert(t, !isClosed)
}

// TestNoMethodInfo test server_handler without method info success
func TestNoMethodInfo(t *testing.T) {
	// 1. prepare mock data
	var isClosed bool
	conn := newMockNetConn(mocks.Conn{
		RemoteAddrFunc: func() (r net.Addr) {
			return addr
		},
		CloseFunc: func() (e error) {
			isClosed = true
			return nil
		},
	})
	conn.IsActiveFunc = func() (r bool) {
		return true
	}

	svrOpt := &remote.ServerOption{
		InitOrResetRPCInfoFunc: func(ri rpcinfo.RPCInfo, addr net.Addr) rpcinfo.RPCInfo {
			fromInfo := rpcinfo.EmptyEndpointInfo()
			toInfo := rpcinfo.EmptyEndpointInfo()
			rpcCfg := rpcinfo.NewRPCConfig()
			ink := rpcinfo.NewInvocation("", method)
			rpcStat := rpcinfo.NewRPCStats()
			return rpcinfo.NewRPCInfo(fromInfo, toInfo, ink, rpcCfg, rpcStat)
		},
		Codec: &MockCodec{
			EncodeFunc: nil,
			DecodeFunc: func(ctx context.Context, msg remote.Message, in remote.ByteBuffer) error {
				return remote.NewTransError(remote.UnknownMethod, errors.New("unknown method"))
			},
		},
		SvcSearcher: mocksremote.NewDefaultSvcSearcher(),
		TracerCtl:   &rpcinfo.TraceController{},
	}
	svrTransHdlr, _ := newSvrTransHandler(svrOpt)

	remote.NewTransPipeline(svrTransHdlr)

	// 2. test
	ctx := context.Background()
	ctx, err := svrTransHdlr.OnActive(ctx, conn)
	test.Assert(t, err == nil, err)

	err = svrTransHdlr.OnRead(ctx, conn)
	test.Assert(t, err != nil, err)
	test.Assert(t, !isClosed)
}
