package prometheus

import (
	"context"
	"fmt"

	"github.com/tidbcloud/metrics-integration/global/internal/domain/entity"
	"github.com/tidbcloud/metrics-integration/global/internal/infrastructure/config"
	"github.com/tidbcloud/metrics-integration/global/internal/infrastructure/di"
	"github.com/tidbcloud/metrics-integration/global/internal/infrastructure/promconfig"
	"github.com/tidbcloud/metrics-integration/global/pkg/helm/pipeline"
	"github.com/tidbcloud/metrics-integration/global/pkg/log"
	mgmt "github.com/tidbcloud/tidb-management-service/api/spec/tidb_mgmt_service/v1"
	clusterevent "github.com/tidbcloud/tidb-management-service/utilities/clusterevent"
	"go.uber.org/zap"
	"gorm.io/gorm"
)

type PrometheusWithClusterScaling struct{}

func (r *PrometheusWithClusterScaling) Name() string {
	return "PrometheusWithClusterScaling"
}

func (r *PrometheusWithClusterScaling) Match(event *clusterevent.ClusterEvent) bool {
	return event.EventType == clusterevent.EventTypeScaling
}

func (r *PrometheusWithClusterScaling) Execute(
	ctx context.Context, event *clusterevent.ClusterEvent, cluster *mgmt.Cluster,
) error {
	logger := log.GetLogger(ctx).Named("PrometheusWithClusterScaling")
	// Additional validation: ensure cluster information is provided and valid
	if cluster == nil || cluster.ClusterId == 0 {
		logger.Error("cluster information is nil, cannot proceed with scaling operations")
		return fmt.Errorf("cluster information is required but not provided")
	}
	logger.Info("metricskey event: PrometheusWithClusterScaling",
		zap.Uint64("clusterID", cluster.ClusterId))

	// Get all active metricskeys by cluster ID
	var metricsKeys []entity.MetricsKey
	if err := di.DiCall(func(db *gorm.DB) error {
		return db.Model(&entity.MetricsKey{}).
			Where("cluster_id = ?", cluster.ClusterId).
			Find(&metricsKeys).Error
	}); err != nil {
		logger.Error("failed to query metrics keys by cluster ID",
			zap.Uint64("clusterID", cluster.ClusterId),
			zap.Error(err))
		return fmt.Errorf("query metricskeys failed: %w", err)
	}

	// If no active metricskeys found, no upgrade needed
	if len(metricsKeys) == 0 {
		logger.Info("no active metricskeys found for cluster",
			zap.Uint64("clusterID", cluster.ClusterId))
		return nil
	}

	logger.Info("found metricskeys to upgrade",
		zap.Uint64("clusterID", cluster.ClusterId),
		zap.Int("count", len(metricsKeys)))

	// Upgrade each metricskey using workflow
	for _, metricskey := range metricsKeys {
		if err := r.upgradeMetricsKey(metricskey, logger); err != nil {
			logger.Error("failed to upgrade metricskey",
				zap.Uint64("metricskeyID", uint64(metricskey.ID)),
				zap.Uint64("clusterID", cluster.ClusterId),
				zap.Error(err))
			// Continue with other integrations even if one fails
			continue
		}

		logger.Info("successfully triggered upgrade workflow for integration",
			zap.Uint64("integrationID", uint64(metricskey.ID)),
			zap.Uint64("clusterID", cluster.ClusterId))
	}

	return nil
}

// upgradeMetricsKey triggers the workflow to upgrade a single metricsKey
func (r *PrometheusWithClusterScaling) upgradeMetricsKey(
	metricsKey entity.MetricsKey,
	logger *zap.Logger,
) error {
	// Validate metricsKey ID to prevent unauthorized operations
	if metricsKey.ID == 0 {
		logger.Error("metricsKey ID is empty or zero, skipping upgrade to prevent unauthorized access",
			zap.Uint64("metricsKeyID", uint64(metricsKey.ID)))
		return fmt.Errorf("invalid integration ID: cannot be empty or zero")
	}

	// update pipeline resources
	clusterConfigBytes, err := pipeline.BuildTargetClusterConfigYAML(uint64(metricsKey.ClusterID))
	if err != nil {
		return err
	}

	if err := promconfig.RemoteScale(context.Background(), fmt.Sprintf("%d", metricsKey.ClusterID), string(clusterConfigBytes), config.Global().RegionalExecutorConfig.GetServerAddr(string(metricsKey.CloudProviderType), metricsKey.Region)); err != nil {
		return fmt.Errorf("failed to remote delete metrics key to cluster: %v", err)
	}
	logger.Debug("prometheus cluster scaling config update triggered successfully",
		zap.Uint64("integrationID", uint64(metricsKey.ID)),
		zap.String("region", metricsKey.Region))

	return nil
}
