package app

import (
	"context"
	"testing"

	"github.com/stretchr/testify/require"
	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	servermocks "github.com/tidbcloud/cluster-service-ng/domain/server/mocks"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/repo"
	"google.golang.org/protobuf/types/known/emptypb"
	"google.golang.org/protobuf/types/known/timestamppb"
)

func TestAutoscalePolicyHandlers(t *testing.T) {
	ctx := context.Background()
	svc := servermocks.NewService(t)
	app := &appImpl{ngService: svc}

	t.Run("GetResolvedAutoscalePolicyBundle", func(t *testing.T) {
		want := &entity.ResolvedAutoscalePolicyBundle{
			SharedPoolId:  "pool-1",
			ComponentType: apiv1.ComponentType_TIKV,
			Version:       2,
		}
		svc.On("GetResolvedAutoscalePolicyBundle", ctx, "pool-1", apiv1.ComponentType_TIKV).Return(want, nil).Once()

		got, err := app.GetResolvedAutoscalePolicyBundle(ctx, &apiv1.GetResolvedAutoscalePolicyBundleRequest{
			SharedPoolId:  "pool-1",
			ComponentType: apiv1.ComponentType_TIKV,
		})
		require.NoError(t, err)
		require.Equal(t, want, got)
	})

	t.Run("ApplyMaterializedAutoscalePolicies", func(t *testing.T) {
		policies := []*entity.AutoscalePolicy{{
			SubjectType:   apiv1.AutoscalePolicy_SHARED_POOL,
			SubjectId:     "pool-1",
			ComponentType: apiv1.ComponentType_TIKV,
			Direction:     apiv1.AutoscalePolicy_SCALE_OUT,
			Capability:    apiv1.AutoscalePolicy_NODE_COUNT,
			Trigger:       apiv1.AutoscalePolicy_CPU,
			State:         apiv1.AutoscalePolicy_ENABLED,
		}}
		svc.On("ApplyMaterializedAutoscalePolicies", ctx, apiv1.AutoscalePolicy_SHARED_POOL, "pool-1", policies).Return(policies, nil).Once()

		got, err := app.ApplyMaterializedAutoscalePolicies(ctx, &apiv1.ApplyMaterializedAutoscalePoliciesRequest{
			SubjectType: apiv1.AutoscalePolicy_SHARED_POOL,
			SubjectId:   "pool-1",
			Policies:    policies,
		})
		require.NoError(t, err)
		require.Equal(t, policies, got.GetPolicies())
	})

	t.Run("EvaluateAutoscalePolicy", func(t *testing.T) {
		want := &entity.EvaluateAutoscalePolicyResponse{
			Allowed: true,
			Reason:  "enabled",
		}
		svc.On(
			"EvaluateAutoscalePolicy",
			ctx,
			"pool-1",
			apiv1.ComponentType_TIKV,
			apiv1.AutoscalePolicy_SCALE_OUT,
			apiv1.AutoscalePolicy_NODE_COUNT,
			apiv1.AutoscalePolicy_CPU,
		).Return(want, nil).Once()

		got, err := app.EvaluateAutoscalePolicy(ctx, &apiv1.EvaluateAutoscalePolicyRequest{
			SharedPoolId:  "pool-1",
			ComponentType: apiv1.ComponentType_TIKV,
			Direction:     apiv1.AutoscalePolicy_SCALE_OUT,
			Capability:    apiv1.AutoscalePolicy_NODE_COUNT,
			Trigger:       apiv1.AutoscalePolicy_CPU,
		})
		require.NoError(t, err)
		require.Equal(t, want, got)
	})

	t.Run("ListAutoscalePolicies", func(t *testing.T) {
		want := &entity.ListAutoscalePoliciesResponse{
			Policies: []*entity.AutoscalePolicy{{
				SubjectType:   apiv1.AutoscalePolicy_SHARED_POOL,
				SubjectId:     "pool-1",
				ComponentType: apiv1.ComponentType_TIKV,
			}},
		}
		svc.On("ListAutoscalePolicies", ctx, apiv1.AutoscalePolicy_SHARED_POOL, "pool-1", apiv1.ComponentType_TIKV).Return(want, nil).Once()

		got, err := app.ListAutoscalePolicies(ctx, &apiv1.ListAutoscalePoliciesRequest{
			SubjectType:   apiv1.AutoscalePolicy_SHARED_POOL,
			SubjectId:     "pool-1",
			ComponentType: apiv1.ComponentType_TIKV,
		})
		require.NoError(t, err)
		require.Equal(t, want, got)
	})

	t.Run("UpsertAutoscaleRuntimeGuard", func(t *testing.T) {
		expiresAt := timestamppb.Now()
		guard := &entity.AutoscaleRuntimeGuard{
			ScopeType: apiv1.AutoscaleRuntimeGuard_REGION,
			ScopeId:   "aws-ap-southeast-1",
			GuardType: apiv1.AutoscaleRuntimeGuard_FORBID_SCALE_IN,
			Reason:    "capacity shortage",
			ExpiresAt: expiresAt,
		}
		svc.On("UpsertAutoscaleRuntimeGuard", ctx, guard).Return(guard, nil).Once()

		got, err := app.UpsertAutoscaleRuntimeGuard(ctx, &apiv1.UpsertAutoscaleRuntimeGuardRequest{Guard: guard})
		require.NoError(t, err)
		require.Equal(t, guard, got)
	})

	t.Run("DeleteAutoscaleRuntimeGuard", func(t *testing.T) {
		svc.On(
			"DeleteAutoscaleRuntimeGuard",
			ctx,
			apiv1.AutoscaleRuntimeGuard_REGION,
			"aws-ap-southeast-1",
			apiv1.AutoscaleRuntimeGuard_FORBID_SCALE_IN,
			apiv1.ComponentType_TIKV,
		).Return(nil).Once()

		got, err := app.DeleteAutoscaleRuntimeGuard(ctx, &apiv1.DeleteAutoscaleRuntimeGuardRequest{
			ScopeType:     apiv1.AutoscaleRuntimeGuard_REGION,
			ScopeId:       "aws-ap-southeast-1",
			GuardType:     apiv1.AutoscaleRuntimeGuard_FORBID_SCALE_IN,
			ComponentType: apiv1.ComponentType_TIKV,
		})
		require.NoError(t, err)
		require.Equal(t, &emptypb.Empty{}, got)
	})

	t.Run("ListAutoscaleRuntimeGuards", func(t *testing.T) {
		guard := &entity.AutoscaleRuntimeGuard{
			ScopeType:     apiv1.AutoscaleRuntimeGuard_REGION,
			ScopeId:       "aws-ap-southeast-1",
			GuardType:     apiv1.AutoscaleRuntimeGuard_FORBID_SCALE_IN,
			ComponentType: apiv1.ComponentType_TIKV,
		}
		filter := repo.AutoscaleRuntimeGuardFilter{
			ScopeType:     apiv1.AutoscaleRuntimeGuard_REGION,
			ScopeID:       "aws-ap-southeast-1",
			GuardType:     apiv1.AutoscaleRuntimeGuard_FORBID_SCALE_IN,
			ComponentType: apiv1.ComponentType_TIKV,
			ActiveOnly:    true,
		}
		want := &entity.ListAutoscaleRuntimeGuardsResponse{Guards: []*entity.AutoscaleRuntimeGuard{guard}}
		svc.On("ListAutoscaleRuntimeGuards", ctx, filter).Return(want, nil).Once()

		got, err := app.ListAutoscaleRuntimeGuards(ctx, &apiv1.ListAutoscaleRuntimeGuardsRequest{
			ScopeType:     apiv1.AutoscaleRuntimeGuard_REGION,
			ScopeId:       "aws-ap-southeast-1",
			GuardType:     apiv1.AutoscaleRuntimeGuard_FORBID_SCALE_IN,
			ComponentType: apiv1.ComponentType_TIKV,
			ActiveOnly:    true,
		})
		require.NoError(t, err)
		require.Equal(t, want, got)
	})
}
