package v1

import (
	"context"
	"fmt"
	"regexp"
	"strconv"
	"strings"

	v1 "github.com/pingcap-inc/pingcat/model/v1"

	"github.com/pingcap-inc/pchub/pkg/errors"

	"github.com/pingcap-inc/pchub/pkg/log"
)

func (s *ChatService) handleIfCommand(c context.Context, me *v1.LarkMsgEvent, msg string) bool {
	trimStr := strings.TrimSpace(msg)
	apiConf, _ := s.store.UserOpenAI().GetByUser(c, me.Sender.SenderID.UserID)
	atReg := regexp.MustCompile(`(@\w+\s*)`)
	rmAtStr := atReg.ReplaceAllString(trimStr, "")
	rmAtStr = strings.TrimSpace(rmAtStr)

	if strings.HasPrefix(rmAtStr, "/settings ") || rmAtStr == "/settings" {
		s.handleSettings(c, me, apiConf, rmAtStr)

		return true
	}

	if strings.HasPrefix(rmAtStr, "/help ") || rmAtStr == "/help" {
		s.handleHelp(c, me)

		return true
	}

	if strings.HasPrefix(trimStr, "+++") || strings.HasPrefix(trimStr, "---") || strings.HasPrefix(trimStr, "===") || trimStr == "clear" || trimStr == "/clear" {
		if me.Message.ChatType == v1.ChatTypeP2P {
			s.handleNewSession(c, me)
		}

		return true
	}

	return false
}

func (s *ChatService) handleHelp(ctx context.Context, me *v1.LarkMsgEvent) {
	outMsgCont := v1.P2PHelpInfo
	if me.Message.ChatType == v1.ChatTypeGroup || me.Message.ChatType == v1.ChatTypeTopicGroup {
		outMsgCont = v1.GroupHelpInfo
	}
	_, err := s.SendTextLarkMsg(ctx, me, outMsgCont)
	_, er := s.SendShareLarkChat(ctx, me, AskPingCatChatID)
	if err != nil || er != nil {
		log.L(ctx).Error(errors.Wrap(err, "Error when send text lark msg").Error())
	}
}

func (s *ChatService) handleNewSession(ctx context.Context, me *v1.LarkMsgEvent) {
	msgID := me.Message.MessageID
	d := v1.MsgReqData{
		Msg: s.getLarkTextMsgContent(ctx, me.Message.Content),
	}
	inMsg, _ := s.constructInMsg(ctx, d, v1.SceneLarkP2P, msgID)
	inMsg.Semantic = v1.SemanticEndSession
	_, err := s.store.Message().Create(ctx, inMsg)
	if err != nil {
		log.L(ctx).Error(errors.Wrap(err, "Error when create endsession input msg in db").Error())
	}

	outMsgCont := "Okay, let's talk about something new, miaow~"
	_, err = s.SendTextLarkMsg(ctx, me, outMsgCont)
	if err != nil {
		log.L(ctx).Error(errors.Wrap(err, "Error when send text lark msg").Error())
	}
}

//nolint:gocognit,gocyclo,cyclop // acceptable

const SettingsHelpInfo = `You can user /settings command to set parameters for your gpt api.

Sub Commands:

  get: show current settings
    usage:
      /settings get
      /settings get [parameter]
    example:
      /settings get: This will show all settings.
      /settings get model: This will show your model setting.

  set: set new settings
    usage:
      /settings set parameter=value
    example:
      /settings set model=gpt4: This will set your model to gpt4.

  reset/remove: reset(remove) settings to default
    usage:
      /settings reset
      /settings reset/remove [parameter]
    example:
      /settings reset: This will reset all of your settings.
      /settings reset model: This will only reset your model setting to default.

  help: show help info

Parameters:
  model: gpt model alias, default is "gpt3". Valid values: "gpt3" or "gpt4".
  max_tokens: max_tokens for gpt api, default is determined by the API.
  temperature: temperature for gpt api, default is determined by the API.
  top_p: top_p for gpt api, default is determined by the API.
`

func (s *ChatService) handleSettings(ctx context.Context, me *v1.LarkMsgEvent, apiConf *v1.UserOpenAI, trimStr string) {
	if trimStr == "/settings" {
		trimStr = "/settings help"
	}
	cmdParts := regexp.MustCompile(`\s+`).Split(trimStr, -1)
	cmd := cmdParts[1]
	switch cmd {
	case "help":
		_, _ = s.SendTextLarkMsg(ctx, me, SettingsHelpInfo)
	case "get":
		s.handleGetSettings(ctx, me, apiConf, cmdParts)
	case "set":
		s.handleSetSettings(ctx, me, apiConf, cmdParts)
	case "reset", "remove":
		s.handleResetSettings(ctx, me, apiConf, cmdParts)
	default:
		_, _ = s.SendTextLarkMsg(ctx, me, SettingsHelpInfo)
	}
}

