package region

import (
	"context"
	"testing"

	. "github.com/onsi/gomega"
	"github.com/samber/lo"
	"go.uber.org/mock/gomock"
	"gorm.io/gorm"

	globalcommonv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/common/v1"
	globalngv1 "github.com/tidbcloud/tidb-management-service/api/spec/global/nextgen/v1"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/entity"
	resourcepoolrepo "github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/resourcepool"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/domain/repository/resourcepool/mock_resourcepool"
	regionrepoimpl "github.com/tidbcloud/tidb-management-service/nextgen-global-service/infra/repoimpl/region"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/infra/repoimpl/schema"
	"github.com/tidbcloud/tidb-management-service/nextgen-global-service/pkg/testdata"
	"github.com/tidbcloud/tidb-management-service/utilities/bizerr"
)

func TestListRegionCapacities_ExcludesInactiveRegions(t *testing.T) {
	g := NewWithT(t)
	dbPvd := *dbProvider.Load()
	regionRepo := regionrepoimpl.NewRegionRepo(dbPvd)

	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	mockResourcePoolRepo := mock_resourcepool.NewMockRepo(ctrl)

	svc := NewService(regionRepo, nil, nil, mockResourcePoolRepo, nil, dbPvd, nil, nil, nil, nil)

	// Insert an inactive BYOC region associated with the test org
	inactiveRegionID := uint(80001)
	inactiveRegion := &schema.Region{
		Model:       gorm.Model{ID: inactiveRegionID},
		Name:        "us-west-2-inactive-byoc",
		RegionName:  "us-west-2",
		ProviderId:  5, // AWS BYOC provider (ID 5 in testdata)
		DisplayName: "US West 2 Inactive BYOC",
		Status:      "inactive",
		IsPublic:    lo.ToPtr(false),
		ServicePlan: "BYOC",
	}
	dbPvd.WriteDB(t.Context()).Create(inactiveRegion)
	defer dbPvd.WriteDB(t.Context()).Unscoped().Delete(inactiveRegion)

	dbPvd.WriteDB(t.Context()).Create(&schema.RegionTenant{
		RegionId: uint64(inactiveRegionID),
		TenantId: testdata.TestOrgID,
	})
	defer dbPvd.WriteDB(t.Context()).Where("region_id = ? AND tenant_id = ?", inactiveRegionID, testdata.TestOrgID).
		Delete(&schema.RegionTenant{})

	// Mock resource pool query to return empty results for active regions
	mockResourcePoolRepo.EXPECT().
		Query(gomock.Any(), gomock.Any()).
		Return([]*entity.ResourcePool{}, int64(0), nil).
		AnyTimes()

	resp, err := svc.ListRegionCapacities(t.Context(), &globalngv1.ListRegionCapacitiesRequest{
		OrganizationId: testdata.TestOrgID,
		ServicePlan:    globalcommonv1.ServicePlan_BYOC,
	})
	g.Expect(err).Should(BeNil())

	// The inactive region should NOT appear in the response
	for _, capacity := range resp.Capacities {
		g.Expect(capacity.Region.GetLogicalName()).ShouldNot(Equal("us-west-2-inactive-byoc"),
			"inactive region should not be returned by ListRegionCapacities")
	}
}

func TestListRegionCapacities_ResourcePoolQueryUsesRegionName(t *testing.T) {
	g := NewWithT(t)
	dbPvd := *dbProvider.Load()
	regionRepo := regionrepoimpl.NewRegionRepo(dbPvd)

	ctrl := gomock.NewController(t)
	defer ctrl.Finish()

	mockResourcePoolRepo := mock_resourcepool.NewMockRepo(ctrl)

	svc := NewService(regionRepo, nil, nil, mockResourcePoolRepo, nil, dbPvd, nil, nil, nil, nil)

	// Capture the filter passed to the resource pool query
	var capturedFilter *resourcepoolrepo.Filter
	mockResourcePoolRepo.EXPECT().
		Query(gomock.Any(), gomock.Any()).
		DoAndReturn(func(_ context.Context, filter *resourcepoolrepo.Filter) ([]*entity.ResourcePool, int64, bizerr.BizError) {
			capturedFilter = filter
			return []*entity.ResourcePool{}, int64(0), nil
		}).
		AnyTimes()

	// Region ID 5 in testdata is GCP us-east1 BYOC region, tenant=testdata.TestOrgID
	resp, err := svc.ListRegionCapacities(t.Context(), &globalngv1.ListRegionCapacitiesRequest{
		OrganizationId: testdata.TestOrgID,
		ServicePlan:    globalcommonv1.ServicePlan_BYOC,
	})
	g.Expect(err).Should(BeNil())
	g.Expect(resp).ShouldNot(BeNil())

	// Verify that resource pool query was called with the region name filter set
	if capturedFilter != nil {
		g.Expect(capturedFilter.RegionNames).ShouldNot(BeEmpty(),
			"resource pool query must filter by region name")
	}
}

func TestListRegionCapacities_OnlyBYOCAllowed(t *testing.T) {
	g := NewWithT(t)
	dbPvd := *dbProvider.Load()
	regionRepo := regionrepoimpl.NewRegionRepo(dbPvd)

	svc := NewService(regionRepo, nil, nil, nil, nil, dbPvd, nil, nil, nil, nil)

	_, err := svc.ListRegionCapacities(t.Context(), &globalngv1.ListRegionCapacitiesRequest{
		OrganizationId: testdata.TestOrgID,
		ServicePlan:    globalcommonv1.ServicePlan_Premium,
	})
	g.Expect(err).ShouldNot(BeNil())
}
