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

import (
	"bytes"
	"context"
	"testing"
	"time"

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

func TestEncoding(t *testing.T) {
	testBytes := []byte("testbytes")
	encodingStr := encodeBinHeader(testBytes)
	decodingBytes, err := decodeBinHeader(encodingStr)
	test.Assert(t, err == nil, err)
	test.Assert(t, string(decodingBytes) == string(testBytes))

	testKey, testValue := "key-bin", "value"
	encodeValue := encodeMetadataHeader(testKey, testValue)
	decodeValue, err := decodeMetadataHeader(testKey, encodeValue)
	test.Assert(t, err == nil, err)
	test.Assert(t, decodeValue == testValue)

	testEncoding := "%testcoding"
	encodedTestStr := encodeGrpcMessage(testEncoding)
	decodedTestStr := decodeGrpcMessage(encodedTestStr)
	test.Assert(t, decodedTestStr == testEncoding)
}

func TestEncodeTimeout(t *testing.T) {
	_, err := decodeTimeout("")
	test.Assert(t, err != nil, err)

	_, err = decodeTimeout("#####")
	test.Assert(t, err != nil, err)

	_, err = decodeTimeout("1234567890123")
	test.Assert(t, err != nil, err)

	to := encodeTimeout(-1)
	test.Assert(t, to == "0n")
	d, err := decodeTimeout(to)
	test.Assert(t, err == nil, err)
	test.Assert(t, d == 0)

	to = encodeTimeout(time.Nanosecond)
	test.Assert(t, to == "1n")
	d, err = decodeTimeout(to)
	test.Assert(t, err == nil, err)
	test.Assert(t, d == time.Nanosecond)

	to = encodeTimeout(time.Microsecond)
	test.Assert(t, to == "1000n")
	d, err = decodeTimeout(to)
	test.Assert(t, err == nil, err)
	test.Assert(t, d == time.Microsecond)

	to = encodeTimeout(time.Millisecond)
	test.Assert(t, to == "1000000n")
	d, err = decodeTimeout(to)
	test.Assert(t, err == nil, err)
	test.Assert(t, d == time.Millisecond)

	to = encodeTimeout(time.Second)
	test.Assert(t, to == "1000000u")
	d, err = decodeTimeout(to)
	test.Assert(t, err == nil, err)
	test.Assert(t, d == time.Second)

	to = encodeTimeout(time.Minute)
	test.Assert(t, to == "60000000u")
	d, err = decodeTimeout(to)
	test.Assert(t, err == nil, err)
	test.Assert(t, d == time.Minute)

	to = encodeTimeout(time.Hour)
	test.Assert(t, to == "3600000m")
	d, err = decodeTimeout(to)
	test.Assert(t, err == nil, err)
	test.Assert(t, d == time.Hour)

	to = encodeTimeout(time.Minute * 1000000)
	test.Assert(t, to == "60000000S")
	d, err = decodeTimeout(to)
	test.Assert(t, err == nil, err)
	test.Assert(t, d == time.Minute*1000000)

	to = encodeTimeout(time.Hour * 1000000)
	test.Assert(t, to == "60000000M")
	d, err = decodeTimeout(to)
	test.Assert(t, err == nil, err)
	test.Assert(t, d == time.Hour*1000000)

	to = encodeTimeout(time.Hour * 2000000)
	test.Assert(t, to == "2000000H")
	d, err = decodeTimeout(to)
	test.Assert(t, err == nil, err)
	test.Assert(t, d == time.Hour*2000000)
}

func TestTimeUnit(t *testing.T) {
	d, ok := timeoutUnitToDuration(timeoutUnit('H'))
	test.Assert(t, ok)
	test.Assert(t, d == time.Hour)

	d, ok = timeoutUnitToDuration(timeoutUnit('M'))
	test.Assert(t, ok)
	test.Assert(t, d == time.Minute)

	d, ok = timeoutUnitToDuration(timeoutUnit('S'))
	test.Assert(t, ok)
	test.Assert(t, d == time.Second)

	d, ok = timeoutUnitToDuration(timeoutUnit('m'))
	test.Assert(t, ok)
	test.Assert(t, d == time.Millisecond)

	d, ok = timeoutUnitToDuration(timeoutUnit('u'))
	test.Assert(t, ok)
	test.Assert(t, d == time.Microsecond)

	d, ok = timeoutUnitToDuration(timeoutUnit('n'))
	test.Assert(t, ok)
	test.Assert(t, d == time.Nanosecond)

	_, ok = timeoutUnitToDuration(timeoutUnit('x'))
	test.Assert(t, !ok)
}

func TestBufferWriter(t *testing.T) {
	bytes := []byte("hello")
	br := newBufWriter(newMockNpConn(mockAddr0), len(bytes)*5)
	for i := 0; i < 6; i++ {
		_, err := br.Write(bytes)
		test.Assert(t, err == nil, err)
	}
	err := br.Flush()
	test.Assert(t, err == nil, err)
}

func TestBufferPool(t *testing.T) {
	pool := newBufferPool()
	buffer := pool.get()
	pool.put(buffer)
}

func TestRecvBufferReader(t *testing.T) {
	testBytes := []byte("hello")
	buffer := new(bytes.Buffer)
	buffer.Reset()
	buffer.Write(testBytes)
	recvBuffer := newRecvBuffer()
	recvBuffer.put(recvMsg{buffer: buffer})
	ctx := context.Background()
	rbReader := &recvBufferReader{
		ctx:     ctx,
		ctxDone: ctx.Done(),
		recv:    recvBuffer,
		closeStream: func(err error) {
		},
		freeBuffer: func(buffer *bytes.Buffer) {
			buffer.Reset()
		},
	}

	n, err := rbReader.Read(testBytes)
	test.Assert(t, err == nil, err)
	test.Assert(t, n == len(testBytes))

	buffer.Write(testBytes)
	recvBuffer.put(recvMsg{buffer: buffer})
	rbReader.closeStream = nil
	n, err = rbReader.Read(testBytes)
	test.Assert(t, err == nil, err)
	test.Assert(t, n == len(testBytes))
}

func TestConnectionError(t *testing.T) {
	connectionError := ConnectionError{
		Desc: "err desc",
		temp: true,
		err:  nil,
	}

	errStr := connectionError.Error()
	test.Assert(t, errStr == "connection error: desc = \"err desc\"")

	temp := connectionError.Temporary()
	test.Assert(t, temp)

	ori := connectionError.Origin()
	test.Assert(t, ori == connectionError)

	connectionError.err = context.Canceled
	ori = connectionError.Origin()
	test.Assert(t, ori == context.Canceled)

	test.Assert(t, connectionError.Code() == 14)
}
