// Package paymentmethod 提供按资产类型读取和校验 C 端支付方式的能力。
package paymentmethod

import (
	"context"
	stderrors "errors"
	"slices"

	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/bytedance/sonic"
)

// ConfigReader 提供受控系统配置读取能力。
type ConfigReader interface {
	GetStrict(ctx context.Context, key string) (string, error)
}

// Policy 负责返回资产允许的支付方式并执行后端强校验。
type Policy struct {
	reader ConfigReader
}

// NewPolicy 创建支付方式策略。
func NewPolicy(reader ConfigReader) *Policy {
	return &Policy{reader: reader}
}

// AllowedMethods 返回指定资产类型允许的支付方式；配置异常时失败关闭。
func (p *Policy) AllowedMethods(ctx context.Context, assetType string) ([]string, error) {
	if p == nil || p.reader == nil {
		return nil, errors.New(errors.CodeNoPaymentConfig)
	}
	key, err := configKey(assetType)
	if err != nil {
		return nil, err
	}
	raw, err := p.reader.GetStrict(ctx, key)
	if err != nil {
		return nil, errors.Wrap(errors.CodeNoPaymentConfig, err, "读取支付方式配置失败")
	}
	var methods []string
	if err := sonic.Unmarshal([]byte(raw), &methods); err != nil || !validMethods(methods) {
		return nil, errors.New(errors.CodeNoPaymentConfig, "支付方式配置无效")
	}
	return stableMethods(methods), nil
}

// EnsureAllowed 校验指定支付方式是否允许。
func (p *Policy) EnsureAllowed(ctx context.Context, assetType, paymentMethod string) error {
	methods, err := p.AllowedMethods(ctx, assetType)
	if err != nil {
		return err
	}
	if !slices.Contains(methods, paymentMethod) {
		return errors.New(errors.CodePaymentMethodUnavailable)
	}
	return nil
}

// AllowedRechargeMethods 返回资产允许且钱包充值接口支持的第三方支付方式。
func (p *Policy) AllowedRechargeMethods(ctx context.Context, assetType string) ([]string, error) {
	methods, err := p.AllowedMethods(ctx, assetType)
	if err != nil {
		return nil, err
	}
	result := make([]string, 0, 2)
	for _, method := range methods {
		if method == model.PaymentMethodWechat || method == model.PaymentMethodAlipay {
			result = append(result, method)
		}
	}
	return result, nil
}

// EnsureRechargeAllowed 校验普通充值只使用资产配置允许的第三方支付方式。
func (p *Policy) EnsureRechargeAllowed(ctx context.Context, assetType, paymentMethod string) error {
	methods, err := p.AllowedRechargeMethods(ctx, assetType)
	if err != nil {
		return err
	}
	if !slices.Contains(methods, paymentMethod) {
		return errors.New(errors.CodePaymentMethodUnavailable)
	}
	return nil
}

func configKey(assetType string) (string, error) {
	switch assetType {
	case "card", constants.AssetTypeIotCard, model.OrderTypeSingleCard:
		return constants.SystemConfigPaymentAllowedCard, nil
	case constants.AssetTypeDevice:
		return constants.SystemConfigPaymentAllowedDevice, nil
	default:
		return "", errors.New(errors.CodeInvalidParam, "无效的资产类型")
	}
}

func validMethods(methods []string) bool {
	if len(methods) == 0 {
		return false
	}
	seen := make(map[string]struct{}, len(methods))
	for _, method := range methods {
		if method != model.PaymentMethodWallet && method != model.PaymentMethodWechat && method != model.PaymentMethodAlipay {
			return false
		}
		if _, exists := seen[method]; exists {
			return false
		}
		seen[method] = struct{}{}
	}
	return true
}

// ValidateConfigValue 校验支付方式系统配置的 JSON 集合。
func ValidateConfigValue(value string) error {
	var methods []string
	if err := sonic.Unmarshal([]byte(value), &methods); err != nil || !validMethods(methods) {
		return stderrors.New("支付方式配置必须是非空且不重复的合法数组")
	}
	return nil
}

func stableMethods(methods []string) []string {
	result := make([]string, 0, len(methods))
	for _, method := range []string{model.PaymentMethodWallet, model.PaymentMethodWechat, model.PaymentMethodAlipay} {
		if slices.Contains(methods, method) {
			result = append(result, method)
		}
	}
	return result
}
