package wxwork

import (
	"log/slog"
	"strings"
	"sync"

	"github.com/panjf2000/ants/v2"
	"github.com/silenceper/wechat/v2/work/kf"
)

const (
	callbackWorkerCount       = 16    // worker 数量
	callbackMaxBlockingTasks  = 10000 // 队列大小
	callbackMsgTypeEvent      = "event"
	callbackEventKFMsgOrEvent = "kf_msg_or_event"
)

type CallbackHandler func(message kf.CallbackMessage)

var (
	callbackHandlerMu sync.RWMutex
	callbackHandlers  map[string]CallbackHandler
	callbackPool      *ants.PoolWithFunc
)

func init() {
	var err error

	callbackHandlers = make(map[string]CallbackHandler)
	callbackPool, err = ants.NewPoolWithFunc(
		callbackWorkerCount,
		dispatchCallbackMessage,
		ants.WithMaxBlockingTasks(callbackMaxBlockingTasks),
	)
	if err != nil {
		slog.Error("init wxwork callback pool failed", "error", err)
	}
}

// ConsumeCallback 异步消费企业微信客服回调。
func ConsumeCallback(message kf.CallbackMessage) error {
	if callbackPool == nil {
		slog.Error("wxwork callback pool is not initialized",
			"msg_type", message.MsgType,
			"event", message.Event,
			"open_kfid", message.OpenKfID,
		)
		return nil
	}

	if err := callbackPool.Invoke(message); err != nil {
		slog.Error("invoke wxwork callback failed",
			"msg_type", message.MsgType,
			"event", message.Event,
			"open_kfid", message.OpenKfID,
			"error", err,
		)
		return err
	}
	return nil
}

// RegHandler 注册企业微信客服回调处理器。
func RegHandler(msgType, event string, handler CallbackHandler) {
	callbackHandlerMu.Lock()
	defer callbackHandlerMu.Unlock()

	key := buildCallbackKey(msgType, event)
	if callbackHandlers[key] != nil {
		slog.Error("duplicate wxwork callback handler registration", "key", key)
		return
	}
	callbackHandlers[key] = handler
}

// buildCallbackKey 生成客服回调处理器的注册键。
func buildCallbackKey(msgType, event string) string {
	msgType = strings.TrimSpace(msgType)
	event = strings.TrimSpace(event)
	if msgType == callbackMsgTypeEvent {
		return msgType + ":" + event
	}
	return msgType
}

func dispatchCallbackMessage(value any) {
	message, ok := value.(kf.CallbackMessage)
	if !ok {
		slog.Error("invalid wxwork callback message type")
		return
	}

	handler := getCallbackHandler(message)
	if handler == nil {
		return
	}
	handler(message)
}

func getCallbackHandler(message kf.CallbackMessage) CallbackHandler {
	callbackHandlerMu.RLock()
	defer callbackHandlerMu.RUnlock()

	key := buildCallbackKey(message.MsgType, message.Event)
	handler, ok := callbackHandlers[key]
	if ok {
		return handler
	}

	slog.Warn("wxwork callback handler not found",
		"key", key,
		"msg_type", message.MsgType,
		"event", message.Event,
	)
	return nil
}
