// Copyright 2022 PingCAP, Inc.
//
// 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,
// See the License for the specific language governing permissions and
// limitations under the License.

package utils

import (
	"fmt"
	"testing"

	"github.com/pingcap/errcode"
	"github.com/pingcap/errors"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestAdjustedPager(t *testing.T) {
	type args struct {
		p *Pager
	}
	tests := []struct {
		name string
		args args
		want *Pager
	}{
		{
			"invalid page",
			args{&Pager{Page: 0, PageSize: defaultPageSize}},
			&Pager{Page: 1, PageSize: defaultPageSize, Offset: 0},
		},
		{
			"too large page",
			args{&Pager{Page: maxPage + 100, PageSize: defaultPageSize}},
			&Pager{Page: maxPage, PageSize: defaultPageSize, Offset: 9999990},
		},
		{
			"invalid page size",
			args{&Pager{Page: 1, PageSize: 0}},
			&Pager{Page: 1, PageSize: defaultPageSize, Offset: 0},
		},
		{
			"too large size",
			args{&Pager{Page: 1, PageSize: maxPageSize + 100}},
			&Pager{Page: 1, PageSize: maxPageSize, Offset: 0},
		},
	}
	for _, tt := range tests {
		pager := tt.args.p
		out := AdjustedPager(&pager.Page, &pager.PageSize)
		require.Equal(t, tt.want.Page, out.Page, tt.name)
		require.Equal(t, tt.want.PageSize, out.PageSize, tt.name)
		require.Equal(t, tt.want.Offset, out.Offset, tt.name)
	}

	pager := AdjustedPager(nil, nil)
	require.Equal(t, int32(1), pager.Page)
	require.Equal(t, int32(defaultPageSize), pager.PageSize)
	require.Equal(t, int32(0), pager.Offset)
	var v int32 = 10
	pager = AdjustedPager(nil, &v)
	require.Equal(t, int32(1), pager.Page)
	require.Equal(t, v, pager.PageSize)
	require.Equal(t, int32(0), pager.Offset)
	pager = AdjustedPager(&v, nil)
	require.Equal(t, v, pager.Page)
	require.Equal(t, int32(defaultPageSize), pager.PageSize)
	require.Equal(t, int32(90), pager.Offset)
}

func TestWrapErr(t *testing.T) {
	serverErrMSG := "server error"

	type test struct {
		input error
		msg   string
	}

	tests := []test{
		{
			input: errcode.NewInvalidInputErr(fmt.Errorf("bad request")),
			msg:   "bad request",
		},
		{
			input: errors.New("what ever"),
			msg:   serverErrMSG,
		},
	}

	for _, tc := range tests {
		err := WrapAsErrorCodeErr(tc.input, serverErrMSG)
		require.Equal(t, tc.msg, err.Error())
	}
}

func TestCheckTLSParam(t *testing.T) {
	require.Error(t, CheckTLSParam("a", ""))
	require.Error(t, CheckTLSParam("", "a"))
	require.NoError(t, CheckTLSParam("", ""))
	require.NoError(t, CheckTLSParam("a", "a"))
}

func TestUnpackFullTableName(t *testing.T) {
	testCases := []struct {
		input             string
		isErr             bool
		expectedDBName    string
		expectedTableName string
	}{
		{input: "`", isErr: true},
		{input: "``", isErr: true},
		{input: "```", isErr: true},
		{input: "````", isErr: true},
		{input: "`````", isErr: true},
		{input: "``.``", isErr: true},
		{input: "`a`.``", isErr: true},
		{input: "``.`a`", isErr: true},
		{input: "`a`,`a`", isErr: true},
		{input: "`a`.`a`", isErr: false, expectedDBName: "a", expectedTableName: "a"},
		{input: "`a`.`a``", isErr: true},
		{input: "`a`.``a`", isErr: true},
		{input: "`a``.`a`", isErr: true},
		{input: "``a`.`a`", isErr: true},
	}

	for _, tc := range testCases {
		actualDBName, actualTableName, err := UnpackFullTableName(tc.input)
		require.Equal(t, tc.isErr, err != nil)
		if !tc.isErr {
			require.Equal(t, tc.expectedDBName, actualDBName)
			require.Equal(t, tc.expectedTableName, actualTableName)
		}
	}
}

func TestRestoreSpecialChars(t *testing.T) {
	cases := map[string]string{
		`\b`:     "\b",
		`\t`:     "\t",
		`\n`:     "\n",
		`\f`:     "\f",
		`\r`:     "\r",
		`\"`:     "\"",
		`\\`:     "\\",
		`\a`:     "",
		`\u0000`: "\u0000",
		`\u0001`: "\u0001",
		`hahah`:  "hahah",
	}

	for k, v := range cases {
		result, err := RestoreSpecialChars(k)
		if len(v) > 0 {
			require.NoErrorf(t, err, "input string: %s", k)
		} else {
			require.Errorf(t, err, "input string: %s", k)
		}
		assert.Equal(t, v, result)
	}
}
