package grpc_service

import (
	"context"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/tidbcloud/config-center/internal/db"
	"github.com/tidbcloud/config-center/internal/service"
	pb "github.com/tidbcloud/config-center/proto/config_center"
	"google.golang.org/protobuf/types/known/structpb"
)

func TestCreateAndGetTemplate(t *testing.T) {
	assert := assert.New(t)
	mockDB := db.NewMockDB()
	c := &ConfigCenterService{
		DB: mockDB,
		UnifiedService: &service.ConfigCenterService{
			DB: mockDB,
		},
	}
	template, err := c.CreateConfigurationTemplate(context.TODO(), &pb.CreateConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb",
		TemplateName:      "template name",
		Data: map[string]*pb.ParamItem{
			"test": {
				Value:   &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "test"}},
				Comment: "test",
			},
		},
	})

	assert.Nil(err)
	// Check if the template is created
	assert.NotNil(t, template)
	assert.Equal("template name", template.TemplateName)
	assert.Equal("tidb", template.ConfigurationType)

	templateG, err := c.GetConfigurationTemplate(context.TODO(), &pb.GetConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb",
		TemplateId:        template.TemplateId,
	})

	assert.Nil(err)
	assert.NotNil(templateG)
	assert.Equal(template.TemplateId, templateG.TemplateId)
	assert.Equal(template.TemplateName, templateG.TemplateName)
	assert.Equal(template.ConfigurationType, templateG.ConfigurationType)
}

func TestDefaultTemplate(t *testing.T) {
	assert := assert.New(t)
	mockDB := db.NewMockDB()
	c := &ConfigCenterService{
		DB: mockDB,
		UnifiedService: &service.ConfigCenterService{
			DB: mockDB,
		},
	}
	template, err := c.CreateConfigurationTemplate(context.TODO(), &pb.CreateConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb",
		TemplateName:      "template1",
		Data: map[string]*pb.ParamItem{
			"test": {
				Value:   &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "test"}},
				Comment: "test",
			},
		},
	})

	assert.Nil(err)
	assert.NotNil(template)

	template2, err := c.CreateConfigurationTemplate(context.TODO(), &pb.CreateConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb",
		TemplateName:      "template2",
		Data: map[string]*pb.ParamItem{
			"test": {
				Value:   &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "test"}},
				Comment: "test",
			},
		},
	})

	assert.Nil(err)
	assert.NotNil(template2)

	_, err = c.UpdateDefaultConfigurationTemplate(context.TODO(), &pb.UpdateDefaultConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb",
		Version:           "v7.5.1",
		CloudProvider:     "aws",
		NodeInstance:      "m5.xlarge",
		TemplateId:        template.TemplateId,
	})
	assert.Nil(err)

	_, err = c.UpdateDefaultConfigurationTemplate(context.TODO(), &pb.UpdateDefaultConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb",
		Version:           "v7.5.1",
		CloudProvider:     "",
		NodeInstance:      "",
		TemplateId:        template2.TemplateId,
	})
	assert.Nil(err)

	st, err := c.SearchDefaultConfigurationTemplate(context.TODO(), &pb.SearchDefaultConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb",
		Version:           "v7.5.1",
		CloudProvider:     "aws",
		NodeInstance:      "m5.xlarge",
	})
	assert.Nil(err)
	assert.NotNil(st)
	assert.Equal(template.TemplateId, st.TemplateId)

	st2, err := c.SearchDefaultConfigurationTemplate(context.TODO(), &pb.SearchDefaultConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb",
		Version:           "v7.5.1",
		CloudProvider:     "aws",
		NodeInstance:      "",
	})
	assert.Nil(err)
	assert.NotNil(st)
	assert.Equal(template2.TemplateId, st2.TemplateId)

	st3, err := c.SearchDefaultConfigurationTemplate(context.TODO(), &pb.SearchDefaultConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb",
		Version:           "v7.5.1",
		CloudProvider:     "",
		NodeInstance:      "",
	})
	assert.Nil(err)
	assert.NotNil(st)
	assert.Equal(template2.TemplateId, st3.TemplateId)
}

// TestCreateTemplateExplicitIDBypassesDedup verifies that when an explicit
// template_id is provided (the global->regional sync path), content-hash dedup is
// skipped so the requested ID is honored. Without this, the region would collapse
// the template onto a different existing ID and any default-template binding
// referencing the requested ID would 404.
func TestCreateTemplateExplicitIDBypassesDedup(t *testing.T) {
	assert := assert.New(t)
	mockDB := db.NewMockDB()
	c := &ConfigCenterService{
		DB: mockDB,
		UnifiedService: &service.ConfigCenterService{
			DB: mockDB,
		},
	}

	data := map[string]*pb.ParamItem{
		"test": {
			Value:   &structpb.Value{Kind: &structpb.Value_StringValue{StringValue: "test"}},
			Comment: "test",
		},
	}

	// First template, ID auto-assigned.
	first, err := c.CreateConfigurationTemplate(context.TODO(), &pb.CreateConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb",
		TemplateName:      "same-name",
		Data:              data,
	})
	assert.Nil(err)
	assert.NotNil(first)

	// Same content/name/tags but no explicit ID -> dedup reuses the first template.
	deduped, err := c.CreateConfigurationTemplate(context.TODO(), &pb.CreateConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb",
		TemplateName:      "same-name",
		Data:              data,
	})
	assert.Nil(err)
	assert.Equal(first.TemplateId, deduped.TemplateId, "dedup should still reuse when no explicit id is given")

	// Same content/name/tags but with an explicit ID -> dedup is bypassed and the
	// requested ID is honored.
	explicitID := int64(9999)
	withID, err := c.CreateConfigurationTemplate(context.TODO(), &pb.CreateConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb",
		TemplateName:      "same-name",
		TemplateId:        &explicitID,
		Data:              data,
	})
	assert.Nil(err)
	assert.Equal(explicitID, withID.TemplateId, "explicit id must be honored, not deduped onto the existing template")

	// The explicit ID is now retrievable on its own (i.e. a binding referencing it resolves).
	got, err := c.GetConfigurationTemplate(context.TODO(), &pb.GetConfigurationTemplateRequest{
		ClusterType:       pb.ClusterType_DEDICATED_TIDB,
		ConfigurationType: "tidb",
		TemplateId:        explicitID,
	})
	assert.Nil(err)
	assert.Equal(explicitID, got.TemplateId)
}
