package service

import (
	"bytes"
	"context"
	"database/sql"
	"encoding/json"
	"finance-portal/pkg/errors"
	"fmt"
	"math"
	"strconv"
	"strings"
	"time"

	"cloud.google.com/go/bigquery"
	"github.com/google/uuid"
	"google.golang.org/api/iterator"

	"finance-portal/internal/dataplatform/store"
	"finance-portal/model"
	"finance-portal/pkg/log"
)

type OrgOrderSrv interface {
	StartOrgOrder(ctx context.Context)
	OrgOrder(ctx context.Context) error
}

type orgOrderService struct {
	store store.Factory
}

var _ OrgOrderSrv = (*orgOrderService)(nil)

func newOrgOrder(str *service) *orgOrderService {
	return &orgOrderService{store: str.store}
}

const (
	GcpProject          = "gcp-digital-bi"
	ApprovalCode        = "AF5C45B6-6394-441B-B0E8-8853EC4F04C4"
	defaultUserLarkID   = "61c9b9df"
	defaultUserOpLarkID = "ou_df0ff4197ab5d3098fa52ffc2a8a6ad7"
	bitableUrl          = "https://pingcap.feishu.cn/base/PyEFbxv38aQomcs9PcxcgdEgnpf?table=tblXrhy8ihhlMwMA&view=vewEdAW0nH"
	bucketName          = "digitalplatform"
	fileName            = "org_order/org_order.csv"
	fakeNumber          = "fake_"
	apToken             = "PyEFbxv38aQomcs9PcxcgdEgnpf"
	orderTableId        = "tbl3pMs0ILDUOFjC"
	revrecTableId       = "tblcvNdfA7ijv9Rb"
	collectionTableId   = "tblXrhy8ihhlMwMA"
	resTableId          = "tblobOmqIkoZyTOK"
)

var (
	comSrv    *commonService
	larkToken string
)

func (oos *orgOrderService) StartOrgOrder(ctx context.Context) {
	log.L(ctx).Info("StartOrgOrder Called.")

	err := oos.OrgOrder(ctx)
	if err != nil {
		log.L(ctx).Errorf("Failed to run org order: %v", err)

		_ = FreshRunStatus(ctx, 1, err)

		return
	}

	_ = FreshRunStatus(ctx, 0, nil)

	return
}

func (oos *orgOrderService) OrgOrder(ctx context.Context) error {
	log.L(ctx).Info("OrgOrder Called.")

	bqSrv, err := bigquery.NewClient(ctx, GcpProject)
	if err != nil {
		log.L(ctx).Errorf("Failed to create bigquery client: %v", err)

		return err
	}

	comSrv = &commonService{store: oos.store}
	larkToken, _ = tokenCache.GetToken(ctx, "lark")

	err = oos.saveOrderData(ctx, bqSrv)
	if err != nil {
		return err
	}

	err = oos.saveAccountData(ctx, bqSrv)
	if err != nil {
		return err
	}

	//records, err := FetchMonthlyOrderInfo(ctx, bqSrv, true)
	//if err != nil {
	//	log.L(ctx).Errorf("Failed to fetch monthly order info: %v", err)
	//
	//	return err
	//}

	//uniqueRds := records
	//orders, revrecs, collections, err := oos.getCreatedInfo(ctx, uniqueRds)
	//if err != nil {
	//	return err
	//}

	revnue, orgCollection, err := FetchMonthlyOrderInfoV2NotMerge(ctx, bqSrv, true)
	if err != nil {
		log.L(ctx).Errorf("Failed to fetch monthly order info: %v", err)

		return err
	}

	orders, revrecs, collections, err := oos.getCreatedInfoV2(ctx, revnue, orgCollection)
	if err != nil {
		return err
	}

	if len(orders) == 0 && len(revrecs) == 0 && len(collections) == 0 {
		log.L(ctx).Info("No new records need to be created.")

		return nil
	}

	//err = oos.freshBitable(ctx, orders, revrecs, collections)
	//if err != nil {
	//	return err
	//}

	log.L(ctx).Info("OrgOrder success")

	return nil
}

