package integration

import (
	"context"
	"errors"
	"fmt"
	"strings"

	"github.com/DataDog/datadog-api-client-go/api/v1/datadog"
	"github.com/pingcap/dbaas/central/db"
	"github.com/pingcap/dbaas/central/db/entity"
	"github.com/tidbcloud/idl/pbgen/proto/central"
	"github.com/tidbcloud/pkgv2/secret/encrypt"
	"gorm.io/gorm"
)

// CreateOrEnableDatadogIntegration enables the datadog integration for a given customer project.
// @param payload - types of data that Datadog will collect.
func (m Model) CreateOrEnableDatadogIntegration(_ context.Context, _ uint64, projectId uint64, payloads []central.IntegrationPayload, site central.DatadogSite, apiKey string) (status central.IntegrationStatus, maskedKey string, err error) {
	// Map api models to db entity models.
	payloadsDB := make([]entity.IntegrationPayload, 0, len(payloads))
	for _, p := range payloads {
		payloadsDB = append(payloadsDB, payloadRestToDB(p))
	}
	siteDB := siteRestToDB(site)
	pid := entity.ID(projectId)
	maskedKey = maskDatadogAPIKey(apiKey)
	datadogIntegration := entity.Integration{
		ProjectID:           pid,
		ProviderType:        entity.IntegrationTypeDatadog,
		Status:              entity.IntegrationStatusActive,
		StatusReason:        "",
		Payloads:            entity.IntegrationPayloads{Payloads: payloadsDB},
		DatadogSite:         siteDB,
		MaskedDatadogApiKey: maskedKey,
	}

	// Encrypt the api key with AWS KMS (with the customized master key).
	encryptedKey, _ := encrypt.EncryptBase64(m.encryptor, encrypt.Secret(apiKey))
	datadogIntegration.EncryptedDatadogApiKey = encryptedKey

	// Create or update the record in metadb.
	err = db.Transaction(func(tx *gorm.DB) error {
		i := new(entity.Integration)
		if err := entity.NewIntegrationQuerySet(tx).ProjectIDEq(pid).One(i); err != nil {
			if errors.Is(err, gorm.ErrRecordNotFound) {
				return datadogIntegration.Create(tx)
			}
			return err
		}
		datadogIntegration.ID = i.ID
		return datadogIntegration.Update(
			tx,
			entity.IntegrationDBSchema.Status,
			entity.IntegrationDBSchema.StatusReason,
			entity.IntegrationDBSchema.Payloads,
			entity.IntegrationDBSchema.DatadogSite,
			entity.IntegrationDBSchema.MaskedDatadogApiKey,
			entity.IntegrationDBSchema.EncryptedDatadogApiKey,
		)
	})
	if err != nil {
		return central.IntegrationStatus_INTEGRATION_STATUS_UNSPECIFIED, maskedKey, err
	}

	return central.IntegrationStatus_INTEGRATION_STATUS_UNSPECIFIED, maskedKey, nil
}

// DisableDatadogIntegration disable the datadog integration for a given customer project.
func (m Model) DisableDatadogIntegration(_ context.Context, _ uint64, projectId uint64) (central.IntegrationStatus, error) {
	err := db.Transaction(func(tx *gorm.DB) error {
		return entity.NewIntegrationQuerySet(tx).ProjectIDEq(entity.ID(projectId)).ProviderTypeEq(entity.IntegrationTypeDatadog).GetUpdater().SetStatus(entity.IntegrationStatusNone).Update()
	})
	if err != nil {
		return central.IntegrationStatus_INTEGRATION_STATUS_UNSPECIFIED, err
	}
	return central.IntegrationStatus_INTEGRATION_STATUS_NONE, nil
}

// ValidateDatadogAPIKey validates the API key through Datadog site.
func (m Model) ValidateDatadogAPIKey(_ context.Context, apiKey string, site central.DatadogSite) (valid bool, err error) {
	ddCTX := context.Background()
	ddCTX = context.WithValue(
		ddCTX,
		datadog.ContextServerVariables,
		map[string]string{"site": GetDatadogSiteUrl(site)},
	)
	ddCTX = context.WithValue(
		ddCTX,
		datadog.ContextAPIKeys,
		map[string]datadog.APIKey{
			"apiKeyAuth": {Key: apiKey}},
	)
	resp, _, err := m.datadogApi.AuthenticationApi.Validate(ddCTX)
	if err != nil {
		return false, err
	}
	return resp.GetValid(), nil
}

func GetDatadogSiteUrl(site central.DatadogSite) string {
	// DatadogSiteUrl refers to [https://github.com/DataDog/datadog-agent/blob/main/pkg/config/config_template.yaml]
	switch site {
	case central.DatadogSite_DATADOG_SITE_US1:
		return "datadoghq.com"
	case central.DatadogSite_DATADOG_SITE_US3:
		return "us3.datadoghq.com"
	case central.DatadogSite_DATADOG_SITE_US5:
		return "us5.datadoghq.com"
	case central.DatadogSite_DATADOG_SITE_EU1:
		return "datadoghq.eu"
	case central.DatadogSite_DATADOG_SITE_US1_FED:
		return "ddog-gov.com"
	default:
		return "datadoghq.com"
	}
}

// GetDatadogDashboardUrl hardcodes the `TiDBCloud Integration dashboard` url.
func GetDatadogDashboardUrl(site central.DatadogSite) string {
	var (
		authority     string
		integrationID int
	)
	switch site {
	case central.DatadogSite_DATADOG_SITE_US1:
		authority = "app.datadoghq.com"
		integrationID = 30586
	case central.DatadogSite_DATADOG_SITE_US3:
		authority = "us3.datadoghq.com"
		integrationID = 431
	case central.DatadogSite_DATADOG_SITE_US5:
		authority = "us5.datadoghq.com"
		integrationID = 421
	case central.DatadogSite_DATADOG_SITE_EU1:
		authority = "app.datadoghq.eu"
		integrationID = 447
	default:
		authority = "app.datadoghq.com"
		integrationID = 30586
	}
	return fmt.Sprintf("https://%s/dash/integration/%d/tidbcloud-cluster-overview", authority, integrationID)
}

// maskDatadogAPIKey masks the prefix of an API key and only keeps the last 1/8 characters.
func maskDatadogAPIKey(k string) string {
	l := len(k)
	maskLen := l / 8
	var b strings.Builder
	b.Grow(l)
	for i := 0; i < (l-maskLen)/2; i++ {
		b.WriteString("·")
	}
	b.WriteString(k[l-maskLen:])
	return b.String()
}
