// Package wechat_config 提供微信参数配置管理的业务逻辑服务
// 包含配置的 CRUD、激活/停用、Redis 缓存等功能
package wechat_config

import (
	"context"
	"fmt"
	"time"

	"github.com/bytedance/sonic"
	"github.com/redis/go-redis/v9"
	"go.uber.org/zap"
	"gorm.io/gorm"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/internal/store"
	"github.com/break/junhong_cmp_fiber/internal/store/postgres"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"
)

// Redis 缓存键
const redisActiveConfigKey = "wechat:config:active"

// AuditServiceInterface 审计日志服务接口
type AuditServiceInterface interface {
	LogOperation(ctx context.Context, log *model.AccountOperationLog)
}

// Service 微信参数配置业务服务
type Service struct {
	store              *postgres.WechatConfigStore
	orderStore         *postgres.OrderStore
	rechargeOrderStore *postgres.RechargeOrderStore
	agentRechargeStore *postgres.AgentRechargeStore
	paymentStore       *postgres.PaymentStore
	auditService       AuditServiceInterface
	redis              *redis.Client
	logger             *zap.Logger
}

// New 创建微信参数配置服务实例
func New(
	store *postgres.WechatConfigStore,
	orderStore *postgres.OrderStore,
	rechargeOrderStore *postgres.RechargeOrderStore,
	agentRechargeStore *postgres.AgentRechargeStore,
	paymentStore *postgres.PaymentStore,
	auditService AuditServiceInterface,
	rdb *redis.Client,
	logger *zap.Logger,
) *Service {
	return &Service{
		store:              store,
		orderStore:         orderStore,
		rechargeOrderStore: rechargeOrderStore,
		agentRechargeStore: agentRechargeStore,
		paymentStore:       paymentStore,
		auditService:       auditService,
		redis:              rdb,
		logger:             logger,
	}
}

// Create 创建微信参数配置
// POST /api/admin/wechat-configs
func (s *Service) Create(ctx context.Context, req *dto.CreateWechatConfigRequest) (*dto.WechatConfigResponse, error) {
	// 根据 provider_type 校验必填字段
	if err := s.validateProviderFields(req); err != nil {
		return nil, err
	}

	var desc *string
	if req.Description != "" {
		desc = &req.Description
	}

	config := &model.WechatConfig{
		Name:               req.Name,
		Description:        desc,
		ProviderType:       req.ProviderType,
		IsActive:           false,
		OaAppID:            req.OaAppID,
		OaAppSecret:        req.OaAppSecret,
		OaToken:            req.OaToken,
		OaAesKey:           req.OaAesKey,
		OaOAuthRedirectURL: req.OaOAuthRedirectURL,
		MiniappAppID:       req.MiniappAppID,
		MiniappAppSecret:   req.MiniappAppSecret,
		WxMchID:            req.WxMchID,
		WxAPIV3Key:         req.WxAPIV3Key,
		WxAPIV2Key:         req.WxAPIV2Key,
		WxCertContent:      req.WxCertContent,
		WxKeyContent:       req.WxKeyContent,
		WxSerialNo:         req.WxSerialNo,
		WxNotifyURL:        req.WxNotifyURL,
		FyInsCd:            req.FyInsCd,
		FyMchntCd:          req.FyMchntCd,
		FyTermID:           req.FyTermID,
		FyPrivateKey:       req.FyPrivateKey,
		FyPublicKey:        req.FyPublicKey,
		FyAPIURL:           req.FyAPIURL,
		FyNotifyURL:        req.FyNotifyURL,

		AliAppID:            req.AliAppID,
		AliPrivateKey:       req.AliPrivateKey,
		AliPublicKey:        req.AliPublicKey,
		AliNotifyURL:        req.AliNotifyURL,
		AliReturnURL:        req.AliReturnURL,
		AliProduction:       req.AliProduction,
		AliPayExpireMinutes: req.AliPayExpireMinutes,
	}
	// 支付宝过期分钟数默认值
	if config.AliPayExpireMinutes == 0 {
		config.AliPayExpireMinutes = model.DefaultAliPayExpireMinutes
	}
	config.Creator = middleware.GetUserIDFromContext(ctx)

	if err := s.store.Create(ctx, config); err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "创建微信支付配置失败")
	}

	// 审计日志
	afterData := model.JSONB{
		"id":            config.ID,
		"name":          config.Name,
		"provider_type": config.ProviderType,
	}
	go s.auditService.LogOperation(ctx, &model.AccountOperationLog{
		OperatorID:    middleware.GetUserIDFromContext(ctx),
		OperatorType:  middleware.GetUserTypeFromContext(ctx),
		OperatorName:  "",
		OperationType: "create",
		OperationDesc: fmt.Sprintf("创建微信支付配置：%s", config.Name),
		AfterData:     afterData,
		RequestID:     middleware.GetRequestIDFromContext(ctx),
		IPAddress:     middleware.GetIPFromContext(ctx),
		UserAgent:     middleware.GetUserAgentFromContext(ctx),
	})

	return dto.FromWechatConfigModel(config), nil
}

