package integration

import (
	"context"
	"fmt"
	"github.com/DataDog/datadog-api-client-go/v2/api/datadog"
	"github.com/tidbcloud/metrics-server/central/db"
	"github.com/tidbcloud/metrics-server/central/db/entity"
	"github.com/tidbcloud/metrics-server/idl/pbgen/proto/central"
	"github.com/tidbcloud/pkgv2/secret/encrypt"
	"gorm.io/gorm"
	"net/http"
)

// Model encapsulates the all business logics and contexts for integrations.
// Note: no error logs here. We log errors in the service layer.
type Model struct {
	encryptor  encrypt.Encryptor
	datadogApi *datadog.APIClient
	httpc      *http.Client
}

func NewModel(encryptor encrypt.Encryptor) Model {
	configuration := datadog.NewConfiguration()
	apiClient := datadog.NewAPIClient(configuration)

	return Model{
		encryptor:  encryptor,
		datadogApi: apiClient,
		httpc:      http.DefaultClient,
	}
}

// ListIntegrations returns the status of the integrations for a given customer project.
func (m Model) ListIntegrations(ctx context.Context, projectId uint64, providerType central.IntegrationType) ([]*central.IntegrationStatusResp, error) {
	// Get status from metadb.
	is := make([]entity.Integration, 0)
	err := db.Transaction(func(tx *gorm.DB) error {
		q := entity.NewIntegrationQuerySet(tx).ProjectIDEq(entity.ID(projectId))
		if providerType != 0 {
			q = q.ProviderTypeEq(entity.IntegrationType(providerType))
		}
		return q.All(&is)
	})
	if err != nil {
		return nil, err
	}

	// Map entity models to api models.
	results := make([]*central.IntegrationStatusResp, 0, len(is))
	for _, i := range is {

		if i.Status != entity.IntegrationStatusActive && i.Status != entity.IntegrationStatusError {
			// We only return active integration records.
			continue
		}

		var (
			decryptedKey encrypt.Secret
			err          error
			valid        = true
		)

		if len(i.EncryptedDatadogApiKey) > 0 {
			// If the integration is datadog, validate api key.
			decryptedKey, err = encrypt.DecryptBase64(m.encryptor, i.EncryptedDatadogApiKey)
			if err != nil {
				return nil, fmt.Errorf("%s, please check the key or region", err.Error())
			}
			valid, err = m.ValidateDatadogAPIKey(ctx, decryptedKey.String(), DatadogSiteDBToRest(i.DatadogSite))
		}

		if len(i.EncryptedNewrelicApiKey) > 0 {
			// If the integration is newrelic, validate api key.
			decryptedKey, err = encrypt.DecryptBase64(m.encryptor, i.EncryptedNewrelicApiKey)
			if err != nil {
				return nil, err
			}
			valid, err = m.ValidateNewrelicAPIKey(ctx, decryptedKey.String(), NewrelicSiteDBToRest(i.NewrelicSite))
		}
		if err != nil || !valid {
			i.StatusReason = "Invalid api key"
			i.Status = entity.IntegrationStatusError
			err := db.Transaction(func(tx *gorm.DB) error {
				return i.Update(tx, entity.IntegrationDBSchema.Status, entity.IntegrationDBSchema.StatusReason)
			})
			if err != nil {
				return nil, err
			}
		}

		ps := make([]central.IntegrationPayload, 0, len(i.Payloads.Payloads))
		for _, p := range i.Payloads.Payloads {
			ps = append(ps, payloadDBToRest(p))
		}

		results = append(results, &central.IntegrationStatusResp{
			Type:                 typeDBToRest(i.ProviderType),
			Status:               statusDBToRest(i.Status),
			StatusReason:         i.StatusReason,
			Payloads:             ps,
			DatadogSite:          DatadogSiteDBToRest(i.DatadogSite),
			DatadogSiteUrl:       GetDatadogSiteUrl(DatadogSiteDBToRest(i.DatadogSite)),
			DatadogDashboardUrl:  GetDatadogDashboardUrl(DatadogSiteDBToRest(i.DatadogSite)),
			DatadogMaskedApiKey:  i.MaskedDatadogApiKey,
			NewrelicSite:         NewrelicSiteDBToRest(i.NewrelicSite),
			NewrelicSiteUrl:      GetNewrelicSiteEndpoint(NewrelicSiteDBToRest(i.NewrelicSite)),
			NewrelicDashboardUrl: GetNewrelicSiteDashboard(NewrelicSiteDBToRest(i.NewrelicSite)),
			NewrelicMaskedApiKey: i.MaskedNewrelicApiKey,
		})
	}

	return results, nil
}
