package mysql

import (
	"context"
	"fmt"
	"strings"

	"gorm.io/gorm"

	"finance-portal/model"
)

type cloudRevrec struct {
	db *gorm.DB
}

func newCloudRevrec(ds *datastore) *cloudRevrec {
	return &cloudRevrec{db: ds.db}
}

func (c *cloudRevrec) List(_ context.Context) (records []model.CloudRevrec, err error) {
	err = c.db.Find(&records).Error
	return
}

func (c *cloudRevrec) ListByMonth(_ context.Context, billMonth string, sync int) (records []*model.CloudRevrec, err error) {
	tx := c.db.Where("bill_month = ?", billMonth)
	if sync != -1 {
		tx = tx.Where("sync = ?", sync)
	}
	err = tx.Find(&records).Error
	return
}

func (c *cloudRevrec) Update(_ context.Context, record *model.CloudRevrec) error {
	return c.db.Save(record).Error
}

func (c *cloudRevrec) DeleteAndSave(_ context.Context, records []model.CloudRevrec) ([]model.CloudRevrec, error) {
	if len(records) == 0 {
		return nil, nil
	}

	billMonth := records[0].BillMonth

	var existingRecords []model.CloudRevrec
	if err := c.db.Unscoped().Where("bill_month = ?", billMonth).Find(&existingRecords).Error; err != nil {
		return nil, err
	}

	existingMap := make(map[string]model.CloudRevrec)
	for _, record := range existingRecords {
		existingMap[cloudRevrecBusinessKey(record)] = record
	}

	incomingKeys := make(map[string]struct{}, len(records))
	incomingRecognitionTypes := make(map[string]struct{})
	changedRecords := make([]model.CloudRevrec, 0)
	for _, record := range records {
		record = normalizeCloudRevrecForStorage(record)
		key := cloudRevrecBusinessKey(record)
		incomingKeys[key] = struct{}{}
		incomingRecognitionTypes[cloudRevrecRecognitionTypeKey(record)] = struct{}{}
		if existing, exists := existingMap[key]; exists {
			merged, changed := mergeCloudRevrecForUpsert(existing, record)
			if !changed {
				continue
			}
			if err := c.saveCloudRevrec(merged).Error; err != nil {
				return nil, err
			}
			changedRecords = append(changedRecords, merged)
			existingMap[key] = merged
			continue
		}

		if err := c.createCloudRevrec(record).Error; err != nil {
			return nil, err
		}
		changedRecords = append(changedRecords, record)
		existingMap[key] = record
	}

	for _, existing := range existingRecords {
		if !shouldDeleteStaleCloudRevrec(existing, incomingKeys, incomingRecognitionTypes) {
			continue
		}
		if err := c.db.Delete(&existing).Error; err != nil {
			return nil, err
		}
	}

	return changedRecords, nil
}

func (c *cloudRevrec) saveCloudRevrec(record model.CloudRevrec) *gorm.DB {
	if record.SyncTime.IsZero() {
		return c.db.Unscoped().Omit("sync_time").Save(&record)
	}
	return c.db.Unscoped().Save(&record)
}

func (c *cloudRevrec) createCloudRevrec(record model.CloudRevrec) *gorm.DB {
	if record.SyncTime.IsZero() {
		return c.db.Omit("sync_time").Create(&record)
	}
	return c.db.Create(&record)
}

func shouldDeleteStaleCloudRevrec(existing model.CloudRevrec, incomingKeys map[string]struct{}, incomingRecognitionTypes map[string]struct{}) bool {
	if existing.DeletedAt.Valid || existing.RowLock || existing.Sync == 1 {
		return false
	}
	if _, sameRecognitionRun := incomingRecognitionTypes[cloudRevrecRecognitionTypeKey(existing)]; !sameRecognitionRun {
		return false
	}
	if _, stillPresent := incomingKeys[cloudRevrecBusinessKey(existing)]; stillPresent {
		return false
	}
	return true
}

func cloudRevrecBusinessKey(record model.CloudRevrec) string {
	orderKey := strings.TrimSpace(record.OrderNumber)
	// Self-serve rows deliberately expose a blank order_number to downstream
	// finance.  A later source refresh can nevertheless resolve Order to a
	// Salesforce order id.  That id must not turn the same self-serve actual
	// fact into a new business key (and consequently a duplicate RevRec).
	if orderKey == "" && !isStrictSelfServeRevrec(record) {
		orderKey = strings.TrimSpace(record.Order)
	}
	if orderKey == "" {
		orderKey = strings.TrimSpace(record.AdjustTenantID)
	}
	return fmt.Sprintf("%s|%s|%d|%s|%s", record.BillMonth, record.AdjustTenantID, record.Type, orderKey, cloudRevrecRecognitionTypeKey(record))
}

func isStrictSelfServeRevrec(record model.CloudRevrec) bool {
	accountNumber := strings.TrimSpace(record.AccountNumber)
	adjustTenantID := strings.TrimSpace(record.AdjustTenantID)
	if accountNumber == "" || accountNumber != adjustTenantID {
		return false
	}
	for _, char := range accountNumber {
		if char < '0' || char > '9' {
			return false
		}
	}
	return true
}

func cloudRevrecRecognitionTypeKey(record model.CloudRevrec) string {
	value := strings.ToLower(strings.TrimSpace(record.RevenueRecognitionType))
	if value != "" {
		return value
	}
	if strings.TrimSpace(record.ActualRevenueRecognitionDate) != "" || strings.TrimSpace(record.ActualRevenueAmount) != "" {
		return "actual"
	}
	return "estimated"
}

