// Copyright © 2023 OpenIM. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package webhook

import (
	"context"
	"encoding/json"
	"sync"
	"time"

	"git.imall.cloud/openim/open-im-server-deploy/pkg/common/config"
	"git.imall.cloud/openim/open-im-server-deploy/pkg/common/storage/model"
	"git.imall.cloud/openim/open-im-server-deploy/pkg/common/storage/database"
	"github.com/openimsdk/tools/errs"
	"github.com/openimsdk/tools/log"
	"github.com/openimsdk/tools/utils/datautil"
)

const (
	// WebhookConfigKey 数据库中的webhook配置键
	WebhookConfigKey = "webhook_config"
	// DefaultRefreshInterval 默认刷新间隔（30秒，方便调试）
	DefaultRefreshInterval = 30 * time.Second
)

// ConfigManager webhook配置管理器，支持从数据库读取和定时刷新
type ConfigManager struct {
	db              database.SystemConfig
	defaultConfig   *config.Webhooks
	mu              sync.RWMutex
	cachedConfig    *config.Webhooks
	lastUpdate      time.Time
	refreshInterval time.Duration
	stopCh          chan struct{}
}

// NewConfigManager 创建webhook配置管理器
func NewConfigManager(db database.SystemConfig, defaultConfig *config.Webhooks) *ConfigManager {
	cm := &ConfigManager{
		db:              db,
		defaultConfig:   defaultConfig,
		cachedConfig:    defaultConfig,
		refreshInterval: DefaultRefreshInterval,
		stopCh:          make(chan struct{}),
	}
	return cm
}

// Start 启动配置管理器，开始定时刷新
func (cm *ConfigManager) Start(ctx context.Context) error {
	// 立即加载一次配置
	log.ZInfo(ctx, "webhook config manager starting, initial refresh...")
	if err := cm.Refresh(ctx); err != nil {
		log.ZWarn(ctx, "initial webhook config refresh failed, using default config", err)
	} else {
		currentConfig := cm.GetConfig()
		log.ZInfo(ctx, "webhook config manager started successfully", "url", currentConfig.URL, "refresh_interval", cm.refreshInterval)
	}

	// 启动定时刷新goroutine
	go cm.refreshLoop(ctx)
	return nil
}

// Stop 停止配置管理器
func (cm *ConfigManager) Stop() {
	close(cm.stopCh)
}

// refreshLoop 定时刷新循环
func (cm *ConfigManager) refreshLoop(ctx context.Context) {
	ticker := time.NewTicker(cm.refreshInterval)
	defer ticker.Stop()

	log.ZInfo(ctx, "webhook config refresh loop started", "interval", cm.refreshInterval)

	for {
		select {
		case <-ticker.C:
			log.ZDebug(ctx, "webhook config scheduled refresh triggered")
			if err := cm.Refresh(ctx); err != nil {
				log.ZWarn(ctx, "webhook config refresh failed", err)
			}
		case <-cm.stopCh:
			log.ZInfo(ctx, "webhook config refresh loop stopped")
			return
		}
	}
}

