package cmd

import (
	"context"
	"github.com/tidbcloud/dataflow-service/app/workflow/action"

	"github.com/AlekSi/pointer"
	"github.com/spf13/cobra"
	"github.com/tidbcloud/dataflow-service/app/container"
	"github.com/tidbcloud/dataflow-service/app/models/domain"
	bucketRepo "github.com/tidbcloud/dataflow-service/app/repo"
	"github.com/tidbcloud/dataflow-service/app/repo/entity"
	bootstarpSerer "github.com/tidbcloud/dataflow-service/internal/cmd/bootstrap/server"
	"github.com/tidbcloud/dataflow-service/internal/jobs"
	"github.com/tidbcloud/dataflow-service/pkg/log"
	"go.uber.org/zap"
)

func registerBucketJobFunc(cmd *cobra.Command) {
	{
		var (
			tenantID   uint64
			projectID  uint64
			bucketName string
		)
		testCreateNewArchBucket := &cobra.Command{
			Use:   "test-create-bucket",
			Short: "test-create-bucket",
			RunE: func(cmd *cobra.Command, args []string) error {
				if _, err := bootstarpSerer.NewServer(cmd.Context(), bootstarpSerer.ServerOption{
					EnableEventBusConsumer: false,
				}); err != nil {
					return err
				}
				ctx := log.WithTraceID(cmd.Context())
				app := container.Global()
				resp, err := app.BucketModelProxy.CreateBucket(ctx, &domain.CreateBucketReqV2{
					BusinessPurpose: pointer.To(domain.BucketBusinessPurposeDualRegion),
					Scope:           pointer.To(entity.BucketScopeProject),

					TenantId:  &tenantID,
					ProjectId: &projectID,

					Name:               &bucketName,
					InfraCloudProvider: pointer.To(domain.InfraProviderTypeEKS),
					Regions:            []string{"us-west-2"},
				})
				if err != nil {
					log.GetLogger(ctx).Error("create bucket", zap.Error(err))
					return err
				}
				log.GetLogger(ctx).Info("create bucket success", zap.Any("bucket", resp))
				return nil
			},
		}
		testCreateNewArchBucket.Flags().Uint64VarP(&tenantID, "tenantID", "", 0, "")
		testCreateNewArchBucket.Flags().Uint64VarP(&projectID, "projectID", "", 0, "")
		testCreateNewArchBucket.Flags().StringVarP(&bucketName, "bucketName", "", "", "")
		cmd.AddCommand(testCreateNewArchBucket)
	}

	{
		var bucketID uint64
		var infraProvider string
		suBcmd := &cobra.Command{
			Use:   "test-delete-bucket",
			Short: "test-delete-bucket",
			RunE: func(cmd *cobra.Command, args []string) error {
				if _, err := bootstarpSerer.NewServer(cmd.Context(), bootstarpSerer.ServerOption{
					EnableEventBusConsumer: false,
				}); err != nil {
					return err
				}
				ctx := log.WithTraceID(cmd.Context())
				app := container.Global()
				req := &domain.DeleteBucketReqV2{
					BucketID:           pointer.ToUint64(bucketID),
					InfraCloudProvider: pointer.To(domain.InfraProviderType(infraProvider)),
				}
				resp, err := app.BucketModelProxy.DeleteBucket(ctx, req)
				if err != nil {
					log.GetLogger(ctx).Error("delete bucket", zap.Error(err))
					return err
				}
				log.GetLogger(ctx).Info("delete bucket success", zap.Any("bucket", resp))
				return nil
			},
		}
		suBcmd.Flags().Uint64VarP(&bucketID, "id", "", 0, "id")
		suBcmd.Flags().StringVarP(&infraProvider, "infraProvider", "", "eks", "infra provider")
		cmd.AddCommand(suBcmd)
	}

	{
		suBcmd := &cobra.Command{
			Use:   "test-clean-aws-bucket-worker",
			Short: "test clean worker for aws bucket",
			RunE: func(cmd *cobra.Command, args []string) error {
				if _, err := bootstarpSerer.NewServer(cmd.Context(), bootstarpSerer.ServerOption{
					EnableEventBusConsumer: false,
				}); err != nil {
					return err
				}
				ctx := log.WithTraceID(cmd.Context())
				app := container.Global()
				err := app.AWSBucketWorkerV2.RecycleBucket(ctx)
				if err != nil {
					log.GetLogger(ctx).Error("delete bucket", zap.Error(err))
					return err
				}
				return nil
			},
		}
		cmd.AddCommand(suBcmd)
	}

	{
		suBcmd := &cobra.Command{
			Use:   "test-clean-aws-bucket-replication-worker",
			Short: "test clean worker for aws bucket replication",
			RunE: func(cmd *cobra.Command, args []string) error {
				if _, err := bootstarpSerer.NewServer(cmd.Context(), bootstarpSerer.ServerOption{
					EnableEventBusConsumer: false,
				}); err != nil {
					return err
				}
				ctx := log.WithTraceID(cmd.Context())
				app := container.Global()
				err := app.AWSBucketReplicationWorker.RecycleBucketReplication(ctx)
				if err != nil {
					log.GetLogger(ctx).Error("delete bucket replication", zap.Error(err))
					return err
				}
				return nil
			},
		}
		cmd.AddCommand(suBcmd)
	}

	{
		var bucketID uint64
		subCmd := &cobra.Command{
			Use:   "clean-bucket-data",
			Short: "clean-bucket-data",
			RunE: func(cmd *cobra.Command, args []string) error {
				if _, err := bootstarpSerer.NewServer(cmd.Context(), bootstarpSerer.ServerOption{
					EnableEventBusConsumer: false,
				}); err != nil {
					return err
				}
				ctx := cmd.Context()
				return jobs.CleanBucketData(ctx, bucketID)
			},
		}
		subCmd.Flags().Uint64VarP(&bucketID, "bucket-id", "", 0, "bucket id")
		cmd.AddCommand(subCmd)
	}

	{
		subCmd := &cobra.Command{
			Use:   "fix-bucket-tags",
			Short: "fix-bucket-tags",
			RunE: func(cmd *cobra.Command, args []string) error {
				if _, err := bootstarpSerer.NewServer(cmd.Context(), bootstarpSerer.ServerOption{
					EnableEventBusConsumer: false,
				}); err != nil {
					return err
				}
				ctx := cmd.Context()
				return fixBucketTags(ctx)
			},
		}
		cmd.AddCommand(subCmd)
	}
}

