package plan

import (
	"fmt"
	"strconv"

	"github.com/pingcap/dbaas/billing2/db/entity"
	"github.com/pingcap/dbaas/billing2/forms"
	"github.com/pingcap/dbaas/billing2/handler/centralhandler"
	"github.com/pingcap/dbaas/billing2/handler/salesforcehandler"
	"github.com/pingcap/dbaas/billing2/model/marketing"
	"github.com/pingcap/dbaas/billing2/utils/log"
	"github.com/pingcap/dbaas/billing2/utils/timeutils"
	"github.com/shopspring/decimal"
	"go.uber.org/zap"
)

const TimeFmtDTM = "2006-01-02 15:04:05"

const (
	TemplateID_PocThresholdExceed = "d-ac3aefdc061f4bd5a5517e009c2a448a"

	TemplateID_PocCreditsExhaust       = "d-81530bd21fa74683a90e9bb9318db0d8"
	TemplateID_PocCreditsExhaust_Inner = "d-9bd2e7f1b39c41c19493c7026877d2ed"
)

const (
	EmailOnceKey_POCEnd                 = "poc_cluster_will_end_in_3_days"
	EmailOnceKey_POCCreditsEnd          = "poc_credits_end"
	EmailOnceKey_POCEndBackupWillDelete = "poc_end_backup"
	EmailOnceKeyTemplate_POCReminder    = "poc-%d-reatched"
	EmailOnceKey_CustomerSuffix         = "-customer"
	EmailOnceKey_ManagerSuffix          = "-manager"
)

var (
	PocReminderFreePoints1000       = decimal.NewFromInt(1000) // since our unit is penny, the same below
	PoCReminderFreePoints750        = decimal.NewFromInt(750)
	PoCReminderFreePoints500        = decimal.NewFromInt(500)
	PoCReminderFreePoints250        = decimal.NewFromInt(250)
	PoCReminderFreePointsThresholds = []decimal.Decimal{PoCReminderFreePoints250, PoCReminderFreePoints500, PoCReminderFreePoints750, PocReminderFreePoints1000}
)

const SyncPOCCreditsEventType = "sync-poc-credits-salesforce"

func (m *TenantPlanModel) SyncPOCCreditsSalesforce(tenantID uint) error {
	planInfo, err := m.GetTenantPOCPlanInfo(tenantID)
	if err != nil {
		return err
	}

	if planInfo.Status != entity.TenantPlanStatus_Active {
		log.Info("tenant poc plan is not active", zap.Uint("tenantID", tenantID))
		return nil
	}

	credits, err := m.getTenantPOCCredits(tenantID)
	if err != nil {
		return err
	}

	return salesforcehandler.Get().SyncPOCCredits(salesforcehandler.POCCredits{
		PortalID:         fmt.Sprint(tenantID),
		RemainingCredits: *credits,
	})
}

func (m *TenantPlanModel) getTenantPOCCredits(tenantID uint) (*decimal.Decimal, error) {
	summary, err := (&marketing.CreditsPackageModel{}).GetTenantCreditsSummary(tenantID, 0)
	if err != nil {
		return nil, err
	}
	return &summary.AvailableRemainingPOCCredits, nil
}

func (m *TenantPlanModel) SendPOCReminderEmail(tenantID uint) error {
	pocPlanInfo, err := m.GetTenantPOCPlanInfo(tenantID)
	if err != nil {
		return err
	}

	credits, err := m.getTenantPOCCredits(tenantID)
	if err != nil {
		return err
	}

	if pocPlanInfo.Status != entity.TenantPlanStatus_Active {
		log.Info("poc plan not active", zap.Uint("tenantID", tenantID))
		return nil
	}

	if credits.Cmp(decimal.Zero) <= 0 {
		log.Info("poc plan credits empty", zap.Uint("tenantID", tenantID))
		return nil
	}
	tenant, err := centralhandler.GetCentralHandler().GetTenant(tenantID)
	if err != nil {
		log.Error("failed to get tenant", zap.Uint("tenantID", tenantID), zap.Error(err))
		return err
	}
	// send emails
	for _, threshold := range PoCReminderFreePointsThresholds {
		if credits.Cmp(threshold) < 0 {
			emailOnceKey := fmt.Sprintf(EmailOnceKeyTemplate_POCReminder, threshold.IntPart())

			// notify user
			vars4user := map[string]string{
				"remainPoints": credits.StringFixed(2),
				"Org_ID":       strconv.FormatUint(uint64(tenantID), 10),
				"Org_name":     tenant.Name,
			}
			err = centralhandler.GetCentralHandler().SendTenantAdminEmailOnce(tenantID, emailOnceKey+EmailOnceKey_CustomerSuffix, TemplateID_PocThresholdExceed, vars4user)
			if err != nil {
				return err
			}

			// notify ops/account-manager
			vars := map[string]string{
				"now":        timeutils.GetNow().Format(TimeFmtDTM),
				"leftPoints": credits.StringFixed(2),
				"Org_ID":     strconv.FormatUint(uint64(tenantID), 10),
				"Org_name":   tenant.Name,
			}

			err = centralhandler.GetCentralHandler().SendTenantManagerEmailOnce(tenantID, emailOnceKey+EmailOnceKey_ManagerSuffix, TemplateID_PocThresholdExceed, vars)
			if err != nil {
				return err
			}

			return nil
		}
	}

	return nil
}