// Refresh 从数据库刷新配置
func (cm *ConfigManager) Refresh(ctx context.Context) error {
	// 从数据库读取配置
	sysConfig, err := cm.db.FindByKey(ctx, WebhookConfigKey)
	if err != nil {
		// 如果查询出错，使用默认配置
		log.ZWarn(ctx, "failed to get webhook config from database, using default config", err)
		cm.mu.Lock()
		cm.cachedConfig = cm.defaultConfig
		cm.lastUpdate = time.Now()
		cm.mu.Unlock()
		return nil
	}

	// 如果数据库中没有配置，使用默认配置
	if sysConfig == nil {
		cm.mu.Lock()
		cm.cachedConfig = cm.defaultConfig
		cm.lastUpdate = time.Now()
		cm.mu.Unlock()
		log.ZInfo(ctx, "webhook config not found in database, using default config", "default_url", cm.defaultConfig.URL)
		return nil
	}

	// 检查值类型
	if sysConfig.ValueType != model.SystemConfigValueTypeJSON {
		cm.mu.Lock()
		cm.cachedConfig = cm.defaultConfig
		cm.lastUpdate = time.Now()
		cm.mu.Unlock()
		log.ZWarn(ctx, "webhook config valueType is not json, using default config", nil, "value_type", sysConfig.ValueType)
		return nil
	}

	// 如果配置被禁用，使用默认配置
	if !sysConfig.Enabled {
		cm.mu.Lock()
		cm.cachedConfig = cm.defaultConfig
		cm.lastUpdate = time.Now()
		cm.mu.Unlock()
		log.ZInfo(ctx, "webhook config is disabled, using default config", "default_url", cm.defaultConfig.URL)
		return nil
	}

	// 如果配置值为空，使用默认配置
	if sysConfig.Value == "" {
		cm.mu.Lock()
		cm.cachedConfig = cm.defaultConfig
		cm.lastUpdate = time.Now()
		cm.mu.Unlock()
		log.ZInfo(ctx, "webhook config value is empty, using default config", "default_url", cm.defaultConfig.URL)
		return nil
	}

	valuePreview := sysConfig.Value
	if len(valuePreview) > 100 {
		valuePreview = valuePreview[:100] + "..."
	}
	log.ZDebug(ctx, "webhook config value found", "value_length", len(sysConfig.Value), "value_preview", valuePreview)

	// 解析配置
	var webhookConfig config.Webhooks
	if err := json.Unmarshal([]byte(sysConfig.Value), &webhookConfig); err != nil {
		// 如果解析失败，使用默认配置
		log.ZWarn(ctx, "failed to unmarshal webhook config, using default config", err)
		cm.mu.Lock()
		cm.cachedConfig = cm.defaultConfig
		cm.lastUpdate = time.Now()
		cm.mu.Unlock()
		return nil
	}

	// 验证解析后的配置，确保AttentionIds不为nil
	if webhookConfig.AfterSendGroupMsg.AttentionIds == nil {
		webhookConfig.AfterSendGroupMsg.AttentionIds = []string{}
	}

	normalized, ok := normalizeWebhookConfig(cm.defaultConfig, &webhookConfig)
	if !ok {
		log.ZWarn(ctx, "webhook config URL is empty, using default config", nil)
		cm.mu.Lock()
		cm.cachedConfig = cm.defaultConfig
		cm.lastUpdate = time.Now()
		cm.mu.Unlock()
		return nil
	}

	// 更新缓存
	cm.mu.Lock()
	oldURL := cm.cachedConfig.URL
	oldAttentionIdsCount := len(cm.cachedConfig.AfterSendGroupMsg.AttentionIds)
	cm.cachedConfig = normalized
	cm.lastUpdate = time.Now()
	newAttentionIdsCount := len(normalized.AfterSendGroupMsg.AttentionIds)
	cm.mu.Unlock()

	// 如果URL或AttentionIds发生变化，记录日志
	urlChanged := oldURL != webhookConfig.URL
	attentionIdsChanged := oldAttentionIdsCount != newAttentionIdsCount
	if urlChanged || attentionIdsChanged {
		log.ZInfo(ctx, "webhook config updated from database",
			"old_url", oldURL, "new_url", webhookConfig.URL,
			"old_attention_ids_count", oldAttentionIdsCount,
			"new_attention_ids_count", newAttentionIdsCount,
			"url_changed", urlChanged,
			"attention_ids_changed", attentionIdsChanged)
	} else {
		log.ZDebug(ctx, "webhook config refreshed (no change)", "url", webhookConfig.URL, "attention_ids_count", newAttentionIdsCount)
	}

	return nil
}

// GetConfig 获取当前缓存的配置
func (cm *ConfigManager) GetConfig() *config.Webhooks {
	cm.mu.RLock()
	defer cm.mu.RUnlock()
	return cm.cachedConfig
}