// List 获取配置列表
// GET /api/admin/wechat-configs
func (s *Service) List(ctx context.Context, req *dto.WechatConfigListRequest) ([]*dto.WechatConfigResponse, int64, error) {
	opts := &store.QueryOptions{
		Page:     req.Page,
		PageSize: req.PageSize,
		OrderBy:  "id DESC",
	}
	if opts.Page == 0 {
		opts.Page = 1
	}
	if opts.PageSize == 0 {
		opts.PageSize = constants.DefaultPageSize
	}

	filters := make(map[string]interface{})
	if req.ProviderType != nil {
		filters["provider_type"] = *req.ProviderType
	}
	filters["is_active"] = req.IsActive

	configs, total, err := s.store.List(ctx, opts, filters)
	if err != nil {
		return nil, 0, errors.Wrap(errors.CodeInternalError, err, "查询微信支付配置列表失败")
	}

	responses := make([]*dto.WechatConfigResponse, len(configs))
	for i, c := range configs {
		responses[i] = dto.FromWechatConfigModel(c)
	}

	return responses, total, nil
}

// Get 获取配置详情
// GET /api/admin/wechat-configs/:id
func (s *Service) Get(ctx context.Context, id uint) (*dto.WechatConfigResponse, error) {
	config, err := s.store.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeWechatConfigNotFound)
		}
		return nil, errors.Wrap(errors.CodeInternalError, err, "获取微信支付配置失败")
	}
	return dto.FromWechatConfigModel(config), nil
}

// Update 更新微信参数配置
// PUT /api/admin/wechat-configs/:id
func (s *Service) Update(ctx context.Context, id uint, req *dto.UpdateWechatConfigRequest) (*dto.WechatConfigResponse, error) {
	config, err := s.store.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeWechatConfigNotFound)
		}
		return nil, errors.Wrap(errors.CodeInternalError, err, "获取微信支付配置失败")
	}

	// 合并字段：指针非 nil 时更新，敏感字段空字符串表示保持原值
	if req.Name != nil {
		config.Name = *req.Name
	}
	if req.Description != nil {
		config.Description = req.Description
	}
	if req.ProviderType != nil {
		config.ProviderType = *req.ProviderType
	}

	// OAuth 公众号
	s.mergeStringField(&config.OaAppID, req.OaAppID)
	s.mergeSensitiveField(&config.OaAppSecret, req.OaAppSecret)
	s.mergeSensitiveField(&config.OaToken, req.OaToken)
	s.mergeSensitiveField(&config.OaAesKey, req.OaAesKey)
	s.mergeStringField(&config.OaOAuthRedirectURL, req.OaOAuthRedirectURL)

	// OAuth 小程序
	s.mergeStringField(&config.MiniappAppID, req.MiniappAppID)
	s.mergeSensitiveField(&config.MiniappAppSecret, req.MiniappAppSecret)

	// 微信直连支付
	s.mergeStringField(&config.WxMchID, req.WxMchID)
	s.mergeSensitiveField(&config.WxAPIV3Key, req.WxAPIV3Key)
	s.mergeSensitiveField(&config.WxAPIV2Key, req.WxAPIV2Key)
	s.mergeSensitiveField(&config.WxCertContent, req.WxCertContent)
	s.mergeSensitiveField(&config.WxKeyContent, req.WxKeyContent)
	s.mergeStringField(&config.WxSerialNo, req.WxSerialNo)
	s.mergeStringField(&config.WxNotifyURL, req.WxNotifyURL)

	// 富友支付
	s.mergeStringField(&config.FyInsCd, req.FyInsCd)
	s.mergeStringField(&config.FyMchntCd, req.FyMchntCd)
	s.mergeStringField(&config.FyTermID, req.FyTermID)
	s.mergeSensitiveField(&config.FyPrivateKey, req.FyPrivateKey)
	s.mergeSensitiveField(&config.FyPublicKey, req.FyPublicKey)
	s.mergeStringField(&config.FyAPIURL, req.FyAPIURL)
	s.mergeStringField(&config.FyNotifyURL, req.FyNotifyURL)

	// 支付宝支付
	s.mergeStringField(&config.AliAppID, req.AliAppID)
	s.mergeSensitiveField(&config.AliPrivateKey, req.AliPrivateKey)
	s.mergeSensitiveField(&config.AliPublicKey, req.AliPublicKey)
	s.mergeStringField(&config.AliNotifyURL, req.AliNotifyURL)
	s.mergeStringField(&config.AliReturnURL, req.AliReturnURL)
	if req.AliProduction != nil {
		config.AliProduction = *req.AliProduction
	}
	if req.AliPayExpireMinutes != nil && *req.AliPayExpireMinutes > 0 {
		config.AliPayExpireMinutes = *req.AliPayExpireMinutes
	}

	config.Updater = middleware.GetUserIDFromContext(ctx)

	if err := s.store.Update(ctx, config); err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "更新微信支付配置失败")
	}

	// 如果当前配置处于激活状态，清除缓存
	if config.IsActive {
		s.clearActiveConfigCache(ctx)
	}

	afterData := model.JSONB{
		"id":            config.ID,
		"name":          config.Name,
		"provider_type": config.ProviderType,
		"is_active":     config.IsActive,
	}
	go s.auditService.LogOperation(ctx, &model.AccountOperationLog{
		OperatorID:    middleware.GetUserIDFromContext(ctx),
		OperatorType:  middleware.GetUserTypeFromContext(ctx),
		OperatorName:  "",
		OperationType: "update",
		OperationDesc: fmt.Sprintf("更新微信支付配置：%s", config.Name),
		AfterData:     afterData,
		RequestID:     middleware.GetRequestIDFromContext(ctx),
		IPAddress:     middleware.GetIPFromContext(ctx),
		UserAgent:     middleware.GetUserAgentFromContext(ctx),
	})

	return dto.FromWechatConfigModel(config), nil
}

