package repository

import (
	"strings"
	"time"

	"hyapp-admin-server/internal/model"

	"gorm.io/gorm/clause"
)

const (
	legacyGooglePaidRetryBaseMS = int64((15 * time.Second) / time.Millisecond)
	legacyGooglePaidRetryMaxMS  = int64((15 * time.Minute) / time.Millisecond)
)

// UpsertLegacyGooglePaidDetail 写入或覆盖 legacy 谷歌账单实付明细；同一账单重复同步以最新结果为准。
func (s *Store) UpsertLegacyGooglePaidDetail(detail model.LegacyGooglePaidDetail) error {
	detail.AppCode = strings.ToLower(strings.TrimSpace(detail.AppCode))
	detail.TransactionID = strings.TrimSpace(detail.TransactionID)
	return s.db.Clauses(clause.OnConflict{
		Columns: []clause.Column{{Name: "app_code"}, {Name: "transaction_id"}},
		DoUpdates: clause.AssignmentColumns([]string{
			"provider_order_id", "paid_currency_code", "paid_amount_micro",
			"paid_tax_micro", "paid_net_micro", "bill_usd_minor", "bill_created_at_ms",
			"synced_at_ms", "updated_at_ms",
		}),
	}).Create(&detail).Error
}

// LegacyGooglePaidStats 是 legacy 谷歌实付缓存在指定账单时间段内的聚合口径。
type LegacyGooglePaidStats struct {
	SyncedCount     int64
	CoveredUSDMinor int64
	EstFeeUSDMinor  int64
	EstTaxUSDMinor  int64
	EstNetUSDMinor  int64
}

// LegacyGooglePaidStatsByBillTime 按账单创建时间段聚合实付同步覆盖度。
// synced_at_ms 是 Orders API 请求已成功落库的事实；Google 作为 Merchant of Record 时可能不返回
// developerRevenueInBuyerCurrency，此时 paid_net_micro=0 只表示缺少费用明细，不能把已同步订单重新算成未同步。
// 税/费/净收仍只对实付和净收都有效的订单按比例折算，避免用缺失字段伪造费用数据。
func (s *Store) LegacyGooglePaidStatsByBillTime(appCode string, startAtMS int64, endAtMS int64) (LegacyGooglePaidStats, error) {
	where := "app_code = ?"
	args := []any{strings.ToLower(strings.TrimSpace(appCode))}
	if startAtMS > 0 {
		where += " AND bill_created_at_ms >= ?"
		args = append(args, startAtMS)
	}
	if endAtMS > 0 {
		where += " AND bill_created_at_ms < ?"
		args = append(args, endAtMS)
	}
	var stats LegacyGooglePaidStats
	row := s.db.Raw(`
		SELECT COALESCE(SUM(CASE WHEN synced_at_ms > 0 THEN 1 ELSE 0 END), 0),
		       COALESCE(SUM(CASE WHEN paid_amount_micro > 0 AND paid_net_micro > 0 THEN bill_usd_minor ELSE 0 END), 0),
		       COALESCE(SUM(CASE WHEN paid_amount_micro > 0 AND paid_net_micro > 0
		           THEN ROUND(bill_usd_minor * GREATEST(paid_amount_micro - paid_net_micro - paid_tax_micro, 0) / paid_amount_micro) ELSE 0 END), 0),
		       COALESCE(SUM(CASE WHEN paid_amount_micro > 0 AND paid_net_micro > 0
		           THEN ROUND(bill_usd_minor * paid_tax_micro / paid_amount_micro) ELSE 0 END), 0),
		       COALESCE(SUM(CASE WHEN paid_amount_micro > 0 AND paid_net_micro > 0
		           THEN ROUND(bill_usd_minor * paid_net_micro / paid_amount_micro) ELSE 0 END), 0)
		FROM admin_legacy_google_paid_details
		WHERE `+where, args...).Row()
	if err := row.Scan(&stats.SyncedCount, &stats.CoveredUSDMinor, &stats.EstFeeUSDMinor, &stats.EstTaxUSDMinor, &stats.EstNetUSDMinor); err != nil {
		return LegacyGooglePaidStats{}, err
	}
	return stats, nil
}

