package monitor_data_migration

import (
	"context"

	"github.com/tidbcloud/metrics-server/central/db"
	"github.com/tidbcloud/metrics-server/central/db/dbutil"
	"github.com/tidbcloud/metrics-server/central/db/entity"
	"gorm.io/gorm"
)

const (
	thirdPartyPrometheusIntegrationMigrationCfgKey = "thirdPartyPrometheusIntegrationMigrationCfg"
	datadogIntegrationMigrationCfgKey              = "datadogIntegrationMigrationCfg"
)

type migrationStatus uint

const (
	migrationNotBegin   migrationStatus = 1
	migrationInProgress migrationStatus = 2
	migrationEnd        migrationStatus = 3
)

type migrationConfig struct {
	Status migrationStatus `json:"status"`
}

type ProjectMigrationStatus bool

func (s ProjectMigrationStatus) IsMigrationInProgress() bool {
	return bool(s)
}

func (s ProjectMigrationStatus) Value() bool {
	return bool(s)
}

type ProjectMigrationStatuses map[entity.ID]ProjectMigrationStatus

func GetThirdPartyPrometheusIntegrationProjectMigrationStatus(projectIDs []entity.ID) ProjectMigrationStatuses {
	statuses := getIntegrationProjectMigrationStatus(projectIDs, thirdPartyPrometheusIntegrationMigrationCfgKey, getThirdPartyPrometheusIntegrationProjectMigrationStatus)
	return statuses
}

func GetDatadogIntegrationProjectMigrationStatus(projectIDs []entity.ID) ProjectMigrationStatuses {
	statuses := getIntegrationProjectMigrationStatus(projectIDs, datadogIntegrationMigrationCfgKey, getDatadogIntegrationProjectMigrationStatus)
	return statuses
}

func getIntegrationProjectMigrationStatus(projectIDs []entity.ID, cfgKey string, handler getIntegrationProjectMigrationStatusHandler) ProjectMigrationStatuses {
	statuses := initProjectMigrationStatuses(projectIDs, true)
	return statuses
}

type getIntegrationProjectMigrationStatusHandler func(projectIDs []entity.ID) ProjectMigrationStatuses

func getThirdPartyPrometheusIntegrationProjectMigrationStatus(projectIDs []entity.ID) ProjectMigrationStatuses {
	statuses := initProjectMigrationStatuses(projectIDs, false)

	var (
		projectWhiteLists []entity.MonitorDataMigrationThirdPromProjectWhiteList
		err               error
	)
	err = db.Transaction(func(tx *gorm.DB) error {
		projectWhiteLists, err = dbutil.GetMonitorDataMigrationThirdPromProjectWhiteListByProjectIDs(tx, projectIDs)
		return err
	})

	if err != nil {
		return statuses
	}

	for _, v := range projectWhiteLists {
		statuses[v.ProjectID] = true
	}

	return statuses
}

func getDatadogIntegrationProjectMigrationStatus(projectIDs []entity.ID) ProjectMigrationStatuses {
	statuses := initProjectMigrationStatuses(projectIDs, false)

	var (
		projectWhiteLists []entity.MonitorDataMigrationDatadogProjectWhiteList
		err               error
	)
	err = db.Transaction(func(tx *gorm.DB) error {
		projectWhiteLists, err = dbutil.GetMonitorDataMigrationDatadogProjectWhiteListByProjectIDs(tx, projectIDs)
		return err
	})

	if err != nil {
		return statuses
	}

	for _, v := range projectWhiteLists {
		statuses[v.ProjectID] = true
	}

	return statuses
}

func initProjectMigrationStatuses(projectIDs []entity.ID, status ProjectMigrationStatus) ProjectMigrationStatuses {
	statuses := make(map[entity.ID]ProjectMigrationStatus, len(projectIDs))
	for _, pid := range projectIDs {
		statuses[pid] = status
	}

	return statuses
}

func (pms ProjectMigrationStatuses) GetProjectMigrationStatus(projectID entity.ID) ProjectMigrationStatus {
	return pms[projectID]
}

func (pms ProjectMigrationStatuses) merge(statuses ProjectMigrationStatuses) {
	if pms == nil {
		pms = map[entity.ID]ProjectMigrationStatus{}
	}

	for pid, status := range statuses {
		pms[pid] = status
	}
}

type projectMigrationStatus struct{}

var (
	projectMigrationStatusKey projectMigrationStatus
)

func SetProjectMigrationStatusToContext(ctx context.Context, status ProjectMigrationStatus) context.Context {
	return context.WithValue(ctx, projectMigrationStatusKey, status)
}

func GetProjectMigrationStatusFromContext(ctx context.Context) ProjectMigrationStatus {
	if ctx == nil {
		return false
	}

	value := ctx.Value(projectMigrationStatusKey)
	if value == nil {
		return false
	}

	status, ok := value.(ProjectMigrationStatus)
	if !ok {
		return false
	}

	return status
}

func SetProjectMigrationStatusToContextWithProjectID(ctx context.Context, projectID entity.ID) context.Context {
	projectMigrationStatuses := GetThirdPartyPrometheusIntegrationProjectMigrationStatus([]entity.ID{projectID})
	status := projectMigrationStatuses.GetProjectMigrationStatus(projectID)

	return SetProjectMigrationStatusToContext(ctx, status)
}
