package core

import (
	"context"
	"errors"
	"fmt"
	"math/rand"
	"reflect"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/cdle/sillyplus/core/common"
	"github.com/cdle/sillyplus/core/logs"
	"github.com/cdle/sillyplus/utils"
	"github.com/dop251/goja"
)

type Message map[string]string

type Details struct {
	Content   string
	UserID    string
	ChatID    string
	Username  string
	Chatname  string
	MessageID string
}

type CustomSender struct {
	BaseSender
	details Details
	f       *Factory
}

type MsgChan struct {
	Chan chan string
	Msg  map[string]string
}

type Factory struct {
	botid         string
	botplt        string
	uuid          string
	msgChan       chan MsgChan
	demo          *CustomSender
	reply         func(map[string]string) string
	lm            chan bool
	nm            int64
	recallMessage func(interface{}) bool
	groupKick     func(uid string, gid string, reject_add_request bool) bool
	groupBan      func(uid string, gid string, duration int) bool
	groupUnban    func(uid string, gid string) bool
	isAdmin       func(string) bool
	vm            *goja.Runtime
	ctx           context.Context
	cancel        context.CancelFunc
	destroid      bool
}

type Bot [2]string //botplt botid

var Bots = map[Bot]*Factory{}
var BotsLocker sync.RWMutex

var ErrNotFind = errors.New("adapter not find")

func DestroyAdapterByUUID(uuid string) {
	BotsLocker.RLock()
	defer BotsLocker.RUnlock()
	for i := range Bots {
		if Bots[i].uuid == uuid {
			go Bots[i].Destroy()
		}
	}
}

func GetAdapter(botplt string, bots_id ...string) (*Factory, error) {
	BotsLocker.RLock()
	defer BotsLocker.RUnlock()
	var bots = []*Factory{}
	var select_bots = []*Factory{}
	for i := range Bots {
		plt, id := i[0], i[1]
		// fmt.Println("plt", plt, "id", id, botplt, bots_id)
		for j := range bots_id {
			if plt == botplt && bots_id[j] == id {
				select_bots = append(select_bots, Bots[i])
			}
			if plt == botplt {
				bots = append(bots, Bots[i])
			}
		}
	}
	if len(bots) == 0 {
		return nil, ErrNotFind
	}
	if len(select_bots) != 0 {
		i := rand.Intn(len(select_bots))
		return select_bots[i], nil
	}
	i := rand.Intn(len(bots))
	return bots[i], ErrNotFind
}

func GetAdapters(botplt string, bots_id ...string) ([]*Factory, error) {
	BotsLocker.RLock()
	defer BotsLocker.RUnlock()
	var bots = []*Factory{}
	var select_bots = []*Factory{}
	for i := range Bots {
		plt, id := i[0], i[1]
		// fmt.Println("plt", plt, "id", id, botplt, bots_id)
		for j := range bots_id {
			if plt == botplt && bots_id[j] == id {
				select_bots = append(select_bots, Bots[i])
			}
			if plt == botplt {
				bots = append(bots, Bots[i])
			}
		}
	}
	if len(bots) == 0 {
		return nil, ErrNotFind
	}
	if len(select_bots) != 0 {
		return select_bots, nil
	}
	return bots, ErrNotFind
}

func GetAdapterBotsID(botplt string) []string {
	BotsLocker.RLock()
	defer BotsLocker.RUnlock()
	var bots_id = []string{}
	for i := range Bots {
		// fmt.Println("==", botplt == i[0], botplt, i[0])
		if botplt == i[0] {
			bots_id = append(bots_id, i[1])
		}
	}
	return bots_id
}
func GetAdapterBotPlts() []string {
	BotsLocker.RLock()
	defer BotsLocker.RUnlock()
	var bot_plts = []string{}
	for i := range Bots {
		// fmt.Println("==", botplt == i[0], botplt, i[0])
		has := false
		for _, bot_plt := range bot_plts {
			if bot_plt == i[0] {
				has = true
			}
		}
		if !has {
			bot_plts = append(bot_plts, i[0])
		}
	}
	return bot_plts
}