// LegacyGooglePaidTransactionIDs 返回该 App 已同步实付的账单交易号集合，供“未同步”视图在 Mongo 侧做排除。
func (s *Store) LegacyGooglePaidTransactionIDs(appCode string) ([]string, error) {
	ids := []string{}
	err := s.db.Model(&model.LegacyGooglePaidDetail{}).
		Where("app_code = ?", strings.ToLower(strings.TrimSpace(appCode))).
		Pluck("transaction_id", &ids).Error
	return ids, err
}

// LegacyGooglePaidRetryBlockedTransactionIDs returns failures whose persisted backoff has not expired.
// Due attempts are intentionally omitted so the next 15-second scan can retry them without manual intervention.
func (s *Store) LegacyGooglePaidRetryBlockedTransactionIDs(appCode string, nowMS int64) ([]string, error) {
	ids := []string{}
	err := s.db.Model(&model.LegacyGooglePaidSyncAttempt{}).
		Where("app_code = ? AND next_retry_at_ms > ?", strings.ToLower(strings.TrimSpace(appCode)), nowMS).
		Pluck("transaction_id", &ids).Error
	return ids, err
}

// RecordLegacyGooglePaidSyncFailure atomically advances retry state with exponential backoff.
// The error text is bounded before persistence so provider responses cannot inflate the admin database.
func (s *Store) RecordLegacyGooglePaidSyncFailure(appCode string, transactionID string, message string, nowMS int64) error {
	appCode = strings.ToLower(strings.TrimSpace(appCode))
	transactionID = strings.TrimSpace(transactionID)
	message = strings.TrimSpace(message)
	if len(message) > 512 {
		message = message[:512]
	}
	return s.db.Exec(`
		INSERT INTO admin_legacy_google_paid_sync_attempts (
			app_code, transaction_id, attempt_count, last_error, next_retry_at_ms, created_at_ms, updated_at_ms
		) VALUES (?, ?, 1, ?, ?, ?, ?)
		ON DUPLICATE KEY UPDATE
			last_error = VALUES(last_error),
			next_retry_at_ms = VALUES(updated_at_ms) + LEAST(?, ? * POW(2, LEAST(attempt_count, 6))),
			attempt_count = attempt_count + 1,
			updated_at_ms = VALUES(updated_at_ms)`,
		appCode, transactionID, message, nowMS+legacyGooglePaidRetryBaseMS, nowMS, nowMS,
		legacyGooglePaidRetryMaxMS, legacyGooglePaidRetryBaseMS,
	).Error
}

// ClearLegacyGooglePaidSyncFailure removes obsolete retry state after the paid-detail upsert succeeds.
func (s *Store) ClearLegacyGooglePaidSyncFailure(appCode string, transactionID string) error {
	return s.db.Where(
		"app_code = ? AND transaction_id = ?",
		strings.ToLower(strings.TrimSpace(appCode)), strings.TrimSpace(transactionID),
	).Delete(&model.LegacyGooglePaidSyncAttempt{}).Error
}

// LegacyGooglePaidDetails 按交易号批量读取实付明细，供财务明细列表按页回填。
func (s *Store) LegacyGooglePaidDetails(appCode string, transactionIDs []string) (map[string]model.LegacyGooglePaidDetail, error) {
	details := map[string]model.LegacyGooglePaidDetail{}
	cleaned := make([]string, 0, len(transactionIDs))
	for _, transactionID := range transactionIDs {
		if transactionID = strings.TrimSpace(transactionID); transactionID != "" {
			cleaned = append(cleaned, transactionID)
		}
	}
	if len(cleaned) == 0 {
		return details, nil
	}
	rows := []model.LegacyGooglePaidDetail{}
	if err := s.db.
		Where("app_code = ? AND transaction_id IN ?", strings.ToLower(strings.TrimSpace(appCode)), cleaned).
		Find(&rows).Error; err != nil {
		return nil, err
	}
	for _, row := range rows {
		details[row.TransactionID] = row
	}
	return details, nil
}
