package integration

import (
	"context"
	"encoding/json"
	"errors"
	integrationbiz "kra/internal/biz/integration"
	"strings"
	"time"

	"kra/internal/integration/runtimeconfig"

	"gorm.io/gorm"
)

type ConfigPO struct {
	ID        uint `gorm:"primaryKey"`
	CreatedAt time.Time
	UpdatedAt time.Time
	Kind      string `gorm:"size:32;not null;uniqueIndex:idx_integration_kind_provider"`
	Provider  string `gorm:"size:64;not null;uniqueIndex:idx_integration_kind_provider"`
	Enabled   bool
	Config    string `gorm:"type:text;not null"`
}

func (ConfigPO) TableName() string { return "sys_integration_configs" }

type integrationConfigRepo struct{ data Provider }

type paymentConfigReader struct{ data Provider }

func NewIntegrationConfigRepo(data Provider) integrationbiz.IntegrationConfigRepo {
	return &integrationConfigRepo{data: data}
}

// NewPaymentConfigReader exposes only the raw payment configuration needed by
// the payment data module. The ConfigPO and its table name stay private here.
func NewPaymentConfigReader(data Provider) integrationbiz.PaymentConfigReader {
	return &paymentConfigReader{data: data}
}

func (r *paymentConfigReader) ReadPaymentConfig(ctx context.Context, provider string) (*integrationbiz.PaymentConfig, error) {
	if r == nil || r.data == nil || r.data.DB() == nil {
		return nil, errors.New("集成配置数据库未初始化")
	}
	provider = strings.ToLower(strings.TrimSpace(provider))
	if provider == "" {
		return nil, errors.New("支付渠道不能为空")
	}
	var row ConfigPO
	if err := r.data.DB().WithContext(ctx).
		Where("kind = ? AND provider = ?", integrationbiz.IntegrationKindPayment, provider).
		First(&row).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, integrationbiz.ErrPaymentConfigNotFound
		}
		return nil, err
	}
	return &integrationbiz.PaymentConfig{Enabled: row.Enabled, Values: append(json.RawMessage(nil), []byte(row.Config)...)}, nil
}

func (r *integrationConfigRepo) ListIntegrationConfigs(ctx context.Context, kind string) ([]*integrationbiz.IntegrationConfig, error) {
	var rows []ConfigPO
	if err := r.data.DB().WithContext(ctx).Where("kind = ?", kind).Order("provider ASC").Find(&rows).Error; err != nil {
		return nil, err
	}
	result := make([]*integrationbiz.IntegrationConfig, 0, len(rows))
	for _, row := range rows {
		result = append(result, integrationConfigFromPO(row))
	}
	return result, nil
}

func (r *integrationConfigRepo) FindIntegrationConfig(ctx context.Context, kind, provider string) (*integrationbiz.IntegrationConfig, error) {
	var row ConfigPO
	if err := r.data.DB().WithContext(ctx).Where("kind = ? AND provider = ?", kind, provider).First(&row).Error; err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, errors.New("集成配置不存在")
		}
		return nil, err
	}
	return integrationConfigFromPO(row), nil
}

func (r *integrationConfigRepo) SaveIntegrationConfig(ctx context.Context, config *integrationbiz.IntegrationConfig) error {
	db := r.data.DB().WithContext(ctx)
	var row ConfigPO
	err := db.Where("kind = ? AND provider = ?", config.Kind, config.Provider).First(&row).Error
	values := integrationObject(config.Values)
	if errors.Is(err, gorm.ErrRecordNotFound) {
		if config.Enabled {
			if err = integrationbiz.ValidateIntegrationConfig(config.Kind, config.Provider, values); err != nil {
				return err
			}
		}
		encoded, _ := json.Marshal(values)
		if err := db.Create(&ConfigPO{Kind: config.Kind, Provider: config.Provider, Enabled: config.Enabled, Config: string(encoded)}).Error; err != nil {
			return err
		}
		r.publish(config.Kind, config.Provider, config.Enabled, encoded)
		return nil
	}
	if err != nil {
		return err
	}
	mergeIntegrationSecrets(config.Kind, config.Provider, values, integrationObject(json.RawMessage(row.Config)))
	if config.Enabled {
		if err = integrationbiz.ValidateIntegrationConfig(config.Kind, config.Provider, values); err != nil {
			return err
		}
	}
	encoded, _ := json.Marshal(values)
	if err := db.Model(&row).Updates(map[string]any{"enabled": config.Enabled, "config": string(encoded)}).Error; err != nil {
		return err
	}
	r.publish(config.Kind, config.Provider, config.Enabled, encoded)
	return nil
}