// Delete 删除微信参数配置
// DELETE /api/admin/wechat-configs/:id
func (s *Service) Delete(ctx context.Context, id uint) error {
	config, err := s.store.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return errors.New(errors.CodeWechatConfigNotFound)
		}
		return errors.Wrap(errors.CodeInternalError, err, "获取微信支付配置失败")
	}

	// 不允许删除正在激活的配置
	if config.IsActive {
		return errors.New(errors.CodeWechatConfigActive)
	}

	// 检查是否存在待支付订单
	pendingOrders, err := s.store.CountPendingOrdersByConfigID(ctx, id)
	if err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "检查在途订单失败")
	}

	pendingRecharges, err := s.store.CountPendingRechargesByConfigID(ctx, id)
	if err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "检查在途充值失败")
	}

	if pendingOrders > 0 || pendingRecharges > 0 {
		return errors.New(errors.CodeWechatConfigHasPendingOrders)
	}

	if err := s.store.SoftDelete(ctx, id); err != nil {
		return errors.Wrap(errors.CodeInternalError, err, "删除微信支付配置失败")
	}

	s.clearActiveConfigCache(ctx)

	beforeData := model.JSONB{
		"id":            config.ID,
		"name":          config.Name,
		"provider_type": config.ProviderType,
	}
	go s.auditService.LogOperation(ctx, &model.AccountOperationLog{
		OperatorID:    middleware.GetUserIDFromContext(ctx),
		OperatorType:  middleware.GetUserTypeFromContext(ctx),
		OperatorName:  "",
		OperationType: "delete",
		OperationDesc: fmt.Sprintf("删除微信支付配置：%s", config.Name),
		BeforeData:    beforeData,
		RequestID:     middleware.GetRequestIDFromContext(ctx),
		IPAddress:     middleware.GetIPFromContext(ctx),
		UserAgent:     middleware.GetUserAgentFromContext(ctx),
	})

	return nil
}

