package errcode

import (
	"context"
	"errors"
	"fmt"
	"testing"

	. "github.com/onsi/gomega"
	"github.com/pingcap/errcode"
	"github.com/tidbcloud/pkgv2/bizerr"
	serviceerrorcode "github.com/tidbcloud/serverless-service/internal/errorcode"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/metadata"
	"google.golang.org/grpc/status"
)

type testClusterSQLError struct {
	bizerr.BizError
	errno uint16
	msg   string
}

func (e *testClusterSQLError) Error() string {
	return fmt.Sprintf("SQL error %d: %s", e.errno, e.msg)
}

func (e *testClusterSQLError) ClusterSQLError() (uint16, string) {
	return e.errno, e.msg
}

func TestErrorConvert(t *testing.T) {
	g := NewGomegaWithT(t)
	ctx := context.Background()

	{
		err := errors.New("raw error")
		g.Expect(errorConvert(ctx, err)).To(Equal(err))
	}
	{
		err := status.Error(codes.Aborted, "status error")
		g.Expect(errorConvert(ctx, err)).To(Equal(err))
	}
	{
		err := errcode.NewInternalErr(errors.New("errorcode"))
		expectedErr := status.Error(codes.Internal, "errorcode")
		g.Expect(errorConvert(ctx, err)).To(Equal(expectedErr))
	}
	{
		err := bizerr.New(bizerr.ErrCodeGRPC, "bizerr")
		g.Expect(errorConvert(ctx, err)).To(Equal(err))
	}
	{
		ctx := metadata.NewIncomingContext(ctx, metadata.Pairs("x-http-uri", "/api/internal/xxx"))
		err := bizerr.New(bizerr.ErrCodeGRPC, "bizerr")
		expectedErr := status.Error(codes.Internal, "Internal Error")
		g.Expect(errorConvert(ctx, err)).To(Equal(expectedErr))
	}
	{
		ctx := metadata.NewIncomingContext(ctx, metadata.Pairs("x-http-uri", "/v1beta1/xxx"))
		err := fmt.Errorf("wrapped: %w", bizerr.New(bizerr.ErrCodeParams, "invalid input"))
		expectedErr := status.Error(codes.InvalidArgument, "invalid input")
		g.Expect(errorConvert(ctx, err)).To(Equal(expectedErr))
	}
	{
		ctx := metadata.NewIncomingContext(ctx, metadata.Pairs("x-http-uri", "/v1beta1/xxx"))
		sqlErr := &testClusterSQLError{
			BizError: bizerr.New(serviceerrorcode.ErrCodeSvcClusterProxy, "cluster SQL error"),
			errno:    1146,
			msg:      "Table 'private.orders' doesn't exist",
		}
		err := serviceerrorcode.MarkClusterSQLErrorUserActionable(sqlErr)
		expectedErr := status.Error(codes.InvalidArgument, "The requested table does not exist. (error 1146)")
		g.Expect(errorConvert(ctx, err)).To(Equal(expectedErr))
	}
	{
		ctx := metadata.NewIncomingContext(ctx, metadata.Pairs("x-http-uri", "/v1beta1/xxx"))
		err := &testClusterSQLError{errno: 1045, msg: "Access denied for user 'secret'@'10.0.0.1'"}
		expectedErr := status.Error(
			codes.Internal,
			"Failed to access the TiDB cluster (error 1045). Please retry; if the problem persists, contact support.",
		)
		g.Expect(errorConvert(ctx, err)).To(Equal(expectedErr))
		g.Expect(status.Convert(errorConvert(ctx, err)).Message()).NotTo(ContainSubstring("secret"))
	}
	g.Expect(errorConvert(ctx, nil)).To(BeNil())
}