func (oos *orgOrderService) getCreatedInfo(ctx context.Context, records []*model.OrgBq) ([]model.CloudOrder, []model.CloudRevrec, []model.CloudCollection, error) {
	createOrders := make([]model.CloudOrder, 0)
	createRevrecs := make([]model.CloudRevrec, 0)
	createCollections := make([]model.CloudCollection, 0)

	orderLists, _ := oos.store.BqOrder().List(ctx)
	accountList, _ := oos.store.BqAccount().List(ctx)
	orderMap := make(map[string]string)
	accountMap := make(map[string]string)
	for _, order := range orderLists {
		orderMap[order.OrderName] = order.OrderID
	}
	for _, account := range accountList {
		accountMap[account.AccountName] = account.AccountID
	}

	for _, record := range records {
		autoType := ""
		fkNum, _ := uuid.NewUUID()

		record.NewOrderAmount.Float64 = roundToTwoDecimals(record.NewOrderAmount.Float64)
		if record.NeedNewOrder.StringVal == "yes" {
			createOrders = append(createOrders, model.CloudOrder{
				CreatedAt:        time.Now(),
				UpdatedAt:        sql.NullTime{Time: time.Now(), Valid: true},
				OrderStartDate:   DateToString(record.NewOrderStartDate),
				OrderEndDate:     DateToString(record.NewOrderEndDate),
				NetFee:           fmt.Sprintf("%.2f", record.NewOrderAmount.Float64),
				AccountID:        accountMap[record.AccountID.StringVal],
				Tax:              TrimSplit(record.StripeTaxRate.StringVal)[0],
				Status:           "Activated",
				BillMonth:        record.BillMonth.StringVal,
				OriginalTenantID: record.OriginalTenantID.StringVal,
				AdjustTenantID:   record.AdjustTenantID.StringVal,
				AccountNumber:    record.AccountID.StringVal,
				AccountName:      record.AccountName.StringVal,
				Sync:             0,
				FakeNumber:       fkNum.String(),
				RowLock:          false,
			})
			createRevrecs = append(createRevrecs, conRevrec(0, "", fkNum.String(), record))

			autoType += "1"
		}
		if record.RevenueAmount.Valid {
			createRevrecs = append(createRevrecs, conRevrec(1, record.OrderNumber.StringVal, orderMap[record.OrderNumber.StringVal], record))

			autoType += "2"
		}
		if record.RevenueAmount2.Valid {
			createRevrecs = append(createRevrecs, conRevrec(2, record.OrderNumber2.StringVal, orderMap[record.OrderNumber2.StringVal], record))

			autoType += "3"
		}

		if record.CollectionsFrom.StringVal == "Stripe" && strings.Contains(autoType, "1") {
			createCollections = append(createCollections, conCollections(0, "", fkNum.String(), record)...)

			autoType += "4"
		}
		if record.CollectionsFrom.StringVal == "Stripe" && strings.Contains(autoType, "2") {
			createCollections = append(createCollections, conCollections(1, record.OrderNumber.StringVal, orderMap[record.OrderNumber.StringVal], record)...)

			autoType += "5"
		}
		if record.CollectionsFrom.StringVal == "Stripe" && strings.Contains(autoType, "3") {
			createCollections = append(createCollections, conCollections(2, record.OrderNumber2.StringVal, orderMap[record.OrderNumber2.StringVal], record)...)

			autoType += "6"
		}
	}

	createOrders, createRevrecs, createCollections, err := filterDupRecords(ctx, createOrders, createRevrecs, createCollections)
	if err != nil {
		log.L(ctx).Errorf("Failed to filter duplicate records: %v", err)

		return nil, nil, nil, err
	}

	// check revrec amount balance
	err = checkRevrecBalance(ctx, createRevrecs)
	if err != nil {
		return nil, nil, nil, err
	}

	createOrders, err = oos.store.CloudOrder().DeleteAndSave(ctx, createOrders)
	if err != nil {
		log.L(ctx).Errorf("Failed to save created orders: %v", err)

		return nil, nil, nil, err
	}

	createRevrecs, err = oos.store.CloudRevrec().DeleteAndSave(ctx, createRevrecs)
	if err != nil {
		log.L(ctx).Errorf("Failed to save created revrecs: %v", err)

		return nil, nil, nil, err
	}

	createCollections, err = oos.store.CloudCollection().DeleteAndSave(ctx, createCollections)
	if err != nil {
		log.L(ctx).Errorf("Failed to save created collections: %v", err)

		return nil, nil, nil, err
	}

	return createOrders, createRevrecs, createCollections, nil
}