// Activate 激活指定配置（同一时间只有一个激活配置）
// POST /api/admin/wechat-configs/:id/activate
func (s *Service) Activate(ctx context.Context, id uint) (*dto.WechatConfigResponse, error) {
	config, err := s.store.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeWechatConfigNotFound)
		}
		return nil, errors.Wrap(errors.CodeInternalError, err, "获取微信支付配置失败")
	}

	// 记录旧的激活配置名称
	oldActiveName := ""
	oldActive, oldErr := s.store.GetActive(ctx)
	if oldErr == nil && oldActive != nil {
		oldActiveName = oldActive.Name
	}

	// 事务内激活
	db := s.store.DB()
	if err := db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		return s.store.ActivateInTx(ctx, tx, id)
	}); err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "激活微信支付配置失败")
	}

	s.clearActiveConfigCache(ctx)

	// 重新查询最新状态
	config, _ = s.store.GetByID(ctx, id)

	desc := fmt.Sprintf("激活微信支付配置：%s", config.Name)
	if oldActiveName != "" && oldActiveName != config.Name {
		desc = fmt.Sprintf("激活微信支付配置：%s（原激活配置：%s）", config.Name, oldActiveName)
	}
	go s.auditService.LogOperation(ctx, &model.AccountOperationLog{
		OperatorID:    middleware.GetUserIDFromContext(ctx),
		OperatorType:  middleware.GetUserTypeFromContext(ctx),
		OperatorName:  "",
		OperationType: "activate",
		OperationDesc: desc,
		AfterData:     model.JSONB{"id": config.ID, "name": config.Name, "is_active": true},
		RequestID:     middleware.GetRequestIDFromContext(ctx),
		IPAddress:     middleware.GetIPFromContext(ctx),
		UserAgent:     middleware.GetUserAgentFromContext(ctx),
	})

	return dto.FromWechatConfigModel(config), nil
}

// Deactivate 停用指定配置
// POST /api/admin/wechat-configs/:id/deactivate
func (s *Service) Deactivate(ctx context.Context, id uint) (*dto.WechatConfigResponse, error) {
	config, err := s.store.GetByID(ctx, id)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, errors.New(errors.CodeWechatConfigNotFound)
		}
		return nil, errors.Wrap(errors.CodeInternalError, err, "获取微信支付配置失败")
	}

	if err := s.store.Deactivate(ctx, id); err != nil {
		return nil, errors.Wrap(errors.CodeInternalError, err, "停用微信支付配置失败")
	}

	s.clearActiveConfigCache(ctx)

	// 重新查询最新状态
	config, _ = s.store.GetByID(ctx, id)

	go s.auditService.LogOperation(ctx, &model.AccountOperationLog{
		OperatorID:    middleware.GetUserIDFromContext(ctx),
		OperatorType:  middleware.GetUserTypeFromContext(ctx),
		OperatorName:  "",
		OperationType: "deactivate",
		OperationDesc: fmt.Sprintf("停用微信支付配置：%s", config.Name),
		AfterData:     model.JSONB{"id": config.ID, "name": config.Name, "is_active": false},
		RequestID:     middleware.GetRequestIDFromContext(ctx),
		IPAddress:     middleware.GetIPFromContext(ctx),
		UserAgent:     middleware.GetUserAgentFromContext(ctx),
	})

	return dto.FromWechatConfigModel(config), nil
}

// GetActiveConfig 获取当前生效的支付配置（带 Redis 缓存）
// 缓存策略：命中直接返回，未命中查 DB 后缓存 5 分钟，无记录缓存 "none" 1 分钟
func (s *Service) GetActiveConfig(ctx context.Context) (*model.WechatConfig, error) {
	// 尝试从 Redis 获取
	val, err := s.redis.Get(ctx, redisActiveConfigKey).Result()
	if err == nil {
		if val == "none" {
			return nil, nil
		}
		var config model.WechatConfig
		if err := sonic.UnmarshalString(val, &config); err == nil {
			return &config, nil
		}
	}

	// Redis 未命中，查询数据库
	config, err := s.store.GetActive(ctx)
	if err != nil {
		if err == gorm.ErrRecordNotFound {
			// 无激活配置，缓存空标记 1 分钟
			s.redis.Set(ctx, redisActiveConfigKey, "none", 1*time.Minute)
			return nil, nil
		}
		return nil, errors.Wrap(errors.CodeInternalError, err, "查询激活配置失败")
	}

	// 缓存配置 5 分钟
	if data, err := sonic.MarshalString(config); err == nil {
		s.redis.Set(ctx, redisActiveConfigKey, data, 5*time.Minute)
	}

	return config, nil
}

// GetActiveConfigForAPI 获取当前生效的支付配置（API 响应，已脱敏）
func (s *Service) GetActiveConfigForAPI(ctx context.Context) (*dto.WechatConfigResponse, error) {
	config, err := s.GetActiveConfig(ctx)
	if err != nil {
		return nil, err
	}
	if config == nil {
		return nil, nil
	}
	return dto.FromWechatConfigModel(config), nil
}