func normalizeCloudRevrecForStorage(record model.CloudRevrec) model.CloudRevrec {
	record.Estimate = cloudRevrecRecognitionTypeKey(record) == "estimated"
	return record
}

func mergeCloudRevrecForUpsert(existing, incoming model.CloudRevrec) (model.CloudRevrec, bool) {
	if existing.RowLock {
		return existing, false
	}

	incoming = normalizeCloudRevrecForStorage(incoming)

	merged := incoming
	merged.ID = existing.ID
	merged.CreatedAt = existing.CreatedAt
	merged.RevrecID = existing.RevrecID
	merged.RevrecNumber = existing.RevrecNumber
	merged.SyncTime = existing.SyncTime
	// A synced row has already established its downstream finance identity.
	// Do not let a later source-order resolution rewrite that identity while
	// refreshing its monetary payload.
	if existing.Sync == 1 {
		merged.Order = existing.Order
		merged.OrderNumber = existing.OrderNumber
	}

	if merged.EstimatedRevenueAmount == "" {
		merged.EstimatedRevenueAmount = existing.EstimatedRevenueAmount
	}
	if merged.ActualRevenueAmount == "" {
		merged.ActualRevenueAmount = existing.ActualRevenueAmount
	}
	if merged.ActualRevenueRecognitionDate == "" {
		merged.ActualRevenueRecognitionDate = existing.ActualRevenueRecognitionDate
	}
	if merged.RevenueRecognitionType == "" {
		merged.RevenueRecognitionType = existing.RevenueRecognitionType
	}
	merged = normalizeCloudRevrecForStorage(merged)

	restoredFromSoftDelete := existing.DeletedAt.Valid
	syncPayloadChanged := cloudRevrecSyncPayloadChanged(existing, merged)
	localOnlyChanged := existing.Estimate != merged.Estimate
	if !syncPayloadChanged && !localOnlyChanged && !restoredFromSoftDelete {
		merged.Sync = existing.Sync
		return merged, false
	}

	if syncPayloadChanged {
		merged.Sync = 0
	} else {
		merged.Sync = existing.Sync
	}
	return merged, true
}

func cloudRevrecSyncPayloadChanged(a, b model.CloudRevrec) bool {
	return a.Date != b.Date ||
		a.BillingEndDate != b.BillingEndDate ||
		a.EstimatedRevenueAmount != b.EstimatedRevenueAmount ||
		a.ActualRevenueAmount != b.ActualRevenueAmount ||
		a.ActualRevenueRecognitionDate != b.ActualRevenueRecognitionDate ||
		a.RevenueRecognitionType != b.RevenueRecognitionType ||
		a.AdjustedStarterAmount != b.AdjustedStarterAmount ||
		a.AdjustedDedicatedAmount != b.AdjustedDedicatedAmount ||
		a.AdjustedSupportAmount != b.AdjustedSupportAmount ||
		a.AdjustedEssentialAmount != b.AdjustedEssentialAmount ||
		a.AdjustedPremiumAmount != b.AdjustedPremiumAmount ||
		a.AdjustedLakeAmount != b.AdjustedLakeAmount ||
		a.AdjustedByocAmount != b.AdjustedByocAmount ||
		a.AdjustedRevenueAmount != b.AdjustedRevenueAmount ||
		a.AdjustedShortfallAmount != b.AdjustedShortfallAmount ||
		a.RevenueAmount != b.RevenueAmount ||
		a.CreditAmount != b.CreditAmount ||
		a.ExchangeRateToUse != b.ExchangeRateToUse ||
		a.CurrencyIsoCode != b.CurrencyIsoCode
}

func (c *cloudRevrec) GetByOrderID(_ context.Context, orderID string) (record model.CloudRevrec, err error) {
	err = c.db.Where("order", orderID).First(&record).Error
	return
}

func (c *cloudRevrec) GetByAdjustID(_ context.Context, adjustTenantID, billMonth string) (record model.CloudRevrec, err error) {
	err = c.db.Where("adjust_tenant_id = ? and bill_month = ?", adjustTenantID, billMonth).First(&record).Error
	return
}

func (c *cloudRevrec) GetByID(_ context.Context, id uint64) (record model.CloudRevrec, err error) {
	err = c.db.Where("id = ?", id).First(&record).Error
	return
}

func (c *cloudRevrec) UpdateRevrecs(records []model.CloudRevrec) error {
	if len(records) == 0 {
		return nil
	}
	for _, record := range records {
		updates := map[string]interface{}{
			"new_order_starter_amount":   record.AdjustedStarterAmount,
			"new_order_dedicated_amount": record.AdjustedDedicatedAmount,
			"new_order_support_amount":   record.AdjustedSupportAmount,
		}
		err := c.db.Model(&model.CloudRevrec{}).
			Where("bill_month = ? AND account_number = ? AND adjust_tenant_id = ?",
				record.BillMonth, record.AccountNumber, record.AdjustTenantID).
			Updates(updates).Error
		if err != nil {
			return fmt.Errorf("failed to update record for account %s: %w", record.AccountNumber, err)
		}
	}

	return nil
}

// ListDeleted 获取已软删除的记录
func (c *cloudRevrec) ListDeleted(_ context.Context, billMonth string) (records []*model.CloudRevrec, err error) {
	// 只查询 deleted_at 不为 NULL 的记录
	err = c.db.Unscoped().Where("deleted_at IS NOT NULL").
		Where("bill_month = ?", billMonth).
		Find(&records).Error
	return
}