func conRevrec(typ int, odNum, order string, record *model.OrgBq) model.CloudRevrec {

	var revenueAmount, adjustedShortfallAmount bigquery.NullFloat64

	adjustedRevenueAmount := record.NewOrderAmount

	// 保留两位小数
	adjustedStarterAmount := record.NewOrderStarterAmount
	adjustedDedicatedAmount := record.NewOrderDedicatedAmount
	adjustedSupportAmount := record.NewOrderSupportAmount
	adjustedEssentialAmount := record.NewOrderEssentialAmount
	adjustedPremiumAmount := record.NewOrderPremiumAmount
	revenueAmount = record.NewOrderAmount

	var creditAmount bigquery.NullFloat64

	if typ == 1 {
		adjustedRevenueAmount = record.AdjustedRevenueAmount
		adjustedStarterAmount = record.AdjustedStarterAmount
		adjustedDedicatedAmount = record.AdjustedDedicatedAmount
		adjustedSupportAmount = record.AdjustedSupportAmount
		adjustedEssentialAmount = record.AdjustedEssentialAmount
		adjustedPremiumAmount = record.AdjustedPremiumAmount

		revenueAmount = record.RevenueAmount
		adjustedShortfallAmount = record.AdjustedShortfallAmount
		creditAmount = record.CreditAmount
	}
	if typ == 2 {
		adjustedRevenueAmount = record.AdjustedRevenueAmount2
		adjustedStarterAmount = record.AdjustedStarterAmount2
		adjustedDedicatedAmount = record.AdjustedDedicatedAmount2
		adjustedSupportAmount = record.AdjustedSupportAmount2
		adjustedEssentialAmount = record.AdjustedEssentialAmount2
		adjustedPremiumAmount = record.AdjustedPremiumAmount2

		revenueAmount = record.RevenueAmount2
		adjustedShortfallAmount = record.AdjustedShortfallAmount2
		creditAmount = record.CreditAmount2
	}

	// 四舍五入函数，保留两位小数

	// 进行四舍五入
	adjustedStarterAmount.Float64 = roundToTwoDecimals(adjustedStarterAmount.Float64)
	adjustedDedicatedAmount.Float64 = roundToTwoDecimals(adjustedDedicatedAmount.Float64)
	adjustedSupportAmount.Float64 = roundToTwoDecimals(adjustedSupportAmount.Float64)
	adjustedEssentialAmount.Float64 = roundToTwoDecimals(adjustedEssentialAmount.Float64)
	adjustedPremiumAmount.Float64 = roundToTwoDecimals(adjustedPremiumAmount.Float64)
	revenueAmount.Float64 = roundToTwoDecimals(revenueAmount.Float64)
	adjustedRevenueAmount.Float64 = roundToTwoDecimals(adjustedRevenueAmount.Float64)
	adjustedShortfallAmount.Float64 = roundToTwoDecimals(adjustedShortfallAmount.Float64)

	return model.CloudRevrec{
		CreatedAt:      time.Now(),
		UpdatedAt:      sql.NullTime{Time: time.Now(), Valid: true},
		Date:           DateToString(record.EndDate),
		BillingEndDate: DateToString(record.EndDate),
		// 小数点后2位
		AdjustedRevenueAmount:   fmt.Sprintf("%.2f", adjustedRevenueAmount.Float64),
		Status:                  "Confirmed",
		BillMonth:               record.BillMonth.StringVal,
		OriginalTenantID:        record.OriginalTenantID.StringVal,
		AdjustTenantID:          record.AdjustTenantID.StringVal,
		AccountNumber:           record.AccountID.StringVal,
		AccountName:             record.AccountName.StringVal,
		Sync:                    0,
		Type:                    typ,
		Order:                   order,
		OrderNumber:             odNum,
		AdjustedStarterAmount:   fmt.Sprintf("%.2f", adjustedStarterAmount.Float64),
		AdjustedDedicatedAmount: fmt.Sprintf("%.2f", adjustedDedicatedAmount.Float64),
		AdjustedSupportAmount:   fmt.Sprintf("%.2f", adjustedSupportAmount.Float64),
		AdjustedEssentialAmount: fmt.Sprintf("%.2f", adjustedEssentialAmount.Float64),
		AdjustedPremiumAmount:   fmt.Sprintf("%.2f", adjustedPremiumAmount.Float64),
		RevenueAmount:           fmt.Sprintf("%.2f", revenueAmount.Float64),
		AdjustedShortfallAmount: fmt.Sprintf("%.2f", adjustedShortfallAmount.Float64),
		CreditAmount:            fmt.Sprintf("%.2f", creditAmount.Float64),
		CurrencyIsoCode:         "USD",
		RowLock:                 false,
		ExchangeRateToUse:       "1",
	}
}