// clearActiveConfigCache 清除激活配置的 Redis 缓存
func (s *Service) clearActiveConfigCache(ctx context.Context) {
	if err := s.redis.Del(ctx, redisActiveConfigKey).Err(); err != nil {
		s.logger.Warn("清除微信支付配置缓存失败", zap.Error(err))
	}
}

// validateProviderFields 根据支付渠道类型校验必填字段
func (s *Service) validateProviderFields(req *dto.CreateWechatConfigRequest) error {
	switch req.ProviderType {
	case model.ProviderTypeWechat:
		if req.WxMchID == "" || req.WxAPIV3Key == "" || req.WxCertContent == "" ||
			req.WxKeyContent == "" || req.WxSerialNo == "" || req.WxNotifyURL == "" {
			return errors.New(errors.CodeInvalidParam, "微信直连支付必填字段不完整：wx_mch_id, wx_api_v3_key, wx_cert_content, wx_key_content, wx_serial_no, wx_notify_url")
		}
	case model.ProviderTypeFuiou:
		if req.FyInsCd == "" || req.FyMchntCd == "" || req.FyTermID == "" ||
			req.FyPrivateKey == "" || req.FyPublicKey == "" || req.FyAPIURL == "" || req.FyNotifyURL == "" {
			return errors.New(errors.CodeInvalidParam, "富友支付必填字段不完整：fy_ins_cd, fy_mchnt_cd, fy_term_id, fy_private_key, fy_public_key, fy_api_url, fy_notify_url")
		}
	}
	return nil
}

// mergeStringField 合并普通字符串字段：指针非 nil 时用新值覆盖
func (s *Service) mergeStringField(target *string, newVal *string) {
	if newVal != nil {
		*target = *newVal
	}
}

// mergeSensitiveField 合并敏感字段：指针非 nil 且非空字符串时覆盖，空字符串保持原值
func (s *Service) mergeSensitiveField(target *string, newVal *string) {
	if newVal != nil && *newVal != "" {
		*target = *newVal
	}
}

// GetConfigForCallback 按订单号查找创建该订单时所用的支付配置（含已软删除/停用的记录）
// 回调验签必须使用创建订单时的密钥，否则签名不匹配。
// 若找不到 payment_config_id（旧订单或数据缺失），回退到当前激活配置。
func (s *Service) GetConfigForCallback(ctx context.Context, orderNo string) (*model.WechatConfig, error) {
	configID, err := s.resolvePaymentConfigID(ctx, orderNo)
	if err != nil {
		s.logger.Warn("回调：查询订单 payment_config_id 失败，回退激活配置",
			zap.String("order_no", orderNo),
			zap.Error(err),
		)
	}

	if configID != nil {
		cfg, err := s.store.GetByIDUnscoped(ctx, *configID)
		if err == nil {
			return cfg, nil
		}
		s.logger.Warn("回调：按 config_id 加载配置失败，回退激活配置",
			zap.Uint("config_id", *configID),
			zap.Error(err),
		)
	}

	return s.GetActiveConfig(ctx)
}

// resolvePaymentConfigID 按订单号前缀从对应表中取 payment_config_id
func (s *Service) resolvePaymentConfigID(ctx context.Context, orderNo string) (*uint, error) {
	if s.paymentStore != nil {
		payment, err := s.paymentStore.GetByPaymentNo(ctx, orderNo)
		if err == nil {
			if payment.PaymentConfigID != nil {
				return payment.PaymentConfigID, nil
			}
		} else if err != gorm.ErrRecordNotFound {
			return nil, err
		}
	}

	switch {
	case len(orderNo) >= 3 && orderNo[:3] == "ORD":
		order, err := s.orderStore.GetByOrderNo(ctx, orderNo)
		if err != nil {
			return nil, err
		}
		return order.PaymentConfigID, nil

	case len(orderNo) >= 4 && orderNo[:4] == constants.AssetRechargeOrderPrefix:
		record, err := s.rechargeOrderStore.GetByRechargeOrderNo(ctx, orderNo)
		if err != nil {
			return nil, err
		}
		return record.PaymentConfigID, nil

	case len(orderNo) >= 4 && orderNo[:4] == constants.AgentRechargeOrderPrefix:
		record, err := s.agentRechargeStore.GetByRechargeNo(ctx, orderNo)
		if err != nil {
			return nil, err
		}
		return record.PaymentConfigID, nil

	default:
		return nil, fmt.Errorf("未知订单号前缀: %s", orderNo)
	}
}
