package worker

import (
	"context"
	"fmt"
	"strconv"
	"time"

	"github.com/juju/errors"
	"github.com/pingcap/dbaas/central/config"
	"github.com/pingcap/dbaas/central/db"
	"github.com/pingcap/dbaas/central/db/dbutil"
	"github.com/pingcap/dbaas/central/db/entity"
	"github.com/pingcap/dbaas/central/model/billing"
	"github.com/pingcap/dbaas/central/model/util"
	"go.uber.org/zap"
)

func (c BgContext) POCCleanup() error {
	ctx := context.Background()
	tenantID := c.GetTenantID()
	var err error
	dbProvider := db.NewProvider()
	tx := dbProvider.WriteDB(ctx)
	t, err := c.GetTenantEntity()
	if err != nil {
		return err
	}
	enable, err := c.IsPOCEnabled()
	if err != nil {
		return err
	}
	if enable || t.Plan != entity.TenantPlanPOC && !enable {
		// poc not disabled or start ondemand
		return nil
	}

	trialEndedAt, err := dbutil.GetPOCTrialEndedAt(ctx, tx, tenantID)
	if err != nil {
		return errors.Annotate(err, "failed to get poc trial end at")
	}

	if !config.Global().BillingConfig.EnablePOCEndedCleanup {
		c.Log().Info("poc clean up disabled.")
		return nil
	}

	// we only delete clusters 3 days after points run out
	threeDaysAfterTrialEnd := trialEndedAt.Add(time.Hour * 72)
	now := time.Now()
	if now.Before(threeDaysAfterTrialEnd) {
		// time not reatched yet
		c.Log().Info("time not reatched to 3 days after points run out")
		return nil
	}

	c.Log().Info("3 days after points run out, let's clean poc clusters now.")
	clusterContext, err := c.clusterModel.BgContext(tenantID)
	if err != nil {
		c.Log().Error("failed get cluster bg context", zap.Error(err))
		return err
	}
	err = clusterContext.CleanClusters(ctx)
	if err != nil {
		c.Log().Error("failed clean clusters", zap.Error(err))
		return err
	}
	err = c.sendPOCCleanupEmail(t, *trialEndedAt)
	if err != nil {
		return err
	}
	return nil
}

func (c BgContext) sendPOCCleanupEmail(t *entity.Tenant, trialEndedAt time.Time) error {
	ctx := context.Background()
	notificationMgr := util.NewNotificationMgr()
	user, err := c.GetAdminUser(t.Email)
	if err != nil {
		return err
	}
	dbProvider := db.NewProvider()
	tx := dbProvider.WriteDB(ctx)
	c.Log().Debug("sending poc_end email to tenant")
	timezone, err := strconv.Atoi(t.TimeZone)
	if err != nil {
		return err
	}
	endTime := util.ConvertUtcTime(trialEndedAt.Add(time.Duration(util.GetPocManualBackupReservedDays())*time.Hour*24), int32(timezone))
	return notificationMgr.SendNotificationOnce(ctx, tx, t.ID, entity.NotificationScenePocClusterEnd, string(entity.NotificationScenePocClusterEnd), func() error {
		// notify user
		vars4user := map[string]string{
			"userName": user.FirstName,
			"endAt":    endTime,
		}
		if err := c.GetMailHandler().SendMailWithTemplate(ctx, []string{t.Email}, config.EmailSubjectPocRetrieveDataFromTrial, config.TNPocRetrieveDataFromTrial, vars4user, nil); err != nil {
			return err
		}

		// notify ops/account-manager that customer's poc clusters have been deleted
		vars := map[string]string{
			"now":     time.Now().Format(config.TimeFmtDTM),
			"orgName": t.Name,
			"orgId":   fmt.Sprint(t.ID),
		}
		return c.GetMailHandler().SendMailWithTemplate(ctx, util.GetPocStatusUpdateRelatedManagerEmails(ctx, t), config.CustomerStatusUpdateSubject, config.TNCustomerUpdatePocEndClusterDeleted, vars, nil)
	})
}

func (c BgContext) POCReminder() error {
	ctx := context.Background()

	t, err := c.GetTenantEntity()
	if err != nil {
		return err
	}

	if t.Plan != entity.TenantPlanPOC {
		return nil
	}

	user, err := c.GetAdminUser(t.Email)
	if err != nil {
		return err
	}

	notificationMgr := util.NewNotificationMgr()
	dbProvider := db.NewProvider()

	tx := dbProvider.WriteDB(ctx)

	credits, err := c.GetPOCCredits()

	if err != nil {
		return err
	}

	if credits <= 0 {
		err = c.DisableOrgPOCPlan()
		if err != nil {
			return err
		}
	}

	// send emails
	for _, threshold := range billing.PoCReminderFreePointsThresholds {
		if credits <= threshold {
			err = notificationMgr.SendNotificationOnce(ctx, tx, t.ID, entity.NotificationScenePocWillEnd, fmt.Sprintf("%d-reatched", threshold), func() error {
				// notify user
				vars4user := map[string]string{
					"userName":     user.FirstName,
					"remainPoints": fmt.Sprintf("%.2f", float64(credits)/100.0),
				}
				if err := c.GetMailHandler().SendMailWithTemplate(ctx, []string{t.Email}, config.EmailSubjectPocTrialThresholdExceed, config.TNPocThresholdExceed, vars4user, nil); err != nil {
					return err
				}

				// notify ops/account-manager
				vars := map[string]string{
					"now":        time.Now().Format(config.TimeFmtDTM),
					"orgName":    t.Name,
					"orgId":      fmt.Sprint(t.ID),
					"leftPoints": fmt.Sprintf("%.2f", float64(credits)/100.0),
				}
				return c.GetMailHandler().SendMailWithTemplate(ctx, util.GetPocStatusUpdateRelatedManagerEmails(ctx, t), config.CustomerStatusUpdateSubject, config.TNCustomerUpdatePocPointsChanged, vars, nil)
			})
			if err != nil {
				return err
			}
		}
	}
	return nil
}