func conCollections(typ int, odNum, order string, record *model.OrgBq) []model.CloudCollection {
	createdCollections := make([]model.CloudCollection, 0)

	// stripAmounts := TrimSplit(record.StripeAmount.StringVal)
	StripeAmountIncludingTaxs := TrimSplit(record.StripeAmountIncludingTax.StringVal)
	StripeInvoiceNumbers := TrimSplit(record.StripeInvoiceNumber.StringVal)
	StripeInvoiceFinalizedDates := TrimSplit(record.StripeInvoiceFinalizedDate.StringVal)
	StripeInvoicePaids := TrimSplit(record.StripeInvoicePaid.StringVal)
	StripeInvoicePaidDates := TrimSplit(record.StripeInvoicePaidDate.StringVal)
	StripeInvoices := TrimSplit(record.StripeInvoice.StringVal)
	StripeTax := TrimSplit(record.StripeTaxRate.StringVal)
	for index, stripeTax := range StripeTax {
		if stripeTax == "" {
			StripeTax[index] = "0"
		}
	}

	for index, invoiceNumber := range StripeInvoiceNumbers {

		createdCollections = append(createdCollections, model.CloudCollection{
			CreatedAt:         time.Now(),
			UpdatedAt:         sql.NullTime{Time: time.Now(), Valid: true},
			PayableAmount:     StripeAmountIncludingTaxs[index],
			InvoiceNumber:     invoiceNumber,
			PayableDate:       StripeInvoiceFinalizedDates[index],
			ActualBillingDate: StripeInvoiceFinalizedDates[index],
			CollectionsDate:   TripleExpression(StripeInvoicePaids[index], "true", StripeInvoicePaidDates[index], ""),
			CollectionStatus:  TripleExpression(StripeInvoicePaids[index], "true", "Received", "Uncollected"),
			CollectionsAmount: TripleExpression(StripeInvoicePaids[index], "true", StripeAmountIncludingTaxs[index], ""),
			StripeTaxRate:     TripleExpression(StripeInvoicePaids[index], "true", StripeTax[index], ""),
			Files:             StripeInvoices[index],
			InvoiceStatus:     "Issued",
			BillMonth:         record.BillMonth.StringVal,
			OriginalTenantID:  record.OriginalTenantID.StringVal,
			AdjustTenantID:    record.AdjustTenantID.StringVal,
			CollectionType:    "Stripe",
			AccountNumber:     record.AccountID.StringVal,
			AccountName:       record.AccountName.StringVal,
			StripePaid:        StripeInvoicePaids[index],
			Sync:              0,
			Type:              typ,
			Order:             order,
			OrderNumber:       odNum,
			CollectedDate:     TripleExpression(StripeInvoicePaids[index], "true", StripeInvoicePaidDates[index], ""),
			RowLock:           false,
		})
	}

	return createdCollections
}

func (oos *orgOrderService) freshBitable(ctx context.Context, cOrders []model.CloudOrder, cRevrecs []model.CloudRevrec, cCollections []model.CloudCollection) error {
	log.L(ctx).Info("freshBitable Called.")

	lastMonth := GetLastMonth()
	co, _ := oos.store.CloudOrder().ListByMonth(ctx, lastMonth, -1)
	cr, _ := oos.store.CloudRevrec().ListByMonth(ctx, lastMonth, -1)
	cc, _ := oos.store.CloudCollection().ListByMonth(ctx, lastMonth, -1)
	cOrders = make([]model.CloudOrder, 0)
	cRevrecs = make([]model.CloudRevrec, 0)
	cCollections = make([]model.CloudCollection, 0)
	for _, order := range co {
		cOrders = append(cOrders, *order)
	}
	for _, revrec := range cr {
		cRevrecs = append(cRevrecs, *revrec)
	}
	for _, collection := range cc {
		cCollections = append(cCollections, *collection)
	}

	err := getAndDelBiRecords(ctx, orderTableId)
	if err != nil {
		return err
	}

	err = getAndDelBiRecords(ctx, revrecTableId)
	if err != nil {
		return err
	}

	err = getAndDelBiRecords(ctx, collectionTableId)
	if err != nil {
		return err
	}

	err = writeBiOrders(ctx, cOrders)
	if err != nil {
		return err
	}

	list, err := oos.store.BqOrder().List(ctx)
	if err != nil {
		log.L(ctx).Errorf("Failed to get order data from db: %v", err)

		return err
	}
	orderMap := make(map[string]string)
	for _, order := range list {
		orderMap[order.OrderID] = order.OrderName
	}
	err = writeBiRevrecs(ctx, cRevrecs, orderMap)
	if err != nil {
		return err
	}

	err = writeBiCollections(ctx, cCollections, orderMap)
	if err != nil {
		return err
	}

	log.L(ctx).Info("freshBitable success")

	return nil
}

