package rikkabot

import (
	"context"
	"fmt"
	"github.com/Clov614/rikka-bot-wechat/rikkabot/coreapi"
	"sync"
	"time"

	"github.com/Clov614/logging"
	"github.com/Clov614/rikka-bot-wechat/rikkabot/config"
	"github.com/Clov614/rikka-bot-wechat/rikkabot/groupmanager"
	"github.com/Clov614/rikka-bot-wechat/rikkabot/message"
	"github.com/Clov614/rikka-bot-wechat/rikkabot/onebot/dto/event"
	"github.com/Clov614/rikka-bot-wechat/rikkabot/processor"
	cachepkg "github.com/Clov614/rikka-bot-wechat/rikkabot/processor/cache"
	"github.com/Clov614/rikka-bot-wechat/rikkabot/utils/timeutil"
	wcf "github.com/Clov614/wcf-rpc-sdk"
	"github.com/google/uuid"
	"github.com/rs/zerolog/log"
)

type RikkaBot struct {
	ctx               context.Context
	cancel            context.CancelFunc
	sendMsg           chan *message.Message
	recvMsg           chan *message.Message
	Config            *config.CommonConfig
	EnableProcess     bool // 是否处理消息
	Processor         *processor.Processor
	enableEventHandle bool // 是否开启事件处理
	EventPool         *event.EventPool
	EventFuncs        []func(event event.IEvent)
	mu                sync.Mutex
	Core              *coreapi.Core // 核心组件
	err               error
}

func init() {
	cfg := config.GetConfig()
	// 日志模块设置
	logConfig := logging.Config{
		LogPath:             "./log/rikka.log",
		ProjectKey:          "bot", // 项目唯一标识，默认为 "project"
		ProjectName:         "rikka-bot-wechat",
		MaxLogSize:          int64(cfg.LogMaxSize) * 1024 * 1024, // 10MB, 日志文件最大大小
		MonitorInterval:     4 * time.Hour,                       // 日志文件大小监控间隔
		EnableConsoleOutput: true,
		EnableFileOutput:    true,
	}
	logging.InitLogger(logConfig)

}

func NewRikkaBot(ctx context.Context, cancel context.CancelFunc, cli *wcf.Client, debug bool) *RikkaBot {
	cfg := config.GetConfig()
	if debug {
		logging.SetLogLevel("debug")
	}
	recvChan := make(chan *message.Message)

	// 初始化 Cache
	appCache := cachepkg.GetCache()
	if appCache == nil {
		log.Warn().Msg("Cache instance is nil, attempting to initialize cache.")
		appCache = cachepkg.Init()
		if appCache == nil {
			log.Fatal().Msg("Failed to initialize cache for RikkaBot")
		}
	}

	// 初始化 GroupManager
	gm, err := groupmanager.NewGroupManager(appCache, "rikka_groups_main")
	if err != nil {
		log.Fatal().Err(err).Msg("Failed to initialize GroupManager")
	}

	// 初始化
	return &RikkaBot{
		ctx:        ctx,
		cancel:     cancel,
		sendMsg:    make(chan *message.Message),
		recvMsg:    recvChan,
		Processor:  processor.NewProcessor(ctx, cli),
		Config:     cfg,
		EventPool:  event.NewEventPool(cfg.HttpServer.EventBufferSize),
		EventFuncs: make([]func(event event.IEvent), 0),
		Core:       coreapi.NewCore(cli, gm),
	}

}

func (r *RikkaBot) SetBotName(botname string) (*RikkaBot, error) {
	r.Config.Botname = botname
	err := r.Config.Update()
	if err != nil {
		return nil, fmt.Errorf("error update bot config: %w", err)
	}
	return r, nil
}

