package telegram

import (
	"jannex/telegram-bot-manager/modules/cache"
	"jannex/telegram-bot-manager/modules/config"
	"jannex/telegram-bot-manager/modules/database"
	"jannex/telegram-bot-manager/modules/logger"
	"jannex/telegram-bot-manager/modules/structs"
	"jannex/telegram-bot-manager/modules/utils"
	"regexp"
	"strings"
	"time"

	"git.ex.umbach.dev/Alex/roese-utils/rslogger"
	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
	"github.com/rs/zerolog/log"
)

var bot *tgbotapi.BotAPI

func InitBot() {
	var err error

	bot, err = tgbotapi.NewBotAPI(config.Cfg.Telegram.Token)

	if err != nil {
		logger.AddSystemLog(rslogger.LogTypeError, "Failed to initialize Telegram bot, err: %v", err.Error())
		log.Fatal().Err(err).Msgf("Failed to initialize Telegram bot, err: %v", err.Error())
	}

	bot.Debug = config.Cfg.Telegram.Debug

	logger.AddSystemLog(rslogger.LogTypeInfo, "Bot connected as %s", bot.Self.UserName)

	go IncomingMessagesHandler()
}

func IncomingMessagesHandler() {
	updateConfig := tgbotapi.NewUpdate(0)
	updateConfig.Timeout = 60

	updates, err := bot.GetUpdatesChan(updateConfig)

	if err != nil {
		logger.AddSystemLog(rslogger.LogTypeError, "Failed to get updates from Telegram bot, err: %v", err.Error())
		log.Fatal().Err(err).Msgf("Failed to get updates from Telegram bot, err: %v", err.Error())
	}

	for update := range updates {
		if update.Message == nil {
			continue
		}

		chatID := update.Message.Chat.ID
		text := update.Message.Text

		if text == "" {
			continue
		}

		regStart := regexp.MustCompile(`/start`)
		regHelp := regexp.MustCompile(`/help`)

		regVerify := regexp.MustCompile(`/verify (\w+)`)
		regVerifyMatches := regVerify.FindStringSubmatch(text)

		regUnsubscribe := regexp.MustCompile(`/unsubscribe`)

		regFilter := regexp.MustCompile(`/filter`)
		regFilterWithArgs := regexp.MustCompile(`/filter (.+)`)
		regFilterMatches := regFilterWithArgs.FindStringSubmatch(text)

		var replyMessage string

		if regStart.MatchString(text) || regHelp.MatchString(text) { // command /start or /help
			replyMessage = "Welcome!\n\n" +
				"Type /verify <code> to start receiving notifications. The code can be found in the user profile on the dashboard.\n\n" +
				"Type /unsubscribe to stop receiving notifications.\n\n" +
				"Type /filter <filter> to filter notifications. Filter can be: success, info, warning, error. For example: /filter info,warning,error will filter all notifications except success.\n\n" +
				"Type /filter to get your current filter."

			SendNotification(chatID, replyMessage)

			logger.AddSystemLog(rslogger.LogTypeInfo, "Received command %s from user: %s %s", text, update.Message.From.FirstName, update.Message.From.LastName)
		} else if len(regVerifyMatches) == 2 { // command /verify <code>
			code := regVerifyMatches[1]

			if len(code) != utils.VerifyCodeLength {
				logger.AddSystemLog(rslogger.LogTypeWarning, "Received code with wrong length from user: %s %s. Message: %s", update.Message.From.FirstName, update.Message.From.LastName, code)
				continue
			}

			var tempVerify structs.TempVerifyCode
			var ok bool

			if tempVerify, ok = cache.GetTempVerifyByCode(code); !ok {
				// code not found in cache

				logger.AddSystemLog(rslogger.LogTypeWarning, "Received code which was not found in cache from user: %s %s. Message: %s", update.Message.From.FirstName, update.Message.From.LastName, code)

				replyMessage = "The code you entered is invalid. Please check the code and try again."
			} else {
				// code found in cache
				// check if code is expired
				if tempVerify.ExpiredAt.Before(time.Now()) {
					logger.AddSystemLog(rslogger.LogTypeWarning, "Received code which was expired from user: %s %s. Message: %s", update.Message.From.FirstName, update.Message.From.LastName, code)

					replyMessage = "The code you entered is expired. Please check the code and try again."
				} else {
					var foundVerifiedUser structs.VerifiedUser

					database.DB.Where("chat_id = ?", update.Message.From.ID).First(&foundVerifiedUser)

					if foundVerifiedUser.UserId != "" {
						// user already verified

						replyMessage = "You have already verified your account. You will continue to receive notifications. Type /unsubscribe to stop receiving notifications."

						logger.AddSystemLog(rslogger.LogTypeWarning, "User: %s %s has tried to verify his account with code %s, but has already verified his account", update.Message.From.FirstName, update.Message.From.LastName, code)
					} else {
						// user not verified

						replyMessage = "You have successfully verified your account. You will now receive notifications. Type /unsubscribe to stop receiving notifications."

						database.DB.Create(&structs.VerifiedUser{
							UserId:    tempVerify.UserId,
							ChatId:    update.Message.From.ID,
							Filter:    utils.NotificationTypeError,
							CreatedAt: time.Now(),
						})

						logger.AddSystemLog(rslogger.LogTypeInfo, "User: %s %s has subscribed to receive notifications", update.Message.From.FirstName, update.Message.From.LastName)
					}
				}

				cache.RemoveTempVerifyCode(tempVerify.UserId)
			}

			SendNotification(chatID, replyMessage)
		} else if regUnsubscribe.MatchString(text) { // command /unsubscribe
			res := database.DB.Delete(&structs.VerifiedUser{}, "chat_id = ?", update.Message.From.ID)

			if res.Error != nil {
				logger.AddSystemLog(rslogger.LogTypeError, "Failed to delete verified user from database, err: %v", res.Error.Error())
				continue
			}

			if res.RowsAffected == 0 {
				logger.AddSystemLog(rslogger.LogTypeWarning, "User: %s %s has tried to unsubscribe from receiving notifications, but has not been verified", update.Message.From.FirstName, update.Message.From.LastName)
				replyMessage = "You have not been verified yet. Type /verify <code> to start receiving notifications. The code can be found in the user profile on the dashboard."
			} else {
				logger.AddSystemLog(rslogger.LogTypeInfo, "User: %s %s has unsubscribed from receiving notifications", update.Message.From.FirstName, update.Message.From.LastName)
				replyMessage = "You have unsubscribed from receiving notifications. Type /verify <code> to start receiving notifications again. The code can be found in the user profile on the dashboard."
			}

			SendNotification(chatID, replyMessage)
		} else if len(regFilterMatches) == 2 { // command /filter <filters>
			setFilter(chatID, update, text, regFilterMatches)
		} else if regFilter.MatchString(text) { // command /filter
			var foundVerifiedUser structs.VerifiedUser
			database.DB.Where("chat_id = ?", update.Message.From.ID).First(&foundVerifiedUser)

			if foundVerifiedUser.UserId == "" {
				logger.AddSystemLog(rslogger.LogTypeWarning, "User: %s %s has tried to get filter, but has not been verified", update.Message.From.FirstName, update.Message.From.LastName)
				SendNotification(chatID, "You have not been verified yet. Type /verify <code> to start receiving notifications. The code can be found in the user profile on the dashboard.")
			} else {
				formattedFilters := utils.GetFormattedFilters(foundVerifiedUser.Filter)
				logger.AddSystemLog(rslogger.LogTypeInfo, "User: %s %s has requested filter, current filter: %s", update.Message.From.FirstName, update.Message.From.LastName, formattedFilters)
				SendNotification(chatID, "Your current filter is: "+formattedFilters)
			}
		} else {
			logger.AddSystemLog(rslogger.LogTypeWarning, "Received unknown message: %s from user: %s %s", text, update.Message.From.FirstName, update.Message.From.LastName)
		}
	}
}

