package workers

import (
	"strconv"
	"strings"
	"time"

	"github.com/pingcap/dbaas/billing2/config"
	"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/handler/workerhandler"
	"github.com/pingcap/dbaas/billing2/model/costexplorer"
	"github.com/pingcap/dbaas/billing2/model/payment/paymentsub"
	"github.com/pingcap/dbaas/billing2/utils/log"
	"github.com/pingcap/dbaas/billing2/utils/timeutils"
	"github.com/shopspring/decimal"
	"go.uber.org/zap"
)

const (
	SyncBillsWorkerName = "SyncMarketOrgBills"
)

func SyncMarketOrgBillsByTenant(tenantID uint) {

}

func SyncMarketOrgBills() {
	if !config.Get().GetBool(config.SyncMarketOrgBillsEnable) {
		return
	}
	now := timeutils.GetNow()
	log.Info("worker SyncMarketOrgBills start ", zap.Time("startTime", now))

	// get Market tenantId
	paymentModel := &paymentsub.PaymentModel{}
	tenantPaymentModels, err := paymentModel.GetAllPayableAndActivePaymentInfo()
	if err != nil {
		log.Error("SyncMarketOrgBills failed to get GetAllPayableAndActivePaymentInfo in worker.", zap.Error(err))
		return
	}
	tenants, err := centralhandler.GetCentralHandler().GetAllTenants()
	if err != nil {
		log.Error("SyncMarketOrgBills failed to get GetAllTenants in SyncBills worker.", zap.Error(err))
		return
	}
	tenantIDmMap := make(map[uint]bool, len(tenants))
	for _, tenant := range tenants {
		tenantIDmMap[tenant.TenantID] = true
	}

	// get running month
	invoiceHouse := config.Get().GetInt(config.Invoicehours)
	monthStartTime := timeutils.GetMonthDateForMonthlyWorkerByHours(now, invoiceHouse)
	workHandler := workerhandler.Get()
	marketPlacePaymentTypeMap := map[string]bool{
		entity.PaymentType_AWS:      true,
		entity.PaymentType_GCP:      true,
		entity.PaymentType_AZURE:    true,
		entity.PaymentType_AliCloud: true,
	}

	for _, tenantPaymentModel := range tenantPaymentModels {
		if tenantIDmMap[tenantPaymentModel.TenantID] {
			if marketPlacePaymentTypeMap[tenantPaymentModel.PaymentType] {
				workHandler.DedupeRun(tenantPaymentModel.TenantID, SyncBillsWorkerName, timeutils.MONTH, monthStartTime, DealAndSync)
			}
		} else {
			log.Info("SyncMarketOrgBills account no info, failed to Create Invoice ", zap.Uint("tenantID", tenantPaymentModel.TenantID), zap.Error(err))
		}
	}
	log.Info("worker SyncMarketOrgBills  end ", zap.Time("startTime", now), zap.Time("endTime", timeutils.GetNow()))
	return

}

func DealAndSync(tenantID uint, billsStart time.Time) (interface{}, error) {
	costExplorerModel := &costexplorer.CostExplorerModel{}
	billsEnd := billsStart.AddDate(0, 1, 0)
	//don't need billing config
	res, err := costExplorerModel.CalculateCost(tenantID, 0, &forms.CostExplorerQuery{
		StartDate: billsStart,
		EndDate:   billsEnd,
	})
	if err != nil {
		log.Info("SyncMarketOrgBills   costExplorerModel err ", zap.Uint("tenantID", tenantID), zap.Error(err))
		return nil, err
	}
	if res.InvoiceCostsCosts.CostPrice.Cmp(decimal.Zero) > 0 {
		isOnlySeverLess := false

		if len(res.InvoiceCostsCosts.KindCategoryUnitsCost) == 1 {
			if _, ok := res.InvoiceCostsCosts.KindCategoryUnitsCost[config.Get().GetString(config.WorkerServerlessShowKindName)]; ok {
				isOnlySeverLess = true
			}
		}

		syncParam := salesforcehandler.MonthBills{
			OrgId:           strconv.FormatUint(uint64(tenantID), 10),
			BillStartDate:   billsStart.Format("2006-01-02"),
			BillEndDate:     billsEnd.AddDate(0, 0, -1).Format("2006-01-02"),
			TotalCost:       res.InvoiceCostsCosts.CostPrice,
			IsOnlySeverLess: isOnlySeverLess,
		}

		//sync
		err = salesforcehandler.Get().SyncMarketUserMonthBills(syncParam)

		if err == nil {
			return syncParam, nil

		} else {
			log.Info("SyncMarketOrgBills salesforcehandler.SyncMarketUserMonthBills error", zap.Error(err), zap.Uint("TenantID", tenantID))
			return nil, err
		}

	} else {
		log.Info("SyncMarketOrgBills   costExplorerModel cost is 0 ", zap.Uint("tenantID", tenantID))
		return nil, nil
	}

}

func CostExplorerCacheWorker() {
	if !config.Get().GetBool(config.CostExplorerCacheWorkerEnable) {
		return
	}
	tenantIDSlice := config.Get().GetStringSlice(config.MonthlyBillsCacheList)
	if len(tenantIDSlice) == 0 {
		return
	}
	now := timeutils.GetNow()

	log.Info("worker CostExplorerCache start ", zap.Time("startTime", now))
	model := &costexplorer.CostExplorerModel{}

	for ti := 0; ti < 13; ti++ {
		m := now.AddDate(0, -ti, 0)

		for _, combinedID := range tenantIDSlice {
			parts := strings.Split(combinedID, "-")
			if len(parts) != 2 {
				log.Error("invalid tenant config format", zap.String("val", combinedID))
				continue
			}
			tID64, err1 := strconv.ParseUint(parts[0], 10, 64)
			bID64, err2 := strconv.ParseUint(parts[1], 10, 64)

			if err1 != nil || err2 != nil {
				log.Error("failed to parse ID to uint", zap.String("val", combinedID), zap.Error(err1), zap.Error(err2))
				continue
			}
			tenantID := uint(tID64)
			billingConfigID := uint(bID64)

			_, err := model.GetMonthlyCostFromCache(tenantID, billingConfigID, m.Year(), int(m.Month()))
			if err == nil {
				log.Info("CostExplorerCache cache already exists, skipping",
					zap.String("tenantID", combinedID),
					zap.Int("year", m.Year()),
					zap.Int("month", int(m.Month())))
				continue
			}

			err = model.SetMonthlyCostToCache(tenantID, billingConfigID, m.Year(), int(m.Month()))
			if err != nil {
				log.Error("CostExplorerCacheWorker failed to SetMonthlyCostToCache  ", zap.String("val", combinedID), zap.Error(err))
			}
			log.Info("CostExplorerCache SetMonthlyCostToCache ", zap.String("tenantID", combinedID), zap.Int("year", m.Year()), zap.Int("month", int(m.Month())))
		}
	}
	log.Info("worker CostExplorerCache end ", zap.Time("startTime", now), zap.Time("endTime", timeutils.GetNow()))

}
