package push

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"

	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api/v5"
	log "github.com/sirupsen/logrus"

	"github.com/sjkhsl/study_xxqg/conf"
	"github.com/sjkhsl/study_xxqg/lib"
	"github.com/sjkhsl/study_xxqg/lib/state"
	"github.com/sjkhsl/study_xxqg/model"
	"github.com/sjkhsl/study_xxqg/utils"
	"github.com/sjkhsl/study_xxqg/utils/update"
)

var (
	handles sync.Map
	tgPush  func(id string, kind string, message string)
)

func TgInit() {
	defer func() {
		err := recover()
		if err != nil {
			tgPush = func(id string, kind string, message string) {

			}
		}
	}()
	config := conf.GetConfig()
	log.Infoln("已采用tg交互模式")
	telegram := Telegram{
		Token:  config.TG.Token,
		ChatId: config.TG.ChatID,
		Proxy:  config.TG.Proxy,
	}
	tgPush = func(id string, kind string, message string) {
		defer func() {
			err := recover()
			if err != nil {
				log.Errorln("推送tg消息出现错误")
				log.Errorln(err)
			}
		}()
		chatId := telegram.ChatId
		id1, err := strconv.Atoi(id)
		if err == nil {
			chatId = int64(id1)
		} else {
			log.Warningln("转化pushID错误，将发送给默认用户")
		}
		if kind == "flush" {
			telegram.SendMsg(chatId, strings.ReplaceAll(message, "</br>", "\n"))
		} else if kind == "image" {
			bytes, _ := base64.StdEncoding.DecodeString(message)
			telegram.SendPhoto(chatId, bytes)
		} else {
			if log.GetLevel() == log.DebugLevel {
				telegram.SendMsg(chatId, message)
			}
		}
	}

	telegram.Init()
}

// Telegram
// @Description:
//
type Telegram struct {
	Token  string
	ChatId int64
	bot    *tgbotapi.BotAPI
	Proxy  string
}

type Handle interface {
	getCommand() string
	execute(bot *Telegram, args []string)
}

type Mather struct {
	command string
	handle  func(bot *Telegram, from int64, args []string)
}

func (m Mather) getCommand() string {
	return m.command
}

func (m Mather) execute(bot *Telegram, from int64, args []string) {
	m.handle(bot, from, args)
}

func newPlugin(command string, handle func(bot *Telegram, from int64, args []string)) {
	handles.Store(command, handle)
}

// Init
/**
 * @Description:
 * @receiver t
 * @return func(kind string, message string)
 */