func (s *ChatService) handleGetSettings(ctx context.Context, me *v1.LarkMsgEvent, apiConf *v1.UserOpenAI, cmdParts []string) {
	if len(cmdParts) == 2 {
		if apiConf == nil || apiConf.ID == 0 {
			_, _ = s.SendTextLarkMsg(ctx, me, "You have not set any settings yet. So the default settings will be applied.")

			return
		}
		var smsg strings.Builder
		smsg.WriteString("Your settings:\n\n")
		if apiConf.Model != "" {
			smsg.WriteString("model: " + apiConf.Model + "\n")
		}
		if apiConf.MaxTokens != 0 {
			smsg.WriteString("max_tokens: " + strconv.Itoa(apiConf.MaxTokens) + "\n")
		}
		if apiConf.Temperature != 0.0 {
			smsg.WriteString("temperature: " + fmt.Sprintf("%f", apiConf.Temperature) + "\n")
		}
		if apiConf.TopP != 0.0 {
			smsg.WriteString("top_p: " + fmt.Sprintf("%f", apiConf.TopP))
		}
		_, _ = s.SendTextLarkMsg(ctx, me, smsg.String())
	} else {
		param := cmdParts[2]
		switch param {
		case "model":
			if apiConf.Model != "" {
				_, _ = s.SendTextLarkMsg(ctx, me, "Your model setting: "+apiConf.Model)
			} else {
				_, _ = s.SendTextLarkMsg(ctx, me, "You have not set model setting yet. So the default model setting will be applied.")
			}
		case "max_tokens":
			if apiConf.MaxTokens != 0 {
				_, _ = s.SendTextLarkMsg(ctx, me, "Your max_tokens setting: "+strconv.Itoa(apiConf.MaxTokens))
			} else {
				_, _ = s.SendTextLarkMsg(ctx, me, "You have not set max_tokens setting yet. So the default max_tokens setting will be applied.")
			}
		case "temperature":
			if apiConf.Temperature != 0.0 {
				_, _ = s.SendTextLarkMsg(ctx, me, "Your temperature setting: "+fmt.Sprintf("%f", apiConf.Temperature))
			} else {
				_, _ = s.SendTextLarkMsg(ctx, me, "You have not set temperature setting yet. So the default temperature setting will be applied.")
			}
		case "top_p":
			if apiConf.TopP != 0.0 {
				_, _ = s.SendTextLarkMsg(ctx, me, "Your top_p setting: "+fmt.Sprintf("%f", apiConf.TopP))
			} else {
				_, _ = s.SendTextLarkMsg(ctx, me, "You have not set top_p setting yet. So the default top_p setting will be applied.")
			}
		default:
			_, _ = s.SendTextLarkMsg(ctx, me, "Unknown setting parameter: "+param)
		}
	}
}

func (s *ChatService) handleSetSettings(ctx context.Context, me *v1.LarkMsgEvent, apiConf *v1.UserOpenAI, cmdParts []string) {
	if len(cmdParts) == 2 {
		_, _ = s.SendTextLarkMsg(ctx, me, "Please specify the setting parameter.")

		return
	}
	paramVal := cmdParts[2]
	p := regexp.MustCompile(`=`).Split(paramVal, 2)
	if len(p) != 2 {
		_, _ = s.SendTextLarkMsg(ctx, me, "Please specify the setting parameter correctly, such as: /settings set model=gpt4.")

		return
	}
	param := p[0]
	val := p[1]
	if val == "" {
		_, _ = s.SendTextLarkMsg(ctx, me, "Please specify the setting parameter value.")

		return
	}
	if apiConf == nil || apiConf.ID == 0 {
		apiConf = &v1.UserOpenAI{
			UserLarkID: me.Sender.SenderID.UserID,
		}
	}
	switch param {
	case "model":
		apiConf.Model = strings.ToLower(val)
		_, err := s.store.UserOpenAI().UpInsert(ctx, apiConf)
		if err != nil {
			log.L(ctx).Error("UserOpenAIRecord create/update failed, err: " + err.Error())
			_, _ = s.SendTextLarkMsg(ctx, me, "Can not set your model setting.")

			return
		}
		_, _ = s.SendTextLarkMsg(ctx, me, fmt.Sprintf("Your model setting has been set to %s.", val))
	case "max_tokens":
		maxTokens, err := strconv.Atoi(val)
		if err != nil {
			_, _ = s.SendTextLarkMsg(ctx, me, "Please specify the max_tokens setting parameter correctly.")

			return
		}
		apiConf.MaxTokens = maxTokens
		_, err = s.store.UserOpenAI().UpInsert(ctx, apiConf)
		if err != nil {
			log.L(ctx).Error("UserOpenAIRecord create/update failed, err: " + err.Error())
			_, _ = s.SendTextLarkMsg(ctx, me, "Can not set your max_tokens setting.")

			return
		}
		_, _ = s.SendTextLarkMsg(ctx, me, fmt.Sprintf("Your max_tokens setting has been set to %d.", maxTokens))
	case "temperature":
		temperature, err := strconv.ParseFloat(val, 64)
		if err != nil {
			_, _ = s.SendTextLarkMsg(ctx, me, "Please specify the temperature setting parameter correctly.")

			return
		}
		apiConf.Temperature = float32(temperature)
		_, err = s.store.UserOpenAI().UpInsert(ctx, apiConf)
		if err != nil {
			log.L(ctx).Error("UserOpenAIRecord create/update failed, err: " + err.Error())
			_, _ = s.SendTextLarkMsg(ctx, me, "Can not set your temperature setting.")

			return
		}
		_, _ = s.SendTextLarkMsg(ctx, me, fmt.Sprintf("Your temperature setting has been set to %f.", temperature))
	case "top_p":
		topP, err := strconv.ParseFloat(val, 64)
		if err != nil {
			_, _ = s.SendTextLarkMsg(ctx, me, "Please specify the top_p setting parameter correctly.")

			return
		}
		apiConf.TopP = float32(topP)
		_, err = s.store.UserOpenAI().UpInsert(ctx, apiConf)
		if err != nil {
			log.L(ctx).Error("UserOpenAIRecord create/update failed, err: " + err.Error())
			_, _ = s.SendTextLarkMsg(ctx, me, "Can not set your top_p setting.")

			return
		}
		_, _ = s.SendTextLarkMsg(ctx, me, fmt.Sprintf("Your top_p setting has been set to %f.", topP))
	default:
		_, _ = s.SendTextLarkMsg(ctx, me, "Unknown setting parameter: "+param)
	}
}