type Item struct {
	RecordId string `json:"record_id"`
}
type respData struct {
	Items     []Item `json:"items"`
	PageToken string `json:"page_token"`
	Total     int    `json:"total"`
	HasMore   bool   `json:"has_more"`
}

func getAndDelBiRecords(ctx context.Context, tableId string) error {
	url := "/bitable/v1/apps/" + apToken + "/tables/" + tableId + "/records"
	pageToken := ""
	delRecords := make([]string, 0)
	delRecords = append(delRecords, "")
	for {
		params := map[string]string{
			"page_size":  "500",
			"page_token": pageToken,
		}

		resp, err := comSrv.GetDataByReqLarkAPI(ctx, "GET", LarkOpenAPIBase+url, nil, params, larkToken)
		if err != nil {
			return err
		}

		var resData respData
		err = json.Unmarshal(resp, &resData)
		if err != nil {
			log.L(ctx).Errorf("unmarshal response error: %v", err)

			return err
		}

		for _, item := range resData.Items {
			delRecords = append(delRecords, item.RecordId)
		}

		if resData.PageToken == "" {
			break
		} else {
			pageToken = resData.PageToken
		}
	}

	url = "/bitable/v1/apps/" + apToken + "/tables/" + tableId + "/records/batch_delete"
	body := map[string]interface{}{
		"records": delRecords,
	}

	marshalBody, _ := json.Marshal(body)

	_, err := comSrv.GetDataByReqLarkAPI(ctx, "POST", LarkOpenAPIBase+url, bytes.NewReader(marshalBody), nil, larkToken)
	if err != nil {
		return err
	}

	return nil
}

type BiItem struct {
	Fields   interface{} `json:"fields"`
	ID       string      `json:"id,omitempty" `
	RecordID string      `json:"record_id,omitempty"`
}

func writeBiOrders(ctx context.Context, orders []model.CloudOrder) error {
	log.L(ctx).Info("writeBiOrders Called.")
	if len(orders) == 0 {
		return nil
	}

	url := "/bitable/v1/apps/" + apToken + "/tables/" + orderTableId + "/records/batch_create"
	records := make([]BiItem, 0)
	for _, order := range orders {
		records = append(records, BiItem{
			Fields: model.BiOrder{
				OrderStartDate:   order.OrderStartDate,
				OrderEndDate:     order.OrderEndDate,
				ServiceStartDate: order.OrderStartDate,
				ServiceEndDate:   order.OrderEndDate,
				Tax:              order.Tax,
				Status:           order.Status,
				NetFees:          order.NetFee,
				BillMonth:        order.BillMonth,
				OriginalTenantID: order.OriginalTenantID,
				AdjustTenantID:   order.AdjustTenantID,
				AccountNumber:    order.AccountNumber,
				AccountName:      order.AccountName,
				OrderNumber:      order.NewOrderNumber,
				CreatedAt:        order.CreatedAt.Format("2006-01-02 15:04:05"),
			},
		})
	}
	body := map[string]interface{}{
		"records": records,
	}

	marshalBody, _ := json.Marshal(body)

	_, err := comSrv.GetDataByReqLarkAPI(ctx, "POST", LarkOpenAPIBase+url, bytes.NewReader(marshalBody), nil, larkToken)
	if err != nil {
		return err
	}

	log.L(ctx).Info("writeBiOrders success")

	return nil
}