func (r *integrationConfigRepo) DeleteIntegrationConfig(ctx context.Context, kind, provider string) error {
	if err := r.data.DB().WithContext(ctx).Where("kind = ? AND provider = ?", kind, provider).Delete(&ConfigPO{}).Error; err != nil {
		return err
	}
	if runtime := integrationRuntime(r.data); runtime != nil {
		runtime.Delete(kind, provider)
	}
	return nil
}

func (r *integrationConfigRepo) publish(kind, provider string, enabled bool, values []byte) {
	if runtime := integrationRuntime(r.data); runtime != nil {
		runtime.Set(runtimeconfig.Config{Kind: kind, Provider: provider, Enabled: enabled, Values: values})
	}
}

func integrationRuntime(provider Provider) *runtimeconfig.Store {
	if provider != nil {
		return provider.IntegrationRuntime()
	}
	return nil
}

func integrationConfigFromPO(row ConfigPO) *integrationbiz.IntegrationConfig {
	values := integrationObject(json.RawMessage(row.Config))
	maskIntegrationSecrets(row.Kind, row.Provider, values)
	encoded, _ := json.Marshal(values)
	return &integrationbiz.IntegrationConfig{Kind: row.Kind, Provider: row.Provider, Enabled: row.Enabled, Values: encoded}
}

func integrationObject(raw json.RawMessage) map[string]any {
	values := map[string]any{}
	_ = json.Unmarshal(raw, &values)
	return values
}

func maskIntegrationSecrets(kind, provider string, values map[string]any) {
	secretFields := integrationSecretFields(kind, provider)
	for key, value := range values {
		if secretFields[key] || likelyIntegrationSecret(key) {
			if text, ok := value.(string); ok && text != "" {
				values[key] = "******"
			}
			continue
		}
		if nested, ok := value.(map[string]any); ok {
			maskIntegrationSecrets(kind, provider, nested)
		}
	}
}

func mergeIntegrationSecrets(kind, provider string, values, old map[string]any) {
	secretFields := integrationSecretFields(kind, provider)
	for key, value := range values {
		if secretFields[key] || likelyIntegrationSecret(key) {
			if text, ok := value.(string); ok && text == "******" {
				if prior, exists := old[key]; exists {
					values[key] = prior
				}
			}
			continue
		}
		if nested, ok := value.(map[string]any); ok {
			if prior, ok := old[key].(map[string]any); ok {
				mergeIntegrationSecrets(kind, provider, nested, prior)
			}
		}
	}
}

func integrationSecretFields(kind, provider string) map[string]bool {
	result := map[string]bool{}
	if definition, ok := integrationbiz.IntegrationDefinition(kind, provider); ok {
		for _, field := range definition.Fields {
			if field.Secret {
				result[field.Key] = true
			}
		}
	}
	return result
}

func likelyIntegrationSecret(key string) bool {
	normalized := strings.ToLower(strings.ReplaceAll(key, "-", "_"))
	if strings.Contains(normalized, "secret") || strings.Contains(normalized, "private") || strings.Contains(normalized, "password") || strings.Contains(normalized, "credential") {
		return true
	}
	for _, item := range []string{"key", "token", "access_token", "api_key", "mch_key", "client_key", "certificate", "cert", "p12", "pkcs12", "public_key", "platform_cert", "root_cert"} {
		if normalized == item || strings.HasSuffix(normalized, "_"+item) {
			return true
		}
	}
	return false
}