func (s *ChatService) handleResetSettings(ctx context.Context, me *v1.LarkMsgEvent, apiConf *v1.UserOpenAI, cmdParts []string) {
	if len(cmdParts) == 2 {
		if apiConf != nil && apiConf.ID > 0 {
			err := s.store.UserOpenAI().Delete(ctx, apiConf)
			if err != nil {
				log.L(ctx).Error("UserOpenAIRecord delete failed, err: " + err.Error())
				_, _ = s.SendTextLarkMsg(ctx, me, "Can not reset your settings.")

				return
			}
		}
		_, _ = s.SendTextLarkMsg(ctx, me, "Your settings have been reset to default.")
	} else {
		param := cmdParts[2]
		if apiConf == nil || apiConf.ID == 0 {
			_, _ = s.SendTextLarkMsg(ctx, me, "You have not set any settings yet. So you can not reset any settings.")

			return
		}
		switch param {
		case "model":
			apiConf.Model = "gpt3"
			_, err := s.store.UserOpenAI().UpInsert(ctx, apiConf)
			if err != nil {
				log.L(ctx).Error("UserOpenAIRecord create/update failed, err: " + err.Error())
				_, _ = s.SendTextLarkMsg(ctx, me, "Can not reset your model setting.")

				return
			}
			_, _ = s.SendTextLarkMsg(ctx, me, "Your model setting has been reset to default.")
		case "max_tokens":
			apiConf.MaxTokens = 0
			_, err := s.store.UserOpenAI().UpInsert(ctx, apiConf)
			if err != nil {
				log.L(ctx).Error("UserOpenAIRecord create/update failed, err: " + err.Error())
				_, _ = s.SendTextLarkMsg(ctx, me, "Can not reset your max_tokens setting.")

				return
			}
			_, _ = s.SendTextLarkMsg(ctx, me, "Your max_tokens setting has been reset to default.")
		case "temperature":
			apiConf.Temperature = 0.0
			_, err := s.store.UserOpenAI().UpInsert(ctx, apiConf)
			if err != nil {
				log.L(ctx).Error("UserOpenAIRecord create/update failed, err: " + err.Error())
				_, _ = s.SendTextLarkMsg(ctx, me, "Can not reset your temperature setting.")

				return
			}
			_, _ = s.SendTextLarkMsg(ctx, me, "Your temperature setting has been reset to default.")
		case "top_p":
			apiConf.TopP = 0.0
			_, err := s.store.UserOpenAI().UpInsert(ctx, apiConf)
			if err != nil {
				log.L(ctx).Error("UserOpenAIRecord create/update failed, err: " + err.Error())
				_, _ = s.SendTextLarkMsg(ctx, me, "Can not reset your top_p setting.")

				return
			}
			_, _ = s.SendTextLarkMsg(ctx, me, "Your top_p setting has been reset to default.")
		default:
			_, _ = s.SendTextLarkMsg(ctx, me, "Unknown setting parameter: "+param)
		}
	}
}