func writeBiRevrecs(ctx context.Context, revrecs []model.CloudRevrec, orderMap map[string]string) error {
	log.L(ctx).Info("writeBiRevrecs Called.")
	if len(revrecs) == 0 {
		return nil
	}

	url := "/bitable/v1/apps/" + apToken + "/tables/" + revrecTableId + "/records/batch_create"
	records := make([]BiItem, 0)
	for _, revrec := range revrecs {
		if revrec.Type != 0 {
			revrec.Order = orderMap[revrec.Order]
		}
		records = append(records, BiItem{
			Fields: model.BiRevrec{
				Date:                    revrec.Date,
				TiDBCloudBillingEndDate: revrec.BillingEndDate,
				Status:                  revrec.Status,
				BillMonth:               revrec.BillMonth,
				OriginalTenantID:        revrec.OriginalTenantID,
				AdjustTenantID:          revrec.AdjustTenantID,
				AccountNumber:           revrec.AccountNumber,
				AccountName:             revrec.AccountName,
				OrderNumber:             revrec.OrderNumber,
				RevrecNumber:            revrec.RevrecNumber,
				CreatedAt:               revrec.CreatedAt.Format("2006-01-02 15:04:05"),
				AdjustedSupportAmount:   revrec.AdjustedSupportAmount,
				AdjustedStarterAmount:   revrec.AdjustedStarterAmount,
				AdjustedDedicatedAmount: revrec.AdjustedRevenueAmount,
				AdjustedPremiumAmount:   revrec.AdjustedPremiumAmount,
				CreditAmount:            revrec.CreditAmount,
				RevenueAmount:           revrec.RevenueAmount,
				AdjustedShortfallAmount: revrec.AdjustedShortfallAmount,
			},
		})
	}
	body := map[string]interface{}{
		"records": records,
	}

	marshalBody, _ := json.Marshal(body)

	_, err := comSrv.GetDataByReqLarkAPI(ctx, "POST", LarkOpenAPIBase+url, bytes.NewReader(marshalBody), nil, larkToken)
	if err != nil {
		return err
	}

	log.L(ctx).Infof("writeBiRevrecs success: %v")

	return nil
}

func writeBiCollections(ctx context.Context, collections []model.CloudCollection, orderMap map[string]string) error {
	log.L(ctx).Info("writeBiCollections Called.")
	if len(collections) == 0 {
		return nil
	}

	url := "/bitable/v1/apps/" + apToken + "/tables/" + collectionTableId + "/records/batch_create"
	records := make([]BiItem, 0)
	for _, collection := range collections {
		if collection.Type != 0 {
			collection.Order = orderMap[collection.Order]
		}
		records = append(records, BiItem{
			Fields: model.BiCollection{
				BillMonth:         collection.BillMonth,
				OriginalTenantID:  collection.OriginalTenantID,
				AdjustTenantID:    collection.AdjustTenantID,
				AccountNumber:     collection.AccountNumber,
				AccountName:       collection.AccountName,
				OrderNumber:       collection.OrderNumber,
				CollectionNumber:  collection.CollectionNumber,
				InvoiceNumber:     collection.InvoiceNumber,
				PayableAmount:     collection.PayableAmount,
				PayableDate:       collection.PayableDate,
				ActualBillingDate: collection.ActualBillingDate,
				CollectionsDate:   collection.CollectionsDate,
				CollectionStatus:  collection.CollectionStatus,
				CollectionsAmount: collection.CollectionsAmount,
				Files:             collection.Files,
				InvoiceStatus:     collection.InvoiceStatus,
				StripePaid:        collection.StripePaid,
				CreatedAt:         collection.CreatedAt.Format("2006-01-02 15:04:05"),
			},
		})
	}
	body := map[string]interface{}{
		"records": records,
	}

	marshalBody, _ := json.Marshal(body)

	_, err := comSrv.GetDataByReqLarkAPI(ctx, "POST", LarkOpenAPIBase+url, bytes.NewReader(marshalBody), nil, larkToken)
	if err != nil {
		return err
	}

	log.L(ctx).Infof("writeBiCollections success: %v")

	return nil
}

func FreshRunStatus(ctx context.Context, tp int, err error) error {
	log.L(ctx).Info("FreshRunStatus Called.")

	var sus, msg string
	if tp == 0 {
		sus = "成功"
	} else {
		sus = "失败"
	}
	if err != nil {
		msg = err.Error()
	}

	url := "/bitable/v1/apps/" + apToken + "/tables/" + resTableId + "/records"

	params := map[string]string{
		"page_size":  "10",
		"page_token": "",
	}

	recordID := ""

	resp, err := comSrv.GetDataByReqLarkAPI(ctx, "GET", LarkOpenAPIBase+url, nil, params, larkToken)
	if err != nil {
		return err
	}

	var resData respData
	err = json.Unmarshal(resp, &resData)
	if err != nil {
		log.L(ctx).Errorf("unmarshal response error: %v", err)

		return err
	}

	for _, item := range resData.Items {
		recordID = item.RecordId
	}

	url += "/" + recordID
	loc, _ := time.LoadLocation("Asia/Shanghai")
	body := BiItem{
		Fields: model.RunStatus{
			RunStatus: sus,
			RunTime:   time.Now().In(loc).Format("2006-01-02 15:04:05"),
			Message:   msg,
		},
	}

	marshalBody, _ := json.Marshal(body)

	_, err = comSrv.GetDataByReqLarkAPI(ctx, "PUT", LarkOpenAPIBase+url, bytes.NewReader(marshalBody), nil, larkToken)
	if err != nil {
		log.L(ctx).Errorf("error when update run status, err: %s", err.Error())

		return err
	}

	return nil
}