func (t *Telegram) Init() {

	newPlugin("/login", login)
	newPlugin("/get_users", getAllUser)
	newPlugin("/study", study)
	newPlugin("/get_scores", getScores)
	newPlugin("/quit", quit)
	newPlugin("/study_all", studyAll)
	newPlugin("/delete", deleteUser)
	newPlugin("/version", checkVersion)
	newPlugin("/update", botUpdate)
	newPlugin("/restart", botRestart)
	newPlugin("/get_fail_users", getFailUser)
	var err error
	var uri *url.URL
	if t.Proxy != "" {
		uri, err = url.Parse(t.Proxy)
		if err != nil {
			log.Errorln("代理解析失败" + err.Error())
			err = nil
		}
	}
	t.bot, err = tgbotapi.NewBotAPIWithClient(t.Token, conf.GetConfig().TG.CustomApi+"/bot%s/%s", &http.Client{Transport: &http.Transport{
		// 设置代理
		Proxy: func(r *http.Request) (*url.URL, error) {
			if uri != nil {
				return uri, nil
			} else {
				return http.ProxyFromEnvironment(r)
			}
		},
		//TLSNextProto: make(map[string]func(authority string, c *tls.Conn) http.RoundTripper),
	}})

	if err != nil {
		log.Errorln("telegram token鉴权失败或代理使用失败")
		log.Errorln(err.Error())
	}

	channel := t.bot.GetUpdatesChan(tgbotapi.NewUpdate(1))
	go func() {
		defer func() {
			err := recover()
			if err != nil {
				log.Errorln("处理tg消息时发生异常，请尝试重启程序")
				return
			}
		}()
		for {
			update := <-channel
			if update.Message == nil {
				if update.CallbackQuery != nil {
					update.Message = update.CallbackQuery.Message
					update.Message.Text = update.CallbackQuery.Data
					t.bot.Send(tgbotapi.NewDeleteMessage(update.Message.Chat.ID, update.CallbackQuery.Message.MessageID))
				} else {
					data, _ := json.Marshal(update)
					log.Infoln(string(data))
				}
			}

			if update.Message.Chat.Type == "group" || update.Message.Chat.Type == "supergroup" {
				update.Message.Text = strings.ReplaceAll(update.Message.Text, "@"+t.bot.Self.UserName, "")
			}
			log.Infoln(fmt.Sprintf("收到tg消息,来自%v,内容 ==》 %v", update.Message.Chat.ID, update.Message.Text))
			if len(conf.GetConfig().TG.WhiteList) > 0 {
				inWhiteList := false
				for _, id := range conf.GetConfig().TG.WhiteList {
					if id == update.Message.Chat.ID {
						inWhiteList = true
						break
					}
				}
				if !inWhiteList {
					log.Warningln("已过滤非白名单的消息,若需允许用户使用，请将user_id添加到配置文件white_list中")
					continue
				}
			}
			handles.Range(func(key, value interface{}) bool {
				if strings.Split(update.Message.Text, " ")[0] == key.(string) {
					go func() {
						defer func() {
							err := recover()
							if err != nil {
								log.Errorln(err)
								log.Errorln("handle执行出现了不可挽回的错误")
							}
						}()
						(value.(func(bot *Telegram, from int64, args []string)))(t, update.Message.Chat.ID, strings.Split(update.Message.Text, " ")[1:])
					}()
				}
				return true
			})
		}
	}()

	_, err = t.bot.Request(tgbotapi.NewSetMyCommands(
		tgbotapi.BotCommand{Command: "login", Description: "登录一个账号"},
		tgbotapi.BotCommand{Command: "get_users", Description: "获取所有cookie有效的用户"},
		tgbotapi.BotCommand{Command: "get_fail_users", Description: "获取所有cookie失效的用户"},
		tgbotapi.BotCommand{Command: "study", Description: "对一个账户进行学习"},
		tgbotapi.BotCommand{Command: "get_scores", Description: "获取用户成绩"},
		tgbotapi.BotCommand{Command: "quit", Description: "退出所有正在学习的实例,或者跟上实例ID退出对应实例"},
		tgbotapi.BotCommand{Command: "study_all", Description: "对当前所有用户进行按顺序学习"},
		tgbotapi.BotCommand{Command: "delete", Description: "删除选中的用户"},
		tgbotapi.BotCommand{Command: "version", Description: "获取程序当前的版本"},
		tgbotapi.BotCommand{Command: "restart", Description: "重启程序！"},
		tgbotapi.BotCommand{Command: "update", Description: "更新程序"},
	))
	if err != nil {
		return
	}
}

func (t *Telegram) SendPhoto(id int64, image []byte) {
	photo := tgbotapi.NewPhoto(id, tgbotapi.FileBytes{
		Name:  "login code",
		Bytes: image,
	})
	_, err := t.bot.Send(photo)
	if err != nil {
		log.Errorln("发送图片信息失败")
		log.Errorln(err.Error())
		return
	}
}

func (t *Telegram) SendMsg(id int64, message string) int {
	if id == 0 {
		id = t.ChatId
	}
	msg := tgbotapi.NewMessage(id, message)
	messa, err := t.bot.Send(msg)
	if err != nil {
		return 0
	}
	return messa.MessageID
}

func getFailUser(bot *Telegram, from int64, args []string) {
	user, err := model.QueryFailUser()
	if err != nil {
		bot.SendMsg(from, err.Error())
		return
	}
	msg := "当前过期用户:\n"
	for _, u := range user {
		msg += u.Nick + "\n"
	}
	bot.SendMsg(from, "当前过期用户:\n"+msg)
}

//
//  checkVersion
//  @Description: 检查版本信息
//  @param bot
//  @param from
//  @param args
//
func checkVersion(bot *Telegram, from int64, args []string) {
	about := utils.GetAbout()
	bot.SendMsg(from, about)
}

//
//  botRestart
//  @Description: 重启程序
//  @param bot
//  @param from
//  @param args
//
func botRestart(bot *Telegram, from int64, args []string) {
	if from != conf.GetConfig().TG.ChatID {
		bot.SendMsg(from, "请联系管理员解决！！")
		return
	}
	bot.SendMsg(from, "即将重启程序！！！")
	utils.Restart()
}

//
//  botUpdate
//  @Description: 更新程序
//  @param bot
//  @param from
//  @param args
//
func botUpdate(bot *Telegram, from int64, args []string) {
	if from != conf.GetConfig().TG.ChatID {
		bot.SendMsg(from, "请联系管理员解决！！")
		return
	}
	bot.SendMsg(from, "即将更新程序！！")
	update.SelfUpdate("", conf.GetVersion())
	bot.SendMsg(from, "更新完成，即将重启程序！")
	utils.Restart()
}

