package migrations

import (
	"context"
	"fmt"

	"github.com/grafana/grafana/pkg/infra/kvstore"
	"github.com/grafana/grafana/pkg/services/datasources"
	"github.com/grafana/grafana/pkg/services/featuremgmt"
	secretskvs "github.com/grafana/grafana/pkg/services/secrets/kvstore"
)

const (
	// Not set means migration has not happened
	secretMigrationStatusKey = "secretMigrationStatus"
	// Migration happened with disableSecretCompatibility set to false
	compatibleSecretMigrationValue = "compatible"
	// Migration happened with disableSecretCompatibility set to true
	completeSecretMigrationValue = "complete"
)

type DataSourceSecretMigrationService struct {
	dataSourcesService datasources.DataSourceService
	kvStore            *kvstore.NamespacedKVStore
	features           featuremgmt.FeatureToggles
}

func ProvideDataSourceMigrationService(
	dataSourcesService datasources.DataSourceService,
	kvStore kvstore.KVStore,
	features featuremgmt.FeatureToggles,
) *DataSourceSecretMigrationService {
	return &DataSourceSecretMigrationService{
		dataSourcesService: dataSourcesService,
		kvStore:            kvstore.WithNamespace(kvStore, 0, secretskvs.DataSourceSecretType),
		features:           features,
	}
}

func (s *DataSourceSecretMigrationService) Migrate(ctx context.Context) error {
	migrationStatus, _, err := s.kvStore.Get(ctx, secretMigrationStatusKey)
	if err != nil {
		return err
	}
	logger.Debug(fmt.Sprint("secret migration status is ", migrationStatus))
	// If this flag is true, delete secrets from the legacy secrets store as they are migrated
	disableSecretsCompatibility := s.features.IsEnabled(ctx, featuremgmt.FlagDisableSecretsCompatibility)
	// If migration hasn't happened, migrate to unified secrets and keep copy in legacy
	// If a complete migration happened and now backwards compatibility is enabled, copy secrets back to legacy
	needCompatibility := migrationStatus != compatibleSecretMigrationValue && !disableSecretsCompatibility
	// If migration hasn't happened, migrate to unified secrets and delete from legacy
	// If a compatible migration happened and now compatibility is disabled, delete secrets from legacy
	needMigration := migrationStatus != completeSecretMigrationValue && disableSecretsCompatibility

	if needCompatibility || needMigration {
		logger.Debug("performing secret migration", "needs migration", needMigration, "needs compatibility", needCompatibility)
		query := &datasources.GetAllDataSourcesQuery{}
		dsList, err := s.dataSourcesService.GetAllDataSources(ctx, query)
		if err != nil {
			return err
		}

		for _, ds := range dsList {
			secureJsonData, err := s.dataSourcesService.DecryptedValues(ctx, ds)
			if err != nil {
				return err
			}

			// Secrets are set by the update data source function if the SecureJsonData is set in the command
			// Secrets are deleted by the update data source function if the disableSecretsCompatibility flag is enabled
			_, err = s.dataSourcesService.UpdateDataSource(ctx, &datasources.UpdateDataSourceCommand{
				ID:             ds.ID,
				OrgID:          ds.OrgID,
				UID:            ds.UID,
				Name:           ds.Name,
				JsonData:       ds.JsonData,
				SecureJsonData: secureJsonData,

				// These are needed by the SQL function due to UseBool and MustCols
				IsDefault:       ds.IsDefault,
				BasicAuth:       ds.BasicAuth,
				WithCredentials: ds.WithCredentials,
				ReadOnly:        ds.ReadOnly,
				User:            ds.User,
				Database:        ds.Database,
			})
			if err != nil {
				return err
			}
		}

		var newMigStatus string
		if disableSecretsCompatibility {
			newMigStatus = completeSecretMigrationValue
		} else {
			newMigStatus = compatibleSecretMigrationValue
		}
		err = s.kvStore.Set(ctx, secretMigrationStatusKey, newMigStatus)
		if err != nil {
			return err
		}
		logger.Debug(fmt.Sprint("set secret migration status to ", newMigStatus))
	}

	return nil
}