func (f *Factory) Init(botplt, botid string) {
	f.ctx, f.cancel = context.WithCancel(context.Background())
	BotsLocker.Lock()
	defer BotsLocker.Unlock()
	f.botplt = botplt
	f.botid = botid
	f.msgChan = make(chan MsgChan, 100000)
	f.demo = &CustomSender{
		f: f,
	}
	if _, ok := Bots[[2]string{botplt, botid}]; ok {
		console.Warn("%s机器人%s因冲突销毁！", botplt, botid)
	}
	Bots[[2]string{botplt, botid}] = f
	f.lm = make(chan bool, 10)
	f.nm = 0
	if botid != "" {
		botid = fmt.Sprintf("(%s)", botid)
	}
	console.Log("%s机器人%s已初始化。", botplt, botid)
}

func (f *Factory) IsAdapter(botid string) bool {
	BotsLocker.RLock()
	defer BotsLocker.RUnlock()
	for i := range Bots {
		id := i[0]
		if botid == id {
			return true
		}
	}
	return false
}

func (f *Factory) Destroy() {
	BotsLocker.Lock()
	defer BotsLocker.Unlock()
	f.destroid = true
	f.cancel()
	close(f.msgChan)
	delete(Bots, [2]string{f.botplt, f.botid})
	botid := ""
	if f.botid != "" {
		botid = fmt.Sprintf("(%s)", f.botid)
	} else {
		botid = f.botid
	}
	console.Log("%s机器人%s已销毁。", strings.ToUpper(f.botplt), botid)
}

func (f *Factory) Push(msg map[string]string) (string, error) {
	var demo = *f.demo
	var sender = &demo
	fsps := &common.FakerSenderParams{
		UserID: msg[USER_ID],
		ChatID: msg[CHAT_ID],
	}
	sender.SetFsps(fsps)
	return sender.Reply(msg[CONETNT], PUSH(""))
}

func (f *Factory) SetReplyHandler(function func(map[string]string) string) {
	f.reply = func(m map[string]string) string {
		if f.uuid != "" {
			mutex := GetMutex(f.uuid)
			mutex.Lock()
			defer mutex.Unlock()
		}
		defer func() {
			err := recover()
			if err != nil {
				console.Error("Sender(\""+f.botplt+"\").SetReply error:", err)
			}
		}()
		return function(m)
	}
}

// func (f *Factory) GetReplies() {

// }

func GetReplyMessage(vm *goja.Runtime, plt string, bots_id []string) *goja.Promise {
	promise, resolve, reject := vm.NewPromise()
	adapters, err := GetAdapters(plt, bots_id...)
	if err != nil {
		go func() {
			time.Sleep(time.Second)
			reject(Error(vm, err))
		}()
		return promise
	}
	ctx, cancel := context.WithCancel(context.Background())
	for i := range adapters {
		go func(i int) {
			select {
			case <-ctx.Done():
				logs.Debug("消息获取中断")
			case <-adapters[i].ctx.Done():
				cancel()
				logs.Debug("%s adapter %s destroied", adapters[i].botplt, adapters[i].botid)
				reject("adapter destroied")
			case mc := <-adapters[i].msgChan:
				cancel()
				var msg = map[string]interface{}{}
				for k, v := range mc.Msg {
					msg[k] = v
				}
				obj := adapters[i].vm.NewObject()
				for k, v := range mc.Msg {
					obj.Set(k, v)
				}
				obj.Set("bot_id", adapters[i].botid)
				// msg["bot_id"] = adapters[i].botid
				// msg["setMessageId"] = func(id string) {
				// 	select {
				// 	case <-mc.Chan:
				// 	case <-time.After(time.Millisecond):
				// 		mc.Chan <- id
				// 	}
				// }
				// resolve(msg)
				resolve(vm.NewProxy(obj, &goja.ProxyTrapConfig{
					Set: func(target *goja.Object, property string, value, receiver goja.Value) (success bool) {
						if property == "message_id" {
							select {
							case <-mc.Chan:
								return false
							case <-time.After(time.Millisecond):
							}
							mc.Chan <- fmt.Sprint(value.Export())
						}
						return true
					},
				}))
			}
		}(i)
	}
	return promise
}