func (m *TenantPlanModel) ResetPOCRemindersAfterCreditsCharged(tenantID uint) error {
	credits, err := m.getTenantPOCCredits(tenantID)
	if err != nil {
		return err
	}

	err = centralhandler.GetCentralHandler().ResetOnceEmailStatus(tenantID, EmailOnceKey_POCEnd+EmailOnceKey_CustomerSuffix)
	if err != nil {
		log.Error("failed to reset once email", zap.Uint("tenantID", tenantID), zap.Error(err))
	}
	err = centralhandler.GetCentralHandler().ResetOnceEmailStatus(tenantID, EmailOnceKey_POCEnd+EmailOnceKey_ManagerSuffix)
	if err != nil {
		log.Error("failed to reset once email", zap.Uint("tenantID", tenantID), zap.Error(err))
	}

	err = centralhandler.GetCentralHandler().ResetOnceEmailStatus(tenantID, EmailOnceKey_POCEndBackupWillDelete+EmailOnceKey_CustomerSuffix)
	if err != nil {
		log.Error("failed to reset once email", zap.Uint("tenantID", tenantID), zap.Error(err))
	}
	err = centralhandler.GetCentralHandler().ResetOnceEmailStatus(tenantID, EmailOnceKey_POCEndBackupWillDelete+EmailOnceKey_ManagerSuffix)
	if err != nil {
		log.Error("failed to reset once email", zap.Uint("tenantID", tenantID), zap.Error(err))
	}

	err = centralhandler.GetCentralHandler().ResetOnceEmailStatus(tenantID, EmailOnceKey_POCCreditsEnd+EmailOnceKey_CustomerSuffix)
	if err != nil {
		log.Error("failed to reset once email", zap.Uint("tenantID", tenantID), zap.Error(err))
	}
	err = centralhandler.GetCentralHandler().ResetOnceEmailStatus(tenantID, EmailOnceKey_POCCreditsEnd+EmailOnceKey_ManagerSuffix)
	if err != nil {
		log.Error("failed to reset once email", zap.Uint("tenantID", tenantID), zap.Error(err))
	}

	for _, threshold := range PoCReminderFreePointsThresholds {
		emailOnceKey := fmt.Sprintf(EmailOnceKeyTemplate_POCReminder, threshold.IntPart())
		// for example, if the remaining points is 600, user should receive alert for 500, 250, but not 750, 1000
		// if the remain  ing points is 750, user should receive alert for 500, 250, but not 750, 1000, too.
		// so we clear the notification which is smaller

		if credits.Cmp(threshold) > 0 {
			err = centralhandler.GetCentralHandler().ResetOnceEmailStatus(tenantID, emailOnceKey+EmailOnceKey_CustomerSuffix)
			if err != nil {
				log.Error("failed to reset once email", zap.Uint("tenantID", tenantID), zap.Error(err))
			}
			err = centralhandler.GetCentralHandler().ResetOnceEmailStatus(tenantID, emailOnceKey+EmailOnceKey_ManagerSuffix)
			if err != nil {
				log.Error("failed to reset once email", zap.Uint("tenantID", tenantID), zap.Error(err))
			}
			log.Info("clear notification", zap.Uint("tenantID", tenantID), zap.String("emailOnceKey", emailOnceKey))
		}
	}

	log.Info("all types of poc will end email notification been refreshed", zap.Uint("tenantID", tenantID))

	return nil
}

func (m *TenantPlanModel) sendEmailAfterPocCreditsClosed(tenant *forms.Tenant, creditsPackage *forms.CreditsPackage) error {
	var event string
	var msg string
	var templateID string
	switch creditsPackage.ClosedReason {
	case entity.CreditsPackageClosedReason_Expired:
		event = "expired"
		msg = "expired on " + creditsPackage.ExpiredDate.Format(TimeFmtDTM) + " +00:00"
	case entity.CreditsPackageClosedReason_Exhausted:
		event = "exhausted"
		msg = "exhausted on " + creditsPackage.ClosedDate.Format(TimeFmtDTM) + " +00:00"
	case entity.CreditsPackageClosedReason_Reclaimed:
		event = "reclaimed"
		msg = "reclaimed on " + creditsPackage.ClosedDate.Format(TimeFmtDTM) + " +00:00"
	}

	vars := map[string]string{
		"event":     event,
		"event_msg": msg,
		"Org_ID":    strconv.FormatUint(uint64(tenant.TenantID), 10),
		"Org_name":  tenant.Name,
	}
	if tenant.Kind == forms.TenantKindExternal {
		templateID = TemplateID_PocCreditsExhaust
	} else {
		templateID = TemplateID_PocCreditsExhaust_Inner
	}

	err := centralhandler.GetCentralHandler().SendTenantAdminEmailOnce(tenant.TenantID, EmailOnceKey_POCCreditsEnd+EmailOnceKey_CustomerSuffix, templateID, vars)
	if err != nil {
		return err
	}

	err = centralhandler.GetCentralHandler().SendTenantManagerEmailOnce(tenant.TenantID, EmailOnceKey_POCCreditsEnd+EmailOnceKey_ManagerSuffix, templateID, vars)
	if err != nil {
		return err
	}

	return nil
}