func login(bot *Telegram, from int64, args []string) {
	config := conf.GetConfig()
	go func() {
		defer func() {
			err := recover()
			if err != nil {
				log.Errorln(err)
			}
		}()
		core := lib.Core{
			ShowBrowser: config.ShowBrowser,
			Push: func(id string, kind string, message string) {
				switch {
				case kind == "image":
					bytes, _ := base64.StdEncoding.DecodeString(message)
					bot.SendPhoto(from, bytes)
				case kind == "markdown":
					newMessage := tgbotapi.NewMessage(bot.ChatId, message)
					newMessage.ParseMode = tgbotapi.ModeMarkdownV2
					bot.bot.Send(newMessage)
				case kind == "text":
					if log.GetLevel() == log.DebugLevel {
						bot.SendMsg(from, message)
					}
				case kind == "flush":
					bot.SendMsg(from, message)
				}
			},
		}
		core.Init()
		defer core.Quit()
		_, err := core.L(config.Retry.Times, strconv.Itoa(int(from)))
		if err != nil {
			bot.SendMsg(from, err.Error())
			return
		}
		bot.SendMsg(from, "登录成功")
	}()
}

func getAllUser(bot *Telegram, from int64, args []string) {
	users, err := model.Query()
	if err != nil {
		bot.SendMsg(from, "获取用户失败"+err.Error())
		return
	}
	message := fmt.Sprintf("共获取到%v个有效用户信息\n", len(users))
	for i, user := range users {
		message += fmt.Sprintf("%v   %v", i, user.Nick)
		message += "\n"
	}
	bot.SendMsg(from, message)
}

func studyAll(bot *Telegram, from int64, args []string) {
	config := conf.GetConfig()
	users, err := model.Query()
	if err != nil {
		bot.SendMsg(from, err.Error())
		return
	}
	if len(users) == 0 {
		bot.SendMsg(from, "未发现用户信息，请输入/login进行用户登录")
		return
	}
	getAllUser(bot, from, args)
	for _, user := range users {
		s := func() {
			core := lib.Core{
				ShowBrowser: config.ShowBrowser,
				Push: func(id string, kind string, message string) {
					switch {
					case kind == "image":
						bytes, _ := base64.StdEncoding.DecodeString(message)
						bot.SendPhoto(from, bytes)
					case kind == "markdown":
						newMessage := tgbotapi.NewMessage(bot.ChatId, message)
						newMessage.ParseMode = tgbotapi.ModeMarkdownV2
						_, _ = bot.bot.Send(newMessage)

					case kind == "text":
						if log.GetLevel() == log.DebugLevel {
							bot.SendMsg(from, message)
						}
					case kind == "flush":
						bot.SendMsg(from, message)
					}
				},
			}

			timer := time.After(time.Minute * 30)
			c := make(chan int, 1)
			go func() {

				bot.SendMsg(from, "已创建运行实例："+user.Uid)
				state.Add(user.Uid, &core)
				defer state.Delete(user.Uid)
				core.Init()
				defer core.Quit()
				core.LearnArticle(user)
				core.LearnVideo(user)
				core.RespondDaily(user, "daily")
				core.RespondDaily(user, "weekly")
				core.RespondDaily(user, "special")
				c <- 1
			}()

			select {
			case <-timer:
				{
					bot.SendMsg(from, "学习超时，请重新学习或检查日志")
					log.Errorln("学习超时，已自动退出")
					core.Quit()
				}
			case <-c:
				{
				}
			}
			score, _ := lib.GetUserScore(user.ToCookies())
			bot.SendMsg(from, fmt.Sprintf("%v已学习完成\n%v", user.Nick, lib.PrintScore(score)))
		}
		s()
	}
}

func deleteUser(bot *Telegram, from int64, args []string) {
	config := conf.GetConfig()
	if from != config.TG.ChatID {
		bot.SendMsg(from, "请联系管理员删除！")
		return
	}
	users, err := model.Query()
	if err != nil {
		bot.SendMsg(from, err.Error())
		return
	}
	failUser, err := model.QueryFailUser()
	if err != nil {
		bot.SendMsg(from, err.Error())
		return
	}
	if len(args) < 1 {
		msgID := bot.SendMsg(from, "请选择删除的用户")
		markup := tgbotapi.InlineKeyboardMarkup{}
		for i, user := range users {
			markup.InlineKeyboard = append(markup.InlineKeyboard, append([]tgbotapi.InlineKeyboardButton{}, tgbotapi.NewInlineKeyboardButtonData(user.Nick, "/delete "+strconv.Itoa(i))))
		}

		for i, user := range failUser {
			markup.InlineKeyboard = append(markup.InlineKeyboard, append([]tgbotapi.InlineKeyboardButton{}, tgbotapi.NewInlineKeyboardButtonData(user.Nick+"  (已失效)", "/delete "+strconv.Itoa(len(users)+i))))

		}

		replyMarkup := tgbotapi.NewEditMessageReplyMarkup(from, msgID, markup)
		_, err := bot.bot.Send(replyMarkup)
		if err != nil {
			return
		}
		return
	} else {
		users = append(users, failUser...)
		i, err := strconv.Atoi(args[0])
		if err != nil {
			bot.SendMsg(from, err.Error())
			return
		}
		if i >= len(users) {
			bot.SendMsg(from, "错误的序号")
			return
		}
		err = model.DeleteUser(users[i].Uid)
		if err != nil {
			bot.SendMsg(from, err.Error())
			return
		}
		bot.SendMsg(from, "删除用户"+users[i].Nick+"成功")
	}
}

