package userbehavior

import (
	"fmt"
	"regexp"
	"slices"

	"github.com/tidbcloud/devops/entity/event"
	evbus "github.com/tidbcloud/eventbus-datastruct/event"
	mgmtevent "github.com/tidbcloud/tidb-management-service/utilities/clusterevent"
)

// UB Event handlers
func (s *svc) HandleDBaasTenantPlanEvent(uuid string, ev *event.TenantPlanEvent) error {
	s.logger.Info("handle dbaas tenant plan event for user behavior", "uuid", uuid, "event", ev)
	s.enqueueTenantEventCapture(newTenantPlanCaptureEvent(uuid, ev))
	ubEvent := newTenantPlanUBEvent(ev)
	s.ubCh <- ubEvent
	return nil
}

// UB Event handlers
func (s *svc) HandleDBaasAITenantEvent(uuid string, ev *event.TenantEvent) error {
	s.logger.Info("handle dbaas tenant event for user behavior", "uuid", uuid, "event", ev)
	s.enqueueTenantEventCapture(newOrgCaptureEvent(uuid, ev))
	ubEvent := newstartupUBEvent(ev)
	s.ubCh <- ubEvent
	return nil
}

func (s *svc) HandleDBaaSClusterEvent(uuid string, ev *mgmtevent.ClusterEvent) error {
	s.logger.Info("handle dbaas cluster event for user behavior", "uuid", uuid, "event", ev)
	s.enqueueTenantEventCapture(newClusterCaptureEvent(uuid, ev))
	if ev != nil && ev.ServicePlan == "Starter" {
		s.logger.Info("skip starter cluster event for user behavior", "uuid", uuid, "event", ev)
		return nil
	}
	// TODO: replace this with a more elegant implementaion
	if !slices.Contains(acceptedClusterEvents, ev.EventType) {
		return nil
	}
	ubEvent := newClusterUBEvent(ev)
	s.ubCh <- ubEvent
	return nil
}

const (
	DisplayEventCreateCluster        string = "CreateCluster"
	DisplayEventTypeImport           string = "ImportData"
	DisplayEventTypeRestore          string = "RestoreFromBackup"
	DisplayEventTypeBackup           string = "BackupCluster"
	DisplayEventTypeChangefeed       string = "RunChangefeed"
	DisplayEventTypeCreateChangefeed string = "CreateChangefeed"
	DisplayPauseChangefeed           string = "PauseChangefeed"
	DisplayEditChangefeed            string = "EditChangefeed"
	DisplayResumeChangefeed          string = "ResumeChangefeed"
	DisplayScaleChangefeed           string = "ScaleChangefeed"
	DisplayDeleteChangefeed          string = "DeleteChangefeed"
	DisplayFailedChangefeed          string = "FailedChangefeed"
	DisplayEventServerlessImport     string = "CreateImport"
)

var cdcDisplayEvents = []string{
	DisplayEventTypeCreateChangefeed,
	DisplayPauseChangefeed,
	DisplayEditChangefeed,
	DisplayResumeChangefeed,
	DisplayScaleChangefeed,
	DisplayDeleteChangefeed,
	DisplayFailedChangefeed,
}

func (s *svc) HandleDBaaSDisplayEvent(uuid string, ev *evbus.DisplayEvent) error {
	s.logger.Info("handle dbaas display event for user behavior", "uuid", uuid, "event", ev)
	s.enqueueTenantEventCapture(newDisplayCaptureEvent(uuid, ev))

	// map display event to user behavior event
	switch ev.DisplayEventType {
	case DisplayEventTypeImport:
		if slices.Contains(acceptedImportEvents, ev.DisplayStatus) {
			s.ubCh <- newImportUBEvent(ev)
		}
		if ev.DisplayStatus == evbus.EventStatusFailed {
			s.ubCh <- newStartupImportUBEvent(ev)
		}
	case DisplayEventServerlessImport:
		if ev.DisplayStatus == evbus.EventStatusSucceeded {
			s.ubCh <- newStartupImportUBEvent(ev)
		}
	case DisplayEventTypeBackup:
		if !slices.Contains(acceptedBackupEvents, ev.DisplayStatus) {
			return nil
		}
		// filter out succeeded auto backups
		if ev.DisplayStatus == evbus.EventStatusSucceeded && extractBackupTypeFromSummary(ev.Summary) == AutoBackup {
			return nil
		}
		s.ubCh <- newBackupUBEvent(ev)
	case DisplayEventTypeRestore:
		if !slices.Contains(acceptedRestoreEvents, ev.DisplayStatus) {
			return nil
		}
		s.ubCh <- newRestoreUBEvent(ev)
	case DisplayEventCreateCluster:
		if ev.DisplayStatus == evbus.EventStatusStarted {
			s.ubCh <- newstartupClusterUBEvent(ev)
		}
	default:
		if slices.Contains(cdcDisplayEvents, ev.DisplayEventType) && slices.Contains(acceptedCdcEvents, ev.DisplayStatus) {
			s.ubCh <- newCDCUBEvent(ev)
		}
	}
	return nil
}

// handler utils

// HACK
var backupTypeMatcher = regexp.MustCompile(`The (\S+) task`)

func extractBackupTypeFromSummary(backupSummary string) BackupType {
	subStrs := backupTypeMatcher.FindStringSubmatch(backupSummary)
	fmt.Println(subStrs)
	if len(subStrs) < 2 {
		return BackupType("")
	} else {
		return BackupType(subStrs[1])
	}
}