func fixBucketTags(ctx context.Context) error {
	logger := log.GetLogger(ctx)
	app := container.Global()
	buckets, err := app.BucketRepo.ListByFilter(ctx, bucketRepo.BucketFilter{
		Statuses: []entity.BucketStatus{entity.BucketStatusCreated},
	})
	if err != nil {
		logger.Error("list bucket error", zap.Error(err))
		return err
	}
	for _, bucket := range buckets {
		fixOneBucketTags(ctx, bucket)
	}
	return nil
}

func fixOneBucketTags(ctx context.Context, bucket *entity.Bucket) {
	logger := log.GetLogger(ctx)
	if bucket.Provider == "aws" {
		logger.Info("fix aws bucket tags", zap.Any("bucket id", bucket.ID))
		if err := fixAwsBucketTags(ctx, bucket); err != nil {
			logger.Error("put aws bucket tags error", zap.Any("bucket id", bucket.ID), zap.Error(err))
		}
		return
	}
	if bucket.Provider == "gcp" {
		logger.Info("fix gcp bucket tags", zap.Any("bucket id", bucket.ID))
		if err := fixGcpBucketTags(ctx, bucket); err != nil {
			logger.Error("put gcp bucket tags error", zap.Any("bucket id", bucket.ID), zap.Error(err))
		}
		return
	}
	logger.Warn("unsupported provider",
		zap.Any("bucket id", bucket.ID), zap.String("provider", bucket.Provider))
}

func fixAwsBucketTags(
	ctx context.Context, bucket *entity.Bucket,
) error {
	putAwsBucketTagsReq := &domain.PutAWSBucketTagsReq{
		AWSBucketReq: domain.AWSBucketReq{
			Name:         bucket.Name,
			AWSAccountID: *bucket.CloudAccountID,
			Region:       bucket.Regions[0],
		},
		Tags: map[string]string{
			"environment": action.GenInfraCREnvironmentTagValue(),
			"servicetype": "dedicated",
			"usedby":      "custom-tidb",
			"component":   bucket.GenResourceComponentTagValue(),
			"tenant":      bucket.TenantID,
			"project":     bucket.ProjectID,
		},
	}
	return container.Global().AWSBucketManagerModel.PutBucketTags(ctx, putAwsBucketTagsReq)
}

func fixGcpBucketTags(
	ctx context.Context, bucket *entity.Bucket,
) error {
	putAwsBucketTagsReq := &domain.GCPPutBucketTags{
		Name: bucket.Name,
		Tags: map[string]string{
			"environment": action.GenInfraCREnvironmentTagValue(),
			"servicetype": "dedicated",
			"usedby":      "custom-tidb",
			"component":   bucket.GenResourceComponentTagValue(),
			"tenant":      bucket.TenantID,
			"project":     bucket.ProjectID,
		},
	}
	return container.Global().GCPBucketManagerModel.PutBucketTags(ctx, putAwsBucketTagsReq)
}

func init() {
	bucketCmd := &cobra.Command{Use: "bucket", Short: "bucket cmd"}
	bucketCmd.PreRunE = func(cmd *cobra.Command, args []string) error {
		return nil
	}

	registerBucketJobFunc(bucketCmd)
	jobCmd.AddCommand(bucketCmd)
}