func study(bot *Telegram, from int64, args []string) {
	config := conf.GetConfig()
	users, err := model.Query()
	if err != nil {
		bot.SendMsg(from, err.Error())
		return
	}
	var user *model.User
	switch {
	case len(users) == 1:
		bot.SendMsg(from, "仅存在一名用户信息，自动进行学习")
		user = users[0]
	case len(users) == 0:
		bot.SendMsg(from, "未发现用户信息，请输入/login进行用户登录")
		return
	default:
		if 0 < len(args) {
			i, err := strconv.Atoi(args[0])
			if err != nil {
				bot.SendMsg(from, err.Error())
				return
			}
			user = users[i]
		} else {
			msgID := bot.SendMsg(from, "存在多名用户，未输入用户序号")
			markup := tgbotapi.InlineKeyboardMarkup{}
			for i, user := range users {
				markup.InlineKeyboard = append(markup.InlineKeyboard, append([]tgbotapi.InlineKeyboardButton{}, tgbotapi.NewInlineKeyboardButtonData(user.Nick, "/study "+strconv.Itoa(i))))
			}

			replyMarkup := tgbotapi.NewEditMessageReplyMarkup(from, msgID, markup)
			_, err := bot.bot.Send(replyMarkup)
			if err != nil {
				return
			}
			return
		}
	}
	core := lib.Core{
		ShowBrowser: config.ShowBrowser,
		Push: func(id string, kind string, message string) {
			switch {
			case kind == "image":
				bytes, _ := base64.StdEncoding.DecodeString(message)
				bot.SendPhoto(from, bytes)
			case kind == "markdown":
				newMessage := tgbotapi.NewMessage(bot.ChatId, message)
				newMessage.ParseMode = tgbotapi.ModeMarkdownV2
				_, _ = bot.bot.Send(newMessage)

			case kind == "text":
				if log.GetLevel() == log.DebugLevel {
					bot.SendMsg(from, message)
				}
			case kind == "flush":
				bot.SendMsg(from, message)
			}
		},
	}
	timer := time.After(time.Minute * 30)
	c := make(chan int, 1)
	go func() {
		bot.SendMsg(from, "已创建运行实例："+user.Uid)
		state.Add(user.Uid, &core)
		defer state.Delete(user.Uid)
		core.Init()
		defer core.Quit()

		core.LearnArticle(user)

		core.LearnVideo(user)

		if config.Model == 2 {
			core.RespondDaily(user, "daily")
		} else if config.Model == 3 {
			core.RespondDaily(user, "daily")
			core.RespondDaily(user, "weekly")
			core.RespondDaily(user, "special")
		} else if config.Model == 4 {
			core.RespondDaily(user, "special")
		}

		c <- 1
	}()

	select {
	case <-timer:
		{
			log.Errorln("学习超时，已自动退出")
			bot.SendMsg(from, "学习超时，请重新登录或检查日志")
			core.Quit()
		}
	case <-c:
		{

		}
	}
	score, _ := lib.GetUserScore(user.ToCookies())
	bot.SendMsg(from, fmt.Sprintf("%v已学习完成\n%v", user.Nick, lib.PrintScore(score)))
}

func getScores(bot *Telegram, from int64, args []string) {
	users, err := model.Query()
	if err != nil {
		log.Errorln(err.Error())
		bot.SendMsg(from, "获取用户信息失败"+err.Error())
		return
	}
	message := ""
	for _, user := range users {
		message += user.Nick + "\n"
		score, err := lib.GetUserScore(user.ToCookies())
		if err != nil {
			message += err.Error() + "\n"
			continue
		}
		message += lib.FormatScore(score) + "\n"
		bot.SendMsg(from, message)
		message = ""
	}
}

func quit(bot *Telegram, from int64, args []string) {
	if len(args) < 1 {
		state.Range(func(key, value interface{}) bool {
			bot.SendMsg(from, "已退出运行实例"+key.(string))
			core := value.(*lib.Core)
			core.Quit()
			return true
		})
	} else {
		state.Range(func(key, value interface{}) bool {
			if key.(string) == args[0] {
				core := value.(*lib.Core)
				core.Quit()
			}
			return true
		})
	}
}
