package cluster

import (
	"context"
	"fmt"
	"time"

	"github.com/pingcap/errors"
	"github.com/tidbcloud/core-portal/internal/central/config"
	"github.com/tidbcloud/core-portal/internal/central/db"
	"github.com/tidbcloud/core-portal/internal/central/event"
	"github.com/tidbcloud/core-portal/internal/central/infra-api"
	"github.com/tidbcloud/core-portal/internal/central/model/util"
	"github.com/tidbcloud/core-portal/internal/central/repository"
	entity "github.com/tidbcloud/core-svc/model"
	"github.com/tidbcloud/pkgv2/log"
	mailv2 "github.com/tidbcloud/pkgv2/mail/v2"
	"github.com/tidbcloud/pkgv2/pubsub"
	"go.uber.org/zap"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
)

const (
	ingressTrafficAnnKey = "bandwidth.tidbcloud.com/ingress"
	egressTrafficAnnKey  = "bandwidth.tidbcloud.com/egress"
)

func MakeClusterTrafficUsagePeriod(t time.Time) string {
	year, week := t.UTC().ISOWeek()
	return fmt.Sprintf("%d:%d", year, week)
}

type devTierNetworkThresholdSwitchProcessor struct {
	infraCli infraapi.InfraFacade
	logger   *zap.Logger
}

func NewDevTierNetworkThresholdSwitchProcessor(
	infraCli infraapi.InfraFacade) *devTierNetworkThresholdSwitchProcessor {
	return &devTierNetworkThresholdSwitchProcessor{
		infraCli: infraCli,
		logger:   log.With(zap.String("module", "devTierNetworkThresholdSwitchProcessor")),
	}
}

func (p *devTierNetworkThresholdSwitchProcessor) Name() string {
	return "devTierNetworkThresholdSwitchProcessor"
}

func (p *devTierNetworkThresholdSwitchProcessor) Focused(ctx context.Context, data pubsub.Event) bool {
	logger := log.AppendCtxFields(ctx, p.logger)
	ev, ok := data.(*event.ClusterThresholdStateChanged)
	logger.Sugar().Debugf("focused?, ev=%+v,ok=%t", ev, ok)
	return ok
}

func (p *devTierNetworkThresholdSwitchProcessor) OnEvent(ctx context.Context, data pubsub.Event) error {
	logger := log.AppendCtxFields(ctx, p.logger)
	ev := data.(*event.ClusterThresholdStateChanged)
	if !ev.IsDevTier {
		return nil
	}
	logger.Info("event received", zap.Uint64("clusterID", ev.ClusterID), zap.String("ev", fmt.Sprintf("%+v", ev)))

	cr, err := p.infraCli.GetCluster(ctx, ev.ProjectID, ev.ClusterID)
	if err != nil {
		if apierrors.IsNotFound(errors.Cause(err)) {
			logger.Info("cr is NOT created, wait next time", zap.Uint64("clusterID", ev.ClusterID))
			return nil
		}
		return errors.AddStack(err)
	}

	setNetworkThresholdAnnotations(cr, ev.IngressThrottled, ev.EgressThrottled)
	if err := p.infraCli.UpdateCluster(ctx, cr); err != nil {
		return errors.Annotate(err, "update cluster for network threshold info")
	}
	return nil
}

type devTierNetworkThrottledNotifyProcessor struct {
	tenantRepo      repository.TenantRepository
	userRepo        repository.UserRepository
	dbProvider      db.Provider
	notificationMgr util.NotificationManager
	mailCli         mailv2.Facade
	logger          *zap.Logger
}

func NewDevTierNetworkThrottledNotifyProcessor(
	tenantRepo repository.TenantRepository,
	userRepo repository.UserRepository,
	dbProvider db.Provider,
	notificationMgr util.NotificationManager,
	mailCli mailv2.Facade,
) *devTierNetworkThrottledNotifyProcessor {
	return &devTierNetworkThrottledNotifyProcessor{
		tenantRepo:      tenantRepo,
		userRepo:        userRepo,
		dbProvider:      dbProvider,
		notificationMgr: notificationMgr,
		mailCli:         mailCli,
		logger:          log.With(zap.String("module", "devTierNetworkThrottledNotifyProcessor")),
	}
}
func (p *devTierNetworkThrottledNotifyProcessor) Name() string {
	return "devTierNetworkThrottledNotifyProcessor"
}

func (p *devTierNetworkThrottledNotifyProcessor) Focused(ctx context.Context, data pubsub.Event) bool {
	logger := log.AppendCtxFields(ctx, p.logger)
	ev, ok := data.(*event.ClusterThresholdStateChanged)
	logger.Sugar().Debugf("focused?, ev=%+v,ok=%t", ev, ok)
	if !ok {
		return false
	}
	if !ev.IsDevTier || !ev.AnyThrottled() {
		return false
	}
	return true
}

func (p *devTierNetworkThrottledNotifyProcessor) OnEvent(ctx context.Context, data pubsub.Event) error {
	logger := log.AppendCtxFields(ctx, p.logger)
	ev := data.(*event.ClusterThresholdStateChanged)
	logger.Info("event received", zap.Uint64("clusterID", ev.ClusterID), zap.String("ev", fmt.Sprintf("%+v", ev)))

	tenant, err := p.tenantRepo.Load(ctx, entity.ID(ev.TenantID))
	if err != nil {
		return errors.Annotate(err, "get tenant by id")
	}
	user, err := p.userRepo.LoadByEmail(ctx, tenant.Email)
	if err != nil {
		return errors.Annotate(err, "get user by email")
	}

	tx := p.dbProvider.WriteDB(ctx)
	identifier := p.makeClusterThresholdExceedIdentifier(ctx, ev.TenantID, ev.TriggerTime)
	err = p.notificationMgr.SendNotificationOnce(ctx, tx, entity.ID(ev.TenantID), entity.NotificationSceneClusterNetworkThreshold, identifier, func() error {
		vars := map[string]string{
			"userName": user.FirstName,
		}
		return p.mailCli.SendMailWithTemplate(ctx, []string{tenant.Email}, config.EmailSubjectDevTierClusterNetworkThrottled, config.TNDevTierClusterNetworkThreshold, vars, nil)
	})
	return errors.Annotate(err, "send cluster network throttled notification")
}

func (p *devTierNetworkThrottledNotifyProcessor) makeClusterThresholdExceedIdentifier(ctx context.Context, tenantID uint64, now time.Time) string {
	year, week := now.UTC().ISOWeek()
	// identifier: tenantID + period(year + week)
	return fmt.Sprintf("%d:%d:%d", tenantID, year, week)
}