// normalizeWebhookConfig 校验并填充缺省值，返回是否有效
func normalizeWebhookConfig(defaultCfg *config.Webhooks, cfg *config.Webhooks) (*config.Webhooks, bool) {
	if cfg == nil {
		return nil, false
	}
	if cfg.URL == "" {
		return nil, false
	}

	normalized := *cfg // 浅拷贝

	// 填充默认值，避免数据库配置错误导致回调不可用
	if normalized.AfterSendGroupMsg.AttentionIds == nil {
		normalized.AfterSendGroupMsg.AttentionIds = []string{}
	}

	applyBeforeDefaults := func(dst *config.BeforeConfig, fallback config.BeforeConfig) {
		if dst.Timeout <= 0 {
			dst.Timeout = fallback.Timeout
		}
	}
	applyAfterDefaults := func(dst *config.AfterConfig, fallback config.AfterConfig) {
		if dst.Timeout <= 0 {
			dst.Timeout = fallback.Timeout
		}
		if dst.AttentionIds == nil {
			dst.AttentionIds = []string{}
		}
	}

	if defaultCfg != nil {
		applyBeforeDefaults(&normalized.BeforeSendSingleMsg, defaultCfg.BeforeSendSingleMsg)
		applyBeforeDefaults(&normalized.BeforeUpdateUserInfoEx, defaultCfg.BeforeUpdateUserInfoEx)
		applyAfterDefaults(&normalized.AfterUpdateUserInfoEx, defaultCfg.AfterUpdateUserInfoEx)
		applyAfterDefaults(&normalized.AfterSendSingleMsg, defaultCfg.AfterSendSingleMsg)
		applyBeforeDefaults(&normalized.BeforeSendGroupMsg, defaultCfg.BeforeSendGroupMsg)
		applyBeforeDefaults(&normalized.BeforeMsgModify, defaultCfg.BeforeMsgModify)
		applyAfterDefaults(&normalized.AfterSendGroupMsg, defaultCfg.AfterSendGroupMsg)
		applyAfterDefaults(&normalized.AfterMsgSaveDB, defaultCfg.AfterMsgSaveDB)
		applyAfterDefaults(&normalized.AfterUserOnline, defaultCfg.AfterUserOnline)
		applyAfterDefaults(&normalized.AfterUserOffline, defaultCfg.AfterUserOffline)
		applyAfterDefaults(&normalized.AfterUserKickOff, defaultCfg.AfterUserKickOff)
		applyBeforeDefaults(&normalized.BeforeOfflinePush, defaultCfg.BeforeOfflinePush)
		applyBeforeDefaults(&normalized.BeforeOnlinePush, defaultCfg.BeforeOnlinePush)
		applyBeforeDefaults(&normalized.BeforeGroupOnlinePush, defaultCfg.BeforeGroupOnlinePush)
		applyBeforeDefaults(&normalized.BeforeAddFriend, defaultCfg.BeforeAddFriend)
		applyBeforeDefaults(&normalized.BeforeUpdateUserInfo, defaultCfg.BeforeUpdateUserInfo)
		applyAfterDefaults(&normalized.AfterUpdateUserInfo, defaultCfg.AfterUpdateUserInfo)
		applyBeforeDefaults(&normalized.BeforeCreateGroup, defaultCfg.BeforeCreateGroup)
		applyAfterDefaults(&normalized.AfterCreateGroup, defaultCfg.AfterCreateGroup)
		applyBeforeDefaults(&normalized.BeforeMemberJoinGroup, defaultCfg.BeforeMemberJoinGroup)
		applyBeforeDefaults(&normalized.BeforeSetGroupMemberInfo, defaultCfg.BeforeSetGroupMemberInfo)
		applyAfterDefaults(&normalized.AfterSetGroupMemberInfo, defaultCfg.AfterSetGroupMemberInfo)
		applyAfterDefaults(&normalized.AfterQuitGroup, defaultCfg.AfterQuitGroup)
		applyAfterDefaults(&normalized.AfterKickGroupMember, defaultCfg.AfterKickGroupMember)
		applyAfterDefaults(&normalized.AfterDismissGroup, defaultCfg.AfterDismissGroup)
	}

	return &normalized, true
}

// GetURL 获取当前webhook URL
func (cm *ConfigManager) GetURL() string {
	cm.mu.RLock()
	defer cm.mu.RUnlock()
	return cm.cachedConfig.URL
}

// SetRefreshInterval 设置刷新间隔
func (cm *ConfigManager) SetRefreshInterval(interval time.Duration) {
	cm.mu.Lock()
	defer cm.mu.Unlock()
	cm.refreshInterval = interval
}

// GetLastUpdate 获取最后更新时间
func (cm *ConfigManager) GetLastUpdate() time.Time {
	cm.mu.RLock()
	defer cm.mu.RUnlock()
	return cm.lastUpdate
}

