// Package systemconfig 提供受控系统配置的简单写事务脚本。
package systemconfig

import (
	"context"
	"crypto/sha256"
	"encoding/hex"
	"time"

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

	configinfra "github.com/break/junhong_cmp_fiber/internal/infrastructure/systemconfig"
	"github.com/break/junhong_cmp_fiber/internal/model"
	"github.com/break/junhong_cmp_fiber/internal/model/dto"
	"github.com/break/junhong_cmp_fiber/pkg/constants"
	"github.com/break/junhong_cmp_fiber/pkg/errors"
	"github.com/break/junhong_cmp_fiber/pkg/middleware"
)

// ChangeAudit 是系统配置更新交给统一审计 Port 的事实。
type ChangeAudit struct {
	OperatorID    uint
	OperationType string
	Description   string
	ConfigKey     string
	BeforeData    map[string]any
	AfterData     map[string]any
	RequestID     string
	CorrelationID string
}

// AuditWriter 可选接收系统配置事务内审计事实。
type AuditWriter interface {
	WriteConfigChange(ctx context.Context, tx *gorm.DB, audit ChangeAudit) error
}

// UpdateService 执行单 Key 校验、事务更新、审计和提交后缓存失效。
type UpdateService struct {
	db       *gorm.DB
	registry *configinfra.Registry
	cache    configinfra.Cache
	audit    AuditWriter
	alerts   configinfra.AlertSink
	now      func() time.Time
}

// NewUpdateService 创建系统配置更新事务脚本。
func NewUpdateService(
	db *gorm.DB,
	registry *configinfra.Registry,
	cache configinfra.Cache,
	audit AuditWriter,
	alerts configinfra.AlertSink,
	now func() time.Time,
) *UpdateService {
	if now == nil {
		now = time.Now
	}
	return &UpdateService{db: db, registry: registry, cache: cache, audit: audit, alerts: alerts, now: now}
}

// Execute 更新一个已注册且非只读的配置 Key。
func (s *UpdateService) Execute(ctx context.Context, key string, request dto.UpdateSystemConfigRequest) (*dto.SystemConfigItem, error) {
	if middleware.GetUserTypeFromContext(ctx) != constants.UserTypeSuperAdmin {
		return nil, errors.New(errors.CodeForbidden)
	}
	operatorID := middleware.GetUserIDFromContext(ctx)
	if operatorID == 0 || key == "" {
		return nil, errors.New(errors.CodeInvalidParam)
	}
	definition, registered := s.registry.Get(key)
	if !registered {
		return nil, errors.New(errors.CodeInvalidParam, "系统配置 Key 未注册")
	}
	if definition.Readonly {
		return nil, errors.New(errors.CodeInvalidStatus, "系统配置为只读，不能更新")
	}
	if err := configinfra.ValidateValue(definition, request.Value); err != nil {
		return nil, errors.New(errors.CodeInvalidParam, "系统配置值不符合注册规则")
	}
	now := s.now().UTC()
	var saved model.SystemConfig
	err := s.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
		// 同一 Key 的首次创建和后续更新都由 PostgreSQL 事务级咨询锁串行裁决。
		if err := tx.Exec("SELECT pg_advisory_xact_lock(hashtext(?))", key).Error; err != nil {
			return err
		}
		var existing model.SystemConfig
		findErr := tx.Clauses(clause.Locking{Strength: "UPDATE"}).Where("config_key = ?", key).First(&existing).Error
		beforeValue := definition.DefaultValue
		if findErr == nil {
			beforeValue = existing.ConfigValue
		} else if findErr != gorm.ErrRecordNotFound {
			return findErr
		}
		if findErr == gorm.ErrRecordNotFound {
			existing = model.SystemConfig{
				ConfigKey: key, ConfigValue: request.Value, ValueType: definition.ValueType,
				Module: definition.Module, Description: definition.Description,
				IsReadonly: definition.Readonly, IsSensitive: definition.Sensitive,
				Creator: operatorID, Updater: operatorID, CreatedAt: now, UpdatedAt: now,
			}
			if err := tx.Create(&existing).Error; err != nil {
				return err
			}
		} else {
			if err := tx.Model(&model.SystemConfig{}).Where("id = ?", existing.ID).Updates(map[string]any{
				"config_value": request.Value, "value_type": definition.ValueType,
				"module": definition.Module, "description": definition.Description,
				"is_readonly": definition.Readonly, "is_sensitive": definition.Sensitive,
				"updater": operatorID, "updated_at": now,
			}).Error; err != nil {
				return err
			}
			existing.ConfigValue = request.Value
			existing.Updater = operatorID
			existing.UpdatedAt = now
		}
		requestID := ""
		if value := middleware.GetRequestIDFromContext(ctx); value != nil {
			requestID = *value
		}
		if s.audit != nil {
			if err := s.audit.WriteConfigChange(ctx, tx, ChangeAudit{
				OperatorID: operatorID, OperationType: "system_config_update", Description: "更新受控系统配置",
				ConfigKey:  key,
				BeforeData: map[string]any{"config_key": key, "value": auditValue(definition, beforeValue)},
				AfterData:  map[string]any{"config_key": key, "value": auditValue(definition, request.Value)},
				RequestID:  requestID, CorrelationID: requestID,
			}); err != nil {
				return err
			}
		}
		saved = existing
		return nil
	})
	if err != nil {
		return nil, errors.Wrap(errors.CodeDatabaseError, err, "更新系统配置失败")
	}
	s.registry.Remember(key, request.Value)
	if s.cache != nil {
		if err := s.cache.Delete(ctx, constants.RedisSystemConfigKey(key)); err != nil {
			if s.alerts != nil {
				s.alerts.Warn(ctx, "SYSTEM_CONFIG_CACHE_INVALIDATE_FAILED", "system_config", key, "系统配置缓存失效失败，数据库事实已提交")
			}
		}
	}
	value := saved.ConfigValue
	if definition.Sensitive && value != "" {
		value = "[已配置]"
	}
	updatedAt := saved.UpdatedAt
	return &dto.SystemConfigItem{
		ConfigKey: key, Value: value, ValueType: definition.ValueType, Module: definition.Module,
		Description: definition.Description, Readonly: definition.Readonly, Sensitive: definition.Sensitive,
		Registered: true, Control: definition.Control, EnumValues: definition.EnumValues,
		Min: definition.Min, Max: definition.Max, UpdatedAt: &updatedAt,
	}, nil
}

func auditValue(definition configinfra.Definition, value string) string {
	if !definition.Sensitive {
		return value
	}
	sum := sha256.Sum256([]byte(value))
	return "[敏感值 sha256:" + hex.EncodeToString(sum[:8]) + "]"
}