func (f *Factory) GetReplyMessage() *goja.Promise {
	promise, resolve, reject := f.vm.NewPromise()
	go func() {
		select {
		case <-f.ctx.Done():
			logs.Debug("%s adapter %s destroied", f.botplt, f.botid)
			reject("adapter destroied")
		case mc := <-f.msgChan:
			obj := f.vm.NewObject()
			for k, v := range mc.Msg {
				obj.Set(k, v)
			}
			resolve(f.vm.NewProxy(obj, &goja.ProxyTrapConfig{
				Set: func(target *goja.Object, property string, value, receiver goja.Value) (success bool) {
					if property == "message_id" {
						select {
						case <-mc.Chan:
							return false
						case <-time.After(time.Millisecond):
						}
						mc.Chan <- fmt.Sprint(value.Export())
					}
					return true
				},
			}))
		}
	}()
	return promise
}

func (f *Factory) Send(function func(map[string]string) string) {
	f.SetReplyHandler(function)
}

func (f *Factory) SetRecallMessage(function func(interface{}) bool) {
	f.recallMessage = func(i interface{}) bool {
		defer func() {
			err := recover()
			if err != nil {
				console.Error("Sender(\""+f.botplt+"\").recall error:", err)
			}
		}()
		return function(i)
	}
}

func (f *Factory) SetGroupKick(function func(uid string, gid string, reject_add_request bool) bool) {
	f.groupKick = func(uid string, gid string, reject_add_request bool) bool {
		defer func() {
			err := recover()
			if err != nil {
				console.Error("Sender(\""+f.botplt+"\").GroupKick error:", err)
			}
		}()
		return function(uid, gid, reject_add_request)
	}
}

func (f *Factory) SetGroupBan(function func(uid string, gid string, duration int) bool) {
	f.groupBan = func(uid string, gid string, duration int) bool {
		defer func() {
			err := recover()
			if err != nil {
				console.Error("Sender(\""+f.botplt+"\").SetGroupBan error:", err)
			}
		}()
		return function(uid, gid, duration)
	}
}

func (f *Factory) SetGroupUnban(function func(uid string, gid string) bool) {
	f.groupUnban = func(uid string, gid string) bool {
		defer func() {
			err := recover()
			if err != nil {
				console.Error("Sender(\""+f.botplt+"\").SetgroupUnban error:", err)
			}
		}()
		return function(uid, gid)
	}
}

func (f *Factory) SetIsAdmin(function func(string) bool) {
	f.isAdmin = func(uid string) bool {
		defer func() {
			err := recover()
			if err != nil {
				console.Error("Sender(\""+f.botplt+"\").SetAdmin error:", err)
			}
		}()
		return function(uid)
	}
}

func (f *Factory) Receive(wt interface{}) *CustomSender {
	var demo = *f.demo
	sender := &demo
	props := wt.(map[string]interface{})
	for i := range props {
		switch strings.ToLower(i) {
		case "content":
			sender.details.Content = fmt.Sprint(props[i])
		case "message_id", "messageId":
			sender.details.MessageID = fmt.Sprint(props[i])
		case "user_id", "userId":
			sender.details.UserID = fmt.Sprint(props[i])
		case "chat_id", "chatId", "group_id", "groupId", "group_code", "groupCode":
			sender.details.ChatID = fmt.Sprint(props[i])
		case "user_name", "userName":
			sender.details.Username = fmt.Sprint(props[i])
		case "chat_name", "chatName", "groupName", "group_name":
			sender.details.Chatname = fmt.Sprint(props[i])
		}
	}
	if sender.details.Content != "" {
		Messages <- sender
	}
	return sender
}

