package eventbus

import (
	"context"
	"encoding/base64"
	"encoding/json"

	"github.com/pingcap/errors"
	"github.com/tidbcloud/ecosystem-service/internal/metric"
	vercelProxy "github.com/tidbcloud/ecosystem-service/internal/service/proxy/vercel"
	"github.com/tidbcloud/ecosystem-service/internal/storage/db/vercel"
	"github.com/tidbcloud/ecosystem-service/internal/utils"
	core_event "github.com/tidbcloud/eventbus-datastruct/event"
	"github.com/tidbcloud/pkgv2/log"
	"github.com/tidbcloud/pkgv2/tracing"
	"github.com/tidbcloud/serverless-service/pkg/event"
	"github.com/tidbcloud/tiinf/transport/tieventbus"
	"go.uber.org/zap"
)

type BranchEventService struct {
	eventbusService                *EventbusService
	branchEnvironmentIdentifierDAO vercel.BranchEnvironmentIdentifierDAO
	vercelAPIClient                vercelProxy.APIClient
}

func NewBranchEventService(eventbusService *EventbusService,
	branchEnvironmentIdentifierDAO vercel.BranchEnvironmentIdentifierDAO,
	vercelAPIClient vercelProxy.APIClient,
) *BranchEventService {
	s := &BranchEventService{
		eventbusService:                eventbusService,
		branchEnvironmentIdentifierDAO: branchEnvironmentIdentifierDAO,
		vercelAPIClient:                vercelAPIClient,
	}
	s.registerHandler()
	return s
}

func (s *BranchEventService) registerHandler() {
	s.eventbusService.RegisterHandler(tieventbus.HandlerConfig{
		EventType:  "display_event",
		Group:      "branch-consumer",
		RetryTimes: 5,
		HandleFunc: s.handleFunc,
		DataStruct: core_event.DisplayEvent{},
	})
}

func (s *BranchEventService) handleFunc(e *tieventbus.Event) (err error) {
	ctx := context.Background()
	ctx, span := Tracer.Start(ctx, "BranchEventService.handleFunc")
	defer func() {
		tracing.EndSpan(span, err)
	}()

	logger := log.WithContextL(ctx).With(zap.String("event_id", e.ID)).With(zap.String("event_type", e.Type))
	logger.Info("event received")
	evt, ok := e.Data.(*core_event.DisplayEvent)
	if !ok {
		logger.Warn("event type mismatched")
		return nil
	}

	if evt.DisplayEventType == event.EventTypeDeleteBranch {
		return s.handleDeleteBranch(ctx, e.ID, evt, logger)
	}

	return nil
}

func (s *BranchEventService) handleDeleteBranch(ctx context.Context, eventID string, evt *core_event.DisplayEvent, logger *zap.Logger) (err error) {
	logger.Info("handle delete branch event", zap.Any("event", evt))
	defer func() {
		status := metric.StatusOK
		if err != nil {
			status = metric.StatusErr
		}

		metric.EventHandlerCounter.WithLabelValues(eventID, evt.DisplayEventType, status).Inc()
	}()

	detailString, ok := evt.Detail.(string)
	if !ok {
		logger.Error("failed to convert branch detail")
		return errors.New("failed to convert branch detail")
	}

	decodeBytes, err := base64.StdEncoding.DecodeString(detailString)
	if err != nil {
		logger.Error("failed to decode branch detail", zap.Error(err), zap.String("detail", detailString))
		return err
	}
	detail := &event.BranchDetail{}
	err = json.Unmarshal(decodeBytes, &detail)
	if err != nil {
		logger.Error("failed to unmarshal branch detail", zap.Error(err), zap.String("detail", detailString))
		return err
	}

	logger = logger.With(zap.String("branch_id", detail.ID))
	branchEnvs, err := s.branchEnvironmentIdentifierDAO.GetEnvironmentIdentifiersByBranchID(ctx, detail.ID)
	if err != nil {
		logger.Error("failed to get branch environment identifier", zap.Error(err))
		return err
	}

	if branchEnvs == nil {
		return nil
	}

	for _, env := range branchEnvs {
		envsToDelete := utils.GetBranchEnvsToDelete(env)

		var tID string
		if env.ClusterConnection.VercelIntegrationConfiguration.TeamID.Valid {
			tID = env.ClusterConnection.VercelIntegrationConfiguration.TeamID.String
		} else {
			tID = ""
		}

		err = s.vercelAPIClient.DeleteEnvironmentVariables(ctx, env.ClusterConnection.VercelProjectID,
			tID, envsToDelete, env.ClusterConnection.VercelIntegrationConfiguration.ApiAccessToken)
		if err != nil {
			logger.Error("failed to delete environment variables", zap.Error(err))
			return err
		}

		err = s.branchEnvironmentIdentifierDAO.DeleteEnvironmentIdentifier(ctx, env.ID)
		if err != nil {
			logger.Error("failed to delete branch environment identifier", zap.Error(err))
			return err
		}
	}

	logger.Info("succeed to handle delete branch event")
	return nil
}