func SendNotification(chatId int64, message string) {
	msg := tgbotapi.NewMessage(chatId, message)

	if _, err := bot.Send(msg); err != nil {
		logger.AddSystemLog(rslogger.LogTypeError, "Failed to send notification to chatId %v message: %s, err: %v", chatId, message, err.Error())
	}
}

func setFilter(chatID int64, update tgbotapi.Update, text string, regFilterMatches []string) {
	logger.AddSystemLog(rslogger.LogTypeInfo, "Received filter message: %s from user: %s %s", text, update.Message.From.FirstName, update.Message.From.LastName)

	filter := regFilterMatches[1]
	filterValues := strings.Split(filter, ",")

	for _, v := range filterValues {
		switch v {
		case utils.NotificationTypeSuccessString:
		case utils.NotificationTypeInfoString:
		case utils.NotificationTypeWarningString:
		case utils.NotificationTypeErrorString:
			continue
		default:
			logger.AddSystemLog(rslogger.LogTypeWarning, "Received unknown filter value: %s from user: %s %s", v, update.Message.From.FirstName, update.Message.From.LastName)
			SendNotification(chatID, "You have entered an invalid filter value. Filter can be: success, info, warning, error. For example: /filter info,warning,error will filter all notifications except success.")
			return
		}
	}

	var filteredValues []string
	var filteredTextValues []string

	for _, v := range filterValues {
		switch v {
		case utils.NotificationTypeSuccessString:
			filteredValues = append(filteredValues, utils.NotificationTypeSuccess)
			filteredTextValues = append(filteredTextValues, utils.NotificationTypeSuccessString)
		case utils.NotificationTypeInfoString:
			filteredValues = append(filteredValues, utils.NotificationTypeInfo)
			filteredTextValues = append(filteredTextValues, utils.NotificationTypeInfoString)
		case utils.NotificationTypeWarningString:
			filteredValues = append(filteredValues, utils.NotificationTypeWarning)
			filteredTextValues = append(filteredTextValues, utils.NotificationTypeWarningString)
		case utils.NotificationTypeErrorString:
			filteredValues = append(filteredValues, utils.NotificationTypeError)
			filteredTextValues = append(filteredTextValues, utils.NotificationTypeErrorString)
		}
	}

	var foundVerifiedUser structs.VerifiedUser
	database.DB.Where("chat_id = ?", update.Message.From.ID).First(&foundVerifiedUser)

	if foundVerifiedUser.UserId == "" {
		logger.AddSystemLog(rslogger.LogTypeWarning, "User: %s %s has tried to set filter, but has not been verified", update.Message.From.FirstName, update.Message.From.LastName)
		SendNotification(chatID, "You have not been verified yet. Type /verify <code> to start receiving notifications. The code can be found in the user profile on the dashboard.")
		return
	}

	filter = strings.Join(filteredValues, ",")

	database.DB.Model(&structs.VerifiedUser{}).
		Where("chat_id = ?", update.Message.From.ID).
		Updates(structs.VerifiedUser{Filter: filter})

	formattedFilteredTextValues := strings.Join(filteredTextValues, ", ")

	logger.AddSystemLog(rslogger.LogTypeInfo, "User: %s %s has set filter to %s (%s)", update.Message.From.FirstName, update.Message.From.LastName, filter, formattedFilteredTextValues)

	SendNotification(chatID, "You have successfully set filter to "+formattedFilteredTextValues+". You will now only receive notifications with the selected filter.")
}
