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

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

	"github.com/cloudwego/kitex/internal/test"
	"github.com/cloudwego/kitex/pkg/kerrors"
	"github.com/cloudwego/kitex/pkg/rpcinfo"
	"github.com/cloudwego/kitex/pkg/stats"
	"github.com/cloudwego/kitex/pkg/utils"
	"github.com/cloudwego/kitex/transport"
)

func TestAsTaggable(t *testing.T) {
	test.Assert(t, rpcinfo.AsTaggable(nil) == nil)
	test.Assert(t, rpcinfo.AsTaggable(0) == nil)
	test.Assert(t, rpcinfo.AsTaggable(0.1) == nil)
	test.Assert(t, rpcinfo.AsTaggable(rpcinfo.NewEndpointInfo("s", "m", nil, nil)) != nil)
	test.Assert(t, rpcinfo.AsTaggable(rpcinfo.NewMutableEndpointInfo("s", "m", nil, nil)) != nil)
}

func TestAsMutableEndpointInfo(t *testing.T) {
	test.Assert(t, rpcinfo.AsMutableEndpointInfo(rpcinfo.EndpointInfo(nil)) == nil)

	na := utils.NewNetAddr("nnn", "aaa")
	ts := map[string]string{"a": "b", "1": "2"}
	ei := rpcinfo.NewEndpointInfo("s", "m", na, ts)

	mi := rpcinfo.AsMutableEndpointInfo(ei)
	test.Assert(t, mi != nil)

	mi.SetServiceName("ss")
	mi.SetMethod("mm")
	mi.SetAddress(utils.NewNetAddr("n2", "a2"))
	mi.SetTag("a", "aa")
	mi.SetTag("x", "y")

	test.Assert(t, ei.ServiceName() == "ss")
	test.Assert(t, ei.Method() == "mm")
	test.Assert(t, ei.Address().Network() == "n2")
	test.Assert(t, ei.Address().String() == "a2")
	test.Assert(t, ei.DefaultTag("a", "-") == "aa")
	test.Assert(t, ei.DefaultTag("1", "-") == "2")
	test.Assert(t, ei.DefaultTag("x", "-") == "y")

	tag, ok := ei.Tag("a")
	test.Assert(t, ok && tag == "aa")
	tag, ok = ei.Tag("1")
	test.Assert(t, ok && tag == "2")
	tag, ok = ei.Tag("x")
	test.Assert(t, ok && tag == "y")

	ei = rpcinfo.NewEndpointInfo("s2", "m2", nil, nil)
	mi = rpcinfo.AsMutableEndpointInfo(ei)
	test.Assert(t, ei.Address() == nil)
	test.Assert(t, ei.DefaultTag("x", "-") == "-")
	mi.SetTag("x", "y")
	test.Assert(t, ei.DefaultTag("x", "-") == "y")
}

func TestAsMutableRPCStats(t *testing.T) {
	test.Assert(t, rpcinfo.AsMutableRPCStats(rpcinfo.RPCStats(nil)) == nil)
	test.Assert(t, rpcinfo.AsMutableRPCStats(new(MockRPCStats)) == nil)

	st := rpcinfo.NewRPCStats()
	ms := rpcinfo.AsMutableRPCStats(st)
	test.Assert(t, ms != nil)
	test.Assert(t, ms.ImmutableView() != nil)

	ms.SetSendSize(123)
	ms.SetRecvSize(456)
	test.Assert(t, st.SendSize() == 123 && st.RecvSize() == 456)

	ms.SetError(errors.New("hello"))
	test.Assert(t, st.Error() != nil && st.Error().Error() == "hello")

	ms.SetPanicked("don't panic")
	ok, what := st.Panicked()
	test.Assert(t, ok && what.(string) == "don't panic")

	ms.SetLevel(stats.LevelDetailed)
	st.Record(context.Background(), stats.RPCStart, stats.StatusInfo, "informatic")
	e := st.GetEvent(stats.RPCStart)
	test.Assert(t, e != nil && !e.IsNil())
	test.Assert(t, e.Event() == stats.RPCStart && e.Status() == stats.StatusInfo && e.Info() == "informatic")

	ms.Reset()
	test.Assert(t, st.Level() == stats.LevelDisabled)
	test.Assert(t, st.SendSize() == 0 && st.RecvSize() == 0)
	test.Assert(t, st.Error() == nil)
	ok, what = st.Panicked()
	test.Assert(t, !ok && what == nil)
	test.Assert(t, st.GetEvent(stats.RPCStart) == nil)
}

func TestAsMutableRPCConfig(t *testing.T) {
	test.Assert(t, rpcinfo.AsMutableRPCConfig(rpcinfo.RPCConfig(nil)) == nil)
	test.Assert(t, rpcinfo.AsMutableRPCConfig(new(MockRPCConfig)) == nil)

	c1 := rpcinfo.NewRPCConfig()
	mc := rpcinfo.AsMutableRPCConfig(c1)
	test.Assert(t, mc != nil)
	test.Assert(t, !mc.IsRPCTimeoutLocked())
	test.Assert(t, !mc.IsConnectTimeoutLocked())
	test.Assert(t, !mc.IsReadWriteTimeoutLocked())

	test.Assert(t, mc.SetRPCTimeout(time.Hour) == nil)
	test.Assert(t, mc.SetConnectTimeout(time.Hour*2) == nil)
	test.Assert(t, mc.SetReadWriteTimeout(time.Hour*3) == nil)
	test.Assert(t, mc.SetIOBufferSize(9999) == nil)
	test.Assert(t, mc.SetTransportProtocol(transport.HTTP) == nil)
	test.Assert(t, mc.SetInteractionMode(rpcinfo.PingPong) == nil)

	test.Assert(t, c1.RPCTimeout() == time.Hour)
	test.Assert(t, c1.ConnectTimeout() == time.Hour*2)
	test.Assert(t, c1.ReadWriteTimeout() == time.Hour*3)
	test.Assert(t, c1.IOBufferSize() == 9999)
	test.Assert(t, c1.TransportProtocol()&transport.HTTP == transport.HTTP)
	test.Assert(t, c1.InteractionMode() == rpcinfo.PingPong)

	mc.LockConfig(rpcinfo.BitRPCTimeout)
	test.Assert(t, mc.IsRPCTimeoutLocked())
	err := mc.SetRPCTimeout(time.Hour * 4)
	test.Assert(t, errors.Is(err, kerrors.ErrNotSupported))

	mc.LockConfig(rpcinfo.BitConnectTimeout)
	test.Assert(t, mc.IsConnectTimeoutLocked())
	err = mc.SetConnectTimeout(time.Hour * 4)
	test.Assert(t, errors.Is(err, kerrors.ErrNotSupported))

	mc.LockConfig(rpcinfo.BitReadWriteTimeout)
	test.Assert(t, mc.IsReadWriteTimeoutLocked())
	err = mc.SetReadWriteTimeout(time.Hour * 4)
	test.Assert(t, errors.Is(err, kerrors.ErrNotSupported))

	mc.LockConfig(rpcinfo.BitIOBufferSize)
	err = mc.SetIOBufferSize(8888)
	test.Assert(t, errors.Is(err, kerrors.ErrNotSupported))

	mc2 := mc.Clone()
	test.Assert(t, mc2 != nil)

	test.Assert(t, mc.ImmutableView() != nil)
}