func filterUniqueRecord(records []*model.OrgBq) []*model.OrgBq {
	recordMap := make(map[string]int)
	for _, record := range records {
		recordMap[record.AdjustTenantID.StringVal] += 1
	}

	uniqueRecords := make([]*model.OrgBq, 0)
	for _, record := range records {
		if recordMap[record.AdjustTenantID.StringVal] == 2 && record.AdjustTenantID.StringVal == record.OriginalTenantID.StringVal {
			uniqueRecords = append(uniqueRecords, record)
			fmt.Println(record.AdjustTenantID.StringVal)
		} else if recordMap[record.AdjustTenantID.StringVal] == 1 {
			uniqueRecords = append(uniqueRecords, record)
		}
	}

	return uniqueRecords
}

func (oos *orgOrderService) saveOrderData(ctx context.Context, bqSrv *bigquery.Client) error {
	sql := "SELECT id,name,is_deleted FROM `gcp-digital-bi.salesforce.order` "

	query := bqSrv.Query(sql)
	it, err := query.Read(ctx)
	if err != nil {
		log.L(ctx).Errorf("Failed to read query: %v", err)

		return err
	}
	orderBqr := make([]model.OrderBq, 0)

	for {
		var value model.OrderBq
		err := it.Next(&value)
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.L(ctx).Errorf("Failed to read row: %v", err)

			return err
		}

		if !value.IsDeleted.Bool {
			orderBqr = append(orderBqr, value)
		}
	}

	ordeDb, err := oos.store.BqOrder().List(ctx)
	if err != nil {
		log.L(ctx).Errorf("Failed to get order data from db: %v", err)

		return err
	}

	orderMap := make(map[string]model.OrderBq)
	for _, order := range orderBqr {
		orderMap[order.ID.StringVal] = order
	}

	addRecords := make([]model.BqOrder, 0)
	upRecords := make([]model.BqOrder, 0)
	delRecords := make([]model.BqOrder, 0)
	for _, order := range ordeDb {
		v, ok := orderMap[order.OrderID]
		if !ok {
			delRecords = append(delRecords, order)
		}
		if ok && v.Name.StringVal != order.OrderName {
			order.OrderName = v.Name.StringVal
			upRecords = append(upRecords, order)
		}
		delete(orderMap, order.OrderID)
	}

	for _, order := range orderMap {
		addRecords = append(addRecords, model.BqOrder{
			OrderID:   order.ID.StringVal,
			OrderName: order.Name.StringVal,
		})
	}

	err = oos.store.BqOrder().Save(addRecords, upRecords, delRecords)
	if err != nil {
		log.L(ctx).Errorf("Failed to save order data: %v", err)

		return err
	}

	return nil
}

func (oos *orgOrderService) saveAccountData(ctx context.Context, bqSrv *bigquery.Client) error {
	sql := "SELECT id,account_id_c FROM `gcp-digital-bi.salesforce.account` "

	query := bqSrv.Query(sql)
	it, err := query.Read(ctx)
	if err != nil {
		log.L(ctx).Errorf("Failed to read query: %v", err)

		return err
	}
	accountBqr := make([]model.AccountBq, 0)

	for {
		var value model.AccountBq
		err := it.Next(&value)
		if err == iterator.Done {
			break
		}
		if err != nil {
			log.L(ctx).Errorf("Failed to read row: %v", err)

			return err
		}

		accountBqr = append(accountBqr, value)
	}

	accountDb, err := oos.store.BqAccount().List(ctx)
	if err != nil {
		log.L(ctx).Errorf("Failed to get order data from db: %v", err)

		return err
	}

	accountMap := make(map[string]model.AccountBq)
	for _, account := range accountBqr {
		accountMap[account.ID.StringVal] = account
	}

	addRecords := make([]model.BqAccount, 0)
	upRecords := make([]model.BqAccount, 0)
	delRecords := make([]model.BqAccount, 0)
	for _, account := range accountDb {
		v, ok := accountMap[account.AccountID]
		if !ok {
			delRecords = append(delRecords, account)
		}
		if ok && v.AccountName.StringVal != account.AccountName {
			account.AccountName = v.AccountName.StringVal
			upRecords = append(upRecords, account)
		}
		delete(accountMap, account.AccountID)
	}

	for _, account := range accountMap {
		addRecords = append(addRecords, model.BqAccount{
			AccountID:   account.ID.StringVal,
			AccountName: account.AccountName.StringVal,
		})
	}

	err = oos.store.BqAccount().Save(addRecords, upRecords, delRecords)
	if err != nil {
		log.L(ctx).Errorf("Failed to save order data: %v", err)

		return err
	}

	return nil
}