func (sender *CustomSender) GetContent() string {
	if sender.Fsps.Content != "" {
		return sender.Fsps.Content
	}
	return sender.details.Content
}
func (sender *CustomSender) GetUserID() string {
	if sender.Fsps.UserID != "" {
		return sender.Fsps.UserID
	}
	return sender.details.UserID
}
func (sender *CustomSender) GetChatID() string {
	if !utils.IsZeroOrEmpty(sender.Fsps.ChatID) {
		return fmt.Sprint(sender.Fsps.ChatID)
	}
	return sender.details.ChatID
}
func (sender *CustomSender) GetImType() string {
	return sender.f.botplt
}
func (sender *CustomSender) GetUserName() string {
	return sender.details.Username
}
func (sender *CustomSender) GetChatName() string {
	return sender.details.Chatname
}
func (sender *CustomSender) GetMessageID() string {
	return sender.details.MessageID
}
func (sender *CustomSender) GetReplySenderUserID() int {
	if !sender.IsReply() {
		return 0
	}
	return 0
}

func (sender *CustomSender) GetBotID() string {
	return sender.f.botid
}

type PUSH string

func (sender *CustomSender) Reply(msgs ...interface{}) (string, error) {
	var push = false
	var content = ""
	for _, item := range msgs {
		switch item := item.(type) {
		case PUSH:
			push = true
		case string:
			content = item
		}
	}
	if !push {
		if IsNoReplyGroup(sender) {
			return "", errors.New("is no reply group")
		}
	}
	content = strings.ReplaceAll(content, "\n\r", "\n")
	content = strings.ReplaceAll(content, "\r\n", "\n")
	content = strings.ReplaceAll(content, "\r", "\n")
	content = regexp.MustCompile("[\n]{3,}").ReplaceAllString(content, "\n\n")
	if content != "" {
		msg := map[string]string{
			"message_id": sender.GetMessageID(),
			"content":    content,
			"user_id":    sender.GetUserID(),
			"chat_id":    sender.GetChatID(),
			// "bot_id":     sender.GetBotID(),

			// "uuid":       utils.GenUUID(),
		}
		if sender.f.reply == nil {
			c := MsgChan{
				Msg:  msg,
				Chan: make(chan string),
			}
			if sender.f.destroid {
				return "", errors.New("adapter destroid")
			}
			sender.f.msgChan <- c
			select {
			case id := <-c.Chan:
				return id, nil
			case <-time.After(time.Second * 5):
				close(c.Chan)
				return "", errors.New("get message_id timeout")
			}
		} else {
			//todo 阻塞延迟异常
			v := sender.f.reply(msg)
			return v, nil
		}
	}
	return "", nil
}

const (
	MESSAGE_ID = "message_id"
	CONETNT    = "content"
	USER_ID    = "user_id"
	CHAT_ID    = "chat_id"
	IS_ADMIN   = "is_admin"
	// UUID       = "message_id"
)

func (sender *CustomSender) Copy() common.Sender {
	new := reflect.Indirect(reflect.ValueOf(interface{}(sender))).Interface().(CustomSender)
	return &new
}

func (sender *CustomSender) RecallMessage(ps ...interface{}) error {
	if sender.f.recallMessage == nil {
		return nil
	}
	for _, p := range ps {
		switch p := p.(type) {
		case string:
			sender.f.recallMessage(p)
		case []string:
			for _, v := range p {
				sender.f.recallMessage(v)
			}
		case [][]string:
			for _, v := range p {
				for _, v2 := range v {
					sender.f.recallMessage(v2)
				}
			}
		}
	}
	return nil
}

func (sender *CustomSender) GroupKick(uid string, reject_add_request bool) {
	if sender.f.groupKick == nil {
		return
	}
	sender.f.groupKick(uid, sender.GetChatID(), reject_add_request)
}

func (sender *CustomSender) GroupBan(uid string, duration int) {
	if sender.f.groupBan == nil {
		return
	}
	sender.f.groupBan(uid, sender.GetChatID(), duration)
}

func (sender *CustomSender) GroupUnban(uid string) {
	if sender.f.groupUnban == nil {
		return
	}
	sender.f.groupUnban(uid, sender.GetChatID())
}

func (sender *CustomSender) IsAdmin() bool {
	if sender.f.isAdmin == nil {
		return Contains(strings.Split(MakeBucket(sender.f.botplt).GetString("masters"), "&"), sender.GetUserID())
	}
	return sender.f.isAdmin(sender.GetUserID())
}
