package paymentconfig

import (
	"context"
	"errors"
	"time"

	"hfb_sys/backend/internal/model"

	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

// ExportBackup 导出所有支付配置备份，包含解密后的密钥。
func (r *Repository) ExportBackup(ctx context.Context, actorID uint64, meta AuditMeta) (*ExportBackup, error) {
	var backup *ExportBackup
	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		var items []model.PaymentMerchantConfig
		if err := tx.Order("is_default DESC, id DESC").Find(&items).Error; err != nil {
			return err
		}

		configs := make([]ConfigDTO, 0, len(items))
		for _, item := range items {
			dto, err := r.toDTO(item, true)
			if err != nil {
				return err
			}
			configs = append(configs, dto)
		}

		exportedAt := time.Now().Format(time.RFC3339)
		backup = &ExportBackup{
			Type:       "payment_config_backup",
			Version:    1,
			ExportedAt: exportedAt,
			ExportedBy: actorID,
			Total:      len(configs),
			Configs:    configs,
		}

		return appendAuditLog(tx, actorID, "payment_config.export", 0, meta, map[string]any{
			"total":       len(configs),
			"exported_at": exportedAt,
		})
	})
	if err != nil {
		return nil, err
	}
	return backup, nil
}

// ImportBackup 导入支付配置备份，按 provider + merchant_id + pay_way 更新或新增。
func (r *Repository) ImportBackup(ctx context.Context, backup ExportBackup, actorID uint64, meta AuditMeta) (*ImportBackupResult, error) {
	if backup.Type != "payment_config_backup" || backup.Version <= 0 {
		return nil, ErrInvalidBackup
	}
	if len(backup.Configs) == 0 {
		return nil, ErrEmptyBackup
	}

	activeKeys := backupActiveKeys(backup.Configs)
	result := &ImportBackupResult{Total: len(backup.Configs)}

	err := r.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		for payWay := range activeKeys {
			if err := deactivateOtherConfigs(tx, 0, actorID, payWay); err != nil {
				return err
			}
		}

		for _, cfg := range backup.Configs {
			req := importCreateRequest(cfg, activeKeys)
			if err := r.validateCreateRequest(req); err != nil {
				return err
			}
			payWay := firstNonEmpty(req.PayWay, "ZFBZF")

			encryptedSignKey, err := r.encryptor.Encrypt(req.SignKey)
			if err != nil {
				return ErrEncryptionFailed
			}
			encryptedNotifyKey, err := r.encryptor.Encrypt(req.NotifyKey)
			if err != nil {
				return ErrEncryptionFailed
			}

			updates := map[string]any{
				"name":          req.Name,
				"gateway_url":   req.GatewayURL,
				"sign_key":      encryptedSignKey,
				"notify_key":    encryptedNotifyKey,
				"notify_url":    req.NotifyURL,
				"jump_url":      req.JumpURL,
				"pay_way":       payWay,
				"jspay_flag":    firstNonEmpty(req.JSPayFlag, "2"),
				"sign_type":     firstNonEmpty(req.SignType, defaultSignType(req.Provider)),
				"extra_config":  model.JSONMap(req.ExtraConfig),
				"is_default":    req.IsDefault,
				"status":        firstNonEmpty(req.Status, "active"),
				"environment":   firstNonEmpty(req.Environment, "production"),
				"business_tags": model.JSONArray(req.BusinessTags),
				"updated_by":    actorID,
			}

			var existing model.PaymentMerchantConfig
			err = tx.Clauses(clause.Locking{Strength: "UPDATE"}).
				Where("provider = ? AND merchant_id = ? AND pay_way = ?", req.Provider, req.MerchantID, payWay).
				First(&existing).Error
			if err == nil {
				if err := tx.Model(&existing).Updates(updates).Error; err != nil {
					return err
				}
				result.Updated++
				continue
			}
			if !errors.Is(err, gorm.ErrRecordNotFound) {
				return err
			}

			createdBy := actorID
			item := model.PaymentMerchantConfig{
				Name:         req.Name,
				Provider:     req.Provider,
				MerchantID:   req.MerchantID,
				GatewayURL:   req.GatewayURL,
				SignKey:      encryptedSignKey,
				NotifyKey:    encryptedNotifyKey,
				NotifyURL:    req.NotifyURL,
				JumpURL:      req.JumpURL,
				PayWay:       payWay,
				JSPayFlag:    firstNonEmpty(req.JSPayFlag, "2"),
				SignType:     firstNonEmpty(req.SignType, defaultSignType(req.Provider)),
				ExtraConfig:  model.JSONMap(req.ExtraConfig),
				IsDefault:    req.IsDefault,
				Status:       firstNonEmpty(req.Status, "active"),
				Environment:  firstNonEmpty(req.Environment, "production"),
				BusinessTags: req.BusinessTags,
				CreatedBy:    &createdBy,
				UpdatedBy:    &actorID,
			}
			if err := tx.Create(&item).Error; err != nil {
				return err
			}
			result.Created++
		}

		return appendAuditLog(tx, actorID, "payment_config.import", 0, meta, map[string]any{
			"total":   result.Total,
			"created": result.Created,
			"updated": result.Updated,
		})
	})
	if err != nil {
		return nil, err
	}
	return result, nil
}

// backupActiveKeys 提取备份中每个支付方式的启用配置键。
func backupActiveKeys(configs []ConfigDTO) map[string]string {
	keys := make(map[string]string)
	for _, cfg := range configs {
		payWay := firstNonEmpty(cfg.PayWay, "ZFBZF")
		if _, exists := keys[payWay]; exists {
			continue
		}
		if cfg.Status == "active" || cfg.IsDefault {
			keys[payWay] = paymentConfigImportKey(cfg.Provider, cfg.MerchantID, payWay)
		}
	}
	return keys
}

// importCreateRequest 将备份 DTO 转为创建请求，并保留每个支付方式唯一启用配置。
func importCreateRequest(cfg ConfigDTO, activeKeys map[string]string) CreateRequest {
	status := cfg.Status
	if status == "" {
		status = "disabled"
	}
	payWay := firstNonEmpty(cfg.PayWay, "ZFBZF")
	activeKey := activeKeys[payWay]
	isActive := activeKey != "" && paymentConfigImportKey(cfg.Provider, cfg.MerchantID, payWay) == activeKey
	if isActive {
		status = "active"
	} else if status == "active" {
		status = "disabled"
	}

	return CreateRequest{
		Name:         cfg.Name,
		Provider:     cfg.Provider,
		MerchantID:   cfg.MerchantID,
		GatewayURL:   cfg.GatewayURL,
		SignKey:      cfg.SignKey,
		NotifyKey:    cfg.NotifyKey,
		NotifyURL:    cfg.NotifyURL,
		JumpURL:      cfg.JumpURL,
		PayWay:       payWay,
		JSPayFlag:    cfg.JSPayFlag,
		SignType:     cfg.SignType,
		ExtraConfig:  cfg.ExtraConfig,
		IsDefault:    isActive,
		Status:       status,
		Environment:  cfg.Environment,
		BusinessTags: cfg.BusinessTags,
	}
}

// paymentConfigImportKey 生成导入去重键，同商户可按支付方式拆分为多条配置。
func paymentConfigImportKey(provider string, merchantID string, payWay string) string {
	return provider + ":" + merchantID + ":" + payWay
}