// PushLogOutNoticeEvent 推送机器人掉线事件
func (r *RikkaBot) PushLogOutNoticeEvent(code int, msg string) {
	// 构造 notice—event
	type LogOutType struct {
		Code int    `json:"code"`
		Msg  string `json:"msg"`
	}
	var logoutData LogOutType
	logoutData.Code = code
	logoutData.Msg = msg
	e := event.Event{
		Id:         uuid.New().String(),
		Time:       timeutil.GetTimeUnix(),
		Type:       "notice",
		DetailType: "logout",
		SubType:    "",
	}
	noticeEvent := event.NoticeEvent[LogOutType]{}
	initNoticeEvent := noticeEvent.InitNoticeEvent(e, logoutData)
	err := r.EventPool.AddEvent(*initNoticeEvent)
	if err != nil {
		logging.WarnWithErr(err, "推送登出事件至事件池错误")
	}
}

func (r *RikkaBot) OnEventPush(f func(event event.IEvent)) {
	r.mu.Lock()
	defer r.mu.Unlock()
	r.EventFuncs = append(r.EventFuncs, f)
}

func (r *RikkaBot) GetEventFuncs() []func(event event.IEvent) {
	r.mu.Lock()
	defer r.mu.Unlock()
	return r.EventFuncs
}

func (r *RikkaBot) StartHandleEvent() {
	r.enableEventHandle = true
	logging.Info("开始处理事件")
	r.EventPool.StartProcessing(r.GetEventFuncs()...)
}

func (r *RikkaBot) DispatchMsgEvent(rikkaMsg message.Message) {
	if !r.enableEventHandle {
		return // 不处理分发消息
	}
	var detailType string
	if rikkaMsg.IsGroup {
		detailType = "group"
	} else if rikkaMsg.IsFriend {
		detailType = "private"
	}
	msgEvent := event.MsgEvent{}
	msgEvent.InitEvent("message", detailType, "")
	msgEvent.InitMsgEvent(rikkaMsg)
	err := r.EventPool.AddEvent(msgEvent) // ignore err
	if err != nil {
		logging.Warn(fmt.Sprintf("事件池警告 %s", err))
	}
}

// Start 启动 rikkabot 进行消息处理
func (r *RikkaBot) Start() {
	logging.Info("rikka bot start")
	go r.Processor.Start(r.recvMsg, r.sendMsg)
	r.EnableProcess = true // 防止生产者阻塞
}

var onceExit sync.Once

// Exit 主动退出 rikkabot
func (r *RikkaBot) Exit() {
	onceExit.Do(func() {
		logging.Info("rikka bot exited")
		r.EventPool.Close()
		r.Processor.Close()
		r.Core.Close() // cli.Close()
		r.cancel()
	})
}

// ExitWithErr 异常退出 rikkabot
func (r *RikkaBot) ExitWithErr(code int, msg string) {
	logging.Error("异常退出")
	logging.Error(msg, map[string]interface{}{"exit code": code})
	r.Exit()
}

// Block 当发生错误，该方法会立即返回，否则会一直阻塞
func (r *RikkaBot) Block() error {
	<-r.ctx.Done()
	r.Exit()
	logging.Info("主程序将在2s后退出...")
	time.Sleep(2 * time.Second) // 增加一个固定延迟，简单的确保退出 todo perf -> wg
	return r.err
}

// region Msg Chan Operation

// GetReqMsgSendChan 获取消息接收通道 写通道
func (r *RikkaBot) GetReqMsgSendChan() chan<- *message.Message {
	return r.recvMsg
}

// GetReqMsgRecvChan 获取消息接收通道 读通道
func (r *RikkaBot) GetReqMsgRecvChan() <-chan *message.Message {
	return r.recvMsg
}

// GetRespMsgSendChan 获取消息发送通道 写通道
func (r *RikkaBot) GetRespMsgSendChan() chan<- *message.Message {
	return r.sendMsg
}

// GetRespMsgRecvChan 获取消息发送通道 读通道
func (r *RikkaBot) GetRespMsgRecvChan() <-chan *message.Message {
	return r.sendMsg
}

//endregion