// UpdateAttentionIds 更新webhook配置中的attentionIds（添加或移除群ID）
// add: true表示添加groupID，false表示移除groupID
func UpdateAttentionIds(ctx context.Context, db database.SystemConfig, groupID string, add bool) error {
	if groupID == "" {
		return nil
	}

	if db == nil {
		return nil
	}

	// 获取当前配置
	sysConfig, err := db.FindByKey(ctx, WebhookConfigKey)
	if err != nil {
		log.ZWarn(ctx, "UpdateAttentionIds: failed to get webhook config from database", err, "groupID", groupID, "add", add)
		return errs.WrapMsg(err, "failed to get webhook config from database")
	}

	if sysConfig == nil {
		log.ZDebug(ctx, "UpdateAttentionIds: webhook config not found in database, skipping", "groupID", groupID, "add", add)
		return nil
	}

	if !sysConfig.Enabled {
		log.ZDebug(ctx, "UpdateAttentionIds: webhook config is disabled, skipping", "groupID", groupID, "add", add)
		return nil
	}

	if sysConfig.Value == "" {
		log.ZDebug(ctx, "UpdateAttentionIds: webhook config value is empty, skipping", "groupID", groupID, "add", add)
		return nil
	}

	// 检查值类型
	if sysConfig.ValueType != model.SystemConfigValueTypeJSON {
		log.ZWarn(ctx, "UpdateAttentionIds: webhook config valueType is not json, skipping", nil, "groupID", groupID, "add", add, "value_type", sysConfig.ValueType)
		return nil
	}

	// 解析当前配置
	var webhookConfig config.Webhooks
	if err := json.Unmarshal([]byte(sysConfig.Value), &webhookConfig); err != nil {
		log.ZWarn(ctx, "UpdateAttentionIds: failed to unmarshal webhook config", err, "groupID", groupID, "add", add)
		return errs.WrapMsg(err, "failed to unmarshal webhook config")
	}

	// 记录更新前的Enable状态
	enableBefore := webhookConfig.AfterSendGroupMsg.Enable

	// 验证解析后的配置是否有效
	if webhookConfig.AfterSendGroupMsg.AttentionIds == nil {
		webhookConfig.AfterSendGroupMsg.AttentionIds = []string{}
	}

	// 更新afterSendGroupMsg的attentionIds
	attentionIds := webhookConfig.AfterSendGroupMsg.AttentionIds
	oldCount := len(attentionIds)
	var updated bool

	if add {
		// 添加groupID（如果不存在）
		if !datautil.Contain(groupID, attentionIds...) {
			attentionIds = append(attentionIds, groupID)
			webhookConfig.AfterSendGroupMsg.AttentionIds = attentionIds
			updated = true
			log.ZInfo(ctx, "UpdateAttentionIds: adding groupID to attentionIds", "groupID", groupID, "old_count", oldCount, "new_count", len(attentionIds), "enable", enableBefore)
		} else {
			log.ZDebug(ctx, "UpdateAttentionIds: groupID already exists in attentionIds, skipping", "groupID", groupID, "count", oldCount)
			return nil
		}
	} else {
		// 移除groupID
		newAttentionIds := make([]string, 0, len(attentionIds))
		for _, id := range attentionIds {
			if id != groupID {
				newAttentionIds = append(newAttentionIds, id)
			}
		}
		if len(newAttentionIds) != len(attentionIds) {
			webhookConfig.AfterSendGroupMsg.AttentionIds = newAttentionIds
			updated = true
			log.ZInfo(ctx, "UpdateAttentionIds: removing groupID from attentionIds", "groupID", groupID, "old_count", oldCount, "new_count", len(newAttentionIds), "enable", enableBefore)
		} else {
			log.ZDebug(ctx, "UpdateAttentionIds: groupID not found in attentionIds, skipping", "groupID", groupID, "count", oldCount)
			return nil
		}
	}

	if !updated {
		return nil
	}

	// 验证配置URL是否存在（必要的验证）
	if webhookConfig.URL == "" {
		log.ZWarn(ctx, "UpdateAttentionIds: webhook config URL is empty, skipping update", nil, "groupID", groupID, "add", add)
		return errs.ErrArgs.WrapMsg("webhook config URL is empty")
	}

	// 记录更新后的Enable状态（确保没有被修改）
	enableAfter := webhookConfig.AfterSendGroupMsg.Enable
	if enableBefore != enableAfter {
		log.ZWarn(ctx, "UpdateAttentionIds: Enable field changed unexpectedly", nil,
			"groupID", groupID, "add", add,
			"enable_before", enableBefore, "enable_after", enableAfter)
		// 恢复原始值，确保不会修改Enable字段
		webhookConfig.AfterSendGroupMsg.Enable = enableBefore
	}

	// 序列化更新后的配置（只更新attentionIds，不改变其他配置）
	updatedValue, err := json.Marshal(webhookConfig)
	if err != nil {
		log.ZWarn(ctx, "UpdateAttentionIds: failed to marshal updated webhook config", err, "groupID", groupID, "add", add)
		return errs.WrapMsg(err, "failed to marshal updated webhook config")
	}

	// 更新数据库
	if err := db.Update(ctx, WebhookConfigKey, map[string]any{
		"value": string(updatedValue),
	}); err != nil {
		log.ZWarn(ctx, "UpdateAttentionIds: failed to update webhook config in database", err, "groupID", groupID, "add", add)
		return errs.WrapMsg(err, "failed to update webhook config in database")
	}

	log.ZInfo(ctx, "UpdateAttentionIds: successfully updated attentionIds in database",
		"groupID", groupID, "add", add,
		"attention_ids_count", len(webhookConfig.AfterSendGroupMsg.AttentionIds),
		"enable", webhookConfig.AfterSendGroupMsg.Enable)
	return nil
}