func roundToTwoDecimals(n float64) float64 {
	// 直接使用 math.Round 进行四舍五入并保留 2 位小数
	rounded := math.Round(n*100) / 100

	// 处理 -0.00 的情况
	if rounded == 0 {
		return 0
	}

	return rounded
}

func filterDupRecords(ctx context.Context, orders []model.CloudOrder, revrecs []model.CloudRevrec, collections []model.CloudCollection) ([]model.CloudOrder, []model.CloudRevrec, []model.CloudCollection, error) {
	// 去重Orders (billMonth + AdjustedTenantID)
	orderKeys := make(map[string]struct{})
	filteredOrders := make([]model.CloudOrder, 0, len(orders))
	for _, o := range orders {
		key := fmt.Sprintf("%s|%s", o.BillMonth, o.AdjustTenantID)
		if _, exists := orderKeys[key]; !exists {
			orderKeys[key] = struct{}{}
			filteredOrders = append(filteredOrders, o)
		}
	}

	// 去重Revrecs (billMonth + AdjustedTenantID + Type)
	revrecKeys := make(map[string]struct{})
	filteredRevrecs := make([]model.CloudRevrec, 0, len(revrecs))
	for _, r := range revrecs {
		key := fmt.Sprintf("%s|%s|%d", r.BillMonth, r.AdjustTenantID, r.Type)
		if _, exists := revrecKeys[key]; !exists {
			revrecKeys[key] = struct{}{}
			filteredRevrecs = append(filteredRevrecs, r)
		}
	}

	// 去重Collections (billMonth + AdjustedTenantID + Type + InvoiceNumber)
	collectionKeys := make(map[string]struct{})
	filteredCollections := make([]model.CloudCollection, 0, len(collections))
	for _, c := range collections {
		key := fmt.Sprintf("%s|%s|%d|%s",
			c.BillMonth,
			c.AdjustTenantID,
			c.Type,
			c.InvoiceNumber,
		)
		if _, exists := collectionKeys[key]; !exists {
			collectionKeys[key] = struct{}{}
			filteredCollections = append(filteredCollections, c)
		}
	}

	return filteredOrders, filteredRevrecs, filteredCollections, nil
}

func checkRevrecBalance(ctx context.Context, revrecs []model.CloudRevrec) error {
	log.L(ctx).Info("checkRevrecBalance Called.")

	const tolerance = 0.02

	for _, revrec := range revrecs {
		starterAmount, _ := strconv.ParseFloat(revrec.AdjustedStarterAmount, 64)
		dedicatedAmount, _ := strconv.ParseFloat(revrec.AdjustedDedicatedAmount, 64)
		supportAmount, _ := strconv.ParseFloat(revrec.AdjustedSupportAmount, 64)
		essentialsAmount, _ := strconv.ParseFloat(revrec.AdjustedEssentialAmount, 64)
		shortfullAmount, _ := strconv.ParseFloat(revrec.AdjustedShortfallAmount, 64)
		premiumAmount, _ := strconv.ParseFloat(revrec.AdjustedPremiumAmount, 64)
		lakeAmount, _ := strconv.ParseFloat(revrec.AdjustedLakeAmount, 64)
		byocAmount, _ := strconv.ParseFloat(revrec.AdjustedByocAmount, 64)
		revenueAmount, _ := strconv.ParseFloat(revrec.AdjustedRevenueAmount, 64)

		sum := roundToTwoDecimals(starterAmount + dedicatedAmount + supportAmount + essentialsAmount + shortfullAmount + premiumAmount + lakeAmount + byocAmount)
		difference := math.Abs(sum - revenueAmount)

		if difference > tolerance {
			log.L(ctx).Errorf("Revrec balance error tenant id: %s, difference: %.2f", revrec.AdjustTenantID, difference)
			return errors.New("Revrec balance error")
		}
	}

	return nil
}
