package integration

import (
	"context"
	"fmt"

	"github.com/shiningrush/fastflow/pkg/mod"
	consolev1 "github.com/tidbcloud/metrics-integration/global/api/integration/v1"
	"github.com/tidbcloud/metrics-integration/global/internal/domain/entity"
	"github.com/tidbcloud/metrics-integration/global/internal/infrastructure/di"
	"github.com/tidbcloud/metrics-integration/global/pkg/cvt"
	"github.com/tidbcloud/pkgv2/secret/encrypt"
	"gorm.io/gorm"
)

// ListIntegrationsForConsoleAPI retrieves pipelines for a given cluster ID, supports pagination.
func ListIntegrationsForConsoleAPI(ctx context.Context, clusterId uint64, providerType entity.IntegrationType) ([]*consolev1.IntegrationStatusResp, error) {
	// Get status from metadb.
	is := make([]entity.Integration, 0)

	if err := di.DiCall(func(db *gorm.DB) error {
		query := db.Model(&entity.Integration{}).Where("cluster_id = ?", clusterId)
		if providerType != entity.IntegrationTypeUnspecified {
			query = query.Where("provider_type = ?", providerType)
		}
		return query.Find(&is).Error
	}); err != nil {
		return nil, fmt.Errorf("list pipeline failed: %v", err)
	}

	// Map entity models to api models.
	results := make([]*consolev1.IntegrationStatusResp, 0, len(is))
	for _, i := range is {
		if i.Status != entity.IntegrationStatusActive && i.Status != entity.IntegrationStatusError {
			// We only return active integration records.
			continue
		}

		// Construct result per provider so only relevant fields are populated.
		base := &consolev1.IntegrationStatusResp{
			Id:           uint64(i.ID),
			OrgId:        uint64(i.TenantID),
			ProjectId:    uint64(i.ProjectID),
			ClusterId:    uint64(i.ClusterID),
			Type:         cvt.TypeDBToConsoleAPI(i.ProviderType),
			Status:       cvt.StatusDBToConsoleAPI(i.Status),
			StatusReason: i.StatusReason,
			Payloads:     []consolev1.IntegrationPayload{consolev1.IntegrationPayload_INTEGRATION_PAYLOAD_METRICS},
		}

		switch i.ProviderType {
		case entity.IntegrationTypeDatadog:
			var (
				ddKey   encrypt.Secret
				ddErr   error
				ddValid = true
			)

			if ddErr = di.DiCall(func(encryptor encrypt.Encryptor) error {
				ddKey, ddErr = encrypt.DecryptBase64(encryptor, i.EncryptedDatadogApiKey)
				if ddErr != nil {
					return fmt.Errorf("%s, please check the key or region", ddErr.Error())
				}
				ddValid, ddErr = ValidateDatadogAPIKey(ctx, ddKey.String(), i.DatadogSite)
				return nil
			}); ddErr != nil {
				return nil, fmt.Errorf("failed to decrypt datadog api key: %v", ddErr)
			}

			if ddErr != nil || !ddValid {
				i.StatusReason = "Invalid datadog api key"
				i.Status = entity.IntegrationStatusError

				if err := di.DiCall(func(db *gorm.DB) error {
					return db.Model(&i).Select("status", "status_reason").Updates(i).Error
				}); err != nil {
					return nil, fmt.Errorf("list pipeline failed: %v", err)
				}

				if ddErr != nil {
					return nil, ddErr
				}
				// continue to next integration after marking error
				continue
			}
			base.DatadogSite = cvt.DatadogSiteDBToConsoleAPI(i.DatadogSite)
			base.DatadogSiteUrl = cvt.GetDatadogSiteUrl(i.DatadogSite)
			base.DatadogDashboardUrl = cvt.GetDatadogDashboardUrl(i.DatadogSite)
			base.DatadogMaskedApiKey = i.MaskedDatadogApiKey
		case entity.IntegrationTypeNewrelic:
			var (
				nrKey   encrypt.Secret
				nrErr   error
				nrValid = true
			)

			if nrErr = di.DiCall(func(encryptor encrypt.Encryptor) error {
				nrKey, nrErr = encrypt.DecryptBase64(encryptor, i.EncryptedNewrelicApiKey)
				if nrErr != nil {
					return nrErr
				}
				nrValid, nrErr = ValidateNewrelicAPIKey(ctx, nrKey.String(), i.NewrelicSite)
				return nil
			}); nrErr != nil {
				return nil, fmt.Errorf("failed to decrypt newrelic api key: %v", nrErr)
			}

			if nrErr != nil || !nrValid {
				i.StatusReason = "Invalid newrelic api key"
				i.Status = entity.IntegrationStatusError

				if err := di.DiCall(func(db *gorm.DB) error {
					return db.Model(&i).Select("status", "status_reason").Updates(i).Error
				}); err != nil {
					return nil, fmt.Errorf("list pipeline failed: %v", err)
				}

				if nrErr != nil {
					return nil, nrErr
				}
				// continue to next integration after marking error
				continue
			}
			base.NewrelicSite = cvt.NewrelicSiteDBToConsoleAPI(i.NewrelicSite)
			base.NewrelicSiteUrl = cvt.GetNewrelicSiteEndpoint(i.NewrelicSite)
			base.NewrelicDashboardUrl = cvt.GetNewrelicSiteDashboard(i.NewrelicSite)
			base.NewrelicMaskedApiKey = i.MaskedNewrelicApiKey
		}

		results = append(results, base)
	}

	return results, nil
}

// DisableIntegrationofCluster
func DisableIntegrationofCluster(ctx context.Context, clusterID entity.ID, providerType entity.IntegrationType) (consolev1.IntegrationStatus, error) {
	if err := di.DiCall(func(db *gorm.DB) error {
		var integration entity.Integration
		if err := db.Model(&entity.Integration{}).
			Where("cluster_id = ? AND provider_type = ? AND status != ?", clusterID, providerType, entity.IntegrationStatusNone).
			First(&integration).Error; err != nil {
			if err == gorm.ErrRecordNotFound {
				return nil
			}
			return fmt.Errorf("find integration failed: %v", err)
		}

		// update status to none to make the frontend easy
		if err := db.Model(&entity.Integration{}).Where("id = ?", integration.ID).Update("status", entity.IntegrationStatusNone).Error; err != nil {
			return fmt.Errorf("failed to update status to Deleted: %w", err)
		}

		if _, err := mod.GetCommander().RunDagWithTags("delete-pipeline", map[string]string{
			"id": fmt.Sprintf("%d", integration.ID),
		}, map[string]string{}); err != nil {
			return err
		}

		return nil
	}); err != nil {
		return consolev1.IntegrationStatus_INTEGRATION_STATUS_UNSPECIFIED, fmt.Errorf("disable integration failed: %v", err)
	}

	return consolev1.IntegrationStatus_INTEGRATION_STATUS_NONE, nil
}
