package compapi

import (
	"errors"
	"fmt"
	"wechat-api/ent"
	"wechat-api/internal/types"

	"github.com/openai/openai-go"
)

type MismatchClient struct {
	StdClient
	ResformatConfig ResponseFormatConfig
}

func (me *MismatchClient) ResponseFormatSetting(req *types.CompApiReq) ResponseFormatConfig {

	//Message重构的配置
	me.ResformatConfig.SysmesArgs = []any{req.Variables["missed"]}
	me.ResformatConfig.SysmesTmpl = ` # 任务介绍
		公司在用程序巡检电话通话记录时，有些用户的回复内容没有被关键词库识别到，请根据以下通话记录，给出一些类似的回复，以及对关键词库的具体建议，非常感谢！

		# 输出要求
		1. 用户意图：结合上下文，首先考虑可能的语音识别错误并纠错，然后分析未识别内容的根本意图，用户说这句话的背后真实目的是什么，结合上下文挖掘用户最深的意图，请不要停留在表面的意思。
		2. 类似回复：首先应包含未识别的内容原文：%s。其次生成尽可能多的类似意图的回复。
		2. 关键词库：从类似回复中抽取特征词，要求有两点：一是特征词应尽可能的准确，即要覆盖住此类问题，又要尽可能避免和其他语境相冲突。二是需要注重通用性，一些专有名词、人名、地名、产品名、公司名等需要排除在外。
		3. 正则表达式：对关键词词进行解耦，用正则表达式进行表示，专注于主要内容，排除次要内容

		# 注意事项
		1. 应贴近真实的外呼场景，用户的素质参差不齐，请避免使用任何浮夸的用词，避免使用高级词汇，避免使用任何的礼貌用语或敬语，适当的低素质些，请至少给出20条结果
		2. 通话记录为录音转译，因此可能有错别字或音同字不同的情况（例如：借和接），请根据上下文分析后就成可能的错字错误
		3. 正则关键词库和正则表达式中也应该考虑到音同字不同的情况，且避免使用匹配次数相关的语法如{0,2}`

	me.ResformatConfig.UsermesArgs = []any{req.Variables["chat_history"], req.Variables["missed"]}
	me.ResformatConfig.UsermesTmpl = `
	# 通话记录%s

	# 可能识别有误的内容：%s`

	//ResponseFormat设置的配置
	me.ResformatConfig.ResformatDesc = "从通话记录中提取表单"
	me.ResformatConfig.ResformatTxt = `{
    "user_intent": str, #用户意图
    "similar_reply": list[str], #类似回复
    "keywords": list[str], #关键词库
    "regular": list[str], #正则表达式
}`
	me.ResformatConfig.ResformatStruct = struct {
		UserIntent   string   `json:"user_intent" jsonschema_description:"用户意图"`
		SimilarReply []string `json:"similar_reply" jsonschema_description:"类似回复"`
		Keywords     []string `json:"keywords" jsonschema_description:"关键词库"`
		Regular      []string `json:"regular" jsonschema_description:"正则表达式"`
	}{}
	me.ResformatConfig.HaveSet = true

	return me.ResformatConfig
}

// 向Client.getClientActFace工厂方法注册
func init() {
	builder := func(c *Client) (clientActionFace, error) {
		return &MismatchClient{StdClient: StdClient{Client: c}}, nil
	}
	err := RegisterClient("mismatch", builder)
	if err != nil {
		//panic(fmt.Sprintf("Failed to register client type 'mismatch': %v", err))
	}
}

type ResponseFormatConfig struct {
	SysmesTmpl  string `json:"sysmes_tmpl" jsonschema_description:"系统消息文本模板"`
	SysmesArgs  []any  `json:"sysmes_args" jsonschema_description:"系统消息文本参数列表"`
	UsermesTmpl string `json:"usermes_tmpl" jsonschema_description:"用户消息文本模板"`
	UsermesArgs []any  `json:"usermes_args" jsonschema_description:"用户消息文本参数列表"`

	ResformatDesc   string `json:"resformat_desc" jsonschema_description:"response_format的介绍描述"`
	ResformatTxt    string `json:"resformat_txt" jsonschema_description:"response_format文本版本"`
	ResformatStruct any    `json:"resformat_struct" jsonschema_description:"response_format结构体版本"`
	HaveSet         bool
}

type StringGen func() string

func CreateStringGen(format string, args ...interface{}) StringGen {
	return func() string {
		return fmt.Sprintf(format, args...)
	}
}

/*

{"response_format":
	{"type":"json_schema",
	 "json_schema":
	 	{"description":"从通话记录中提取表单",
		 "name":"keyword_schema",
		 "schema":
		 	{"$schema":"https://json-schema.org/draft/2020-12/schema","additionalProperties":false,
			 "properties":
			 	{"keywords":
					{"description":"关键词库","items":{"type":"string"},"type":"array"},
				"regular":
					{"description":"正则表达式","items":{"type":"string"},"type":"array"},
				"similar_reply":
					{"description":"类似回复","items":{"type":"string"},"type":"array"},
				"user_intent":{"description":"用户意图","type":"string"}
			    },
		     "required":
				["user_intent","similar_reply","keywords","regular"],
		      "type":"object"
		    },
		"strict":true
	   }
	}
}

{"response_format":
	{"type":"json_object"}
	}
*/

func (me *MismatchClient) RebuildMessage(openaiModel bool) []types.StdCompMessage {
	newMessSlice := make([]types.StdCompMessage, 2)
	newMessSlice[0] = types.StdCompMessage{Role: "system",
		Content: CreateStringGen(me.ResformatConfig.SysmesTmpl, me.ResformatConfig.SysmesArgs...)()}
	resFormatStr := ""
	if !openaiModel {
		resFormatStr = CreateStringGen(me.ResformatConfig.ResformatTxt)()
	}
	newMessSlice[1] = types.StdCompMessage{Role: "user",
		Content: CreateStringGen(me.ResformatConfig.UsermesTmpl, me.ResformatConfig.UsermesArgs...)() + resFormatStr}

	return newMessSlice
}

func (me *MismatchClient) BuildJsonSchemaParam() (*openai.ResponseFormatJSONSchemaJSONSchemaParam, error) {
	schema, err := GenerateSchemaFromValue(me.ResformatConfig.ResformatStruct)
	if err != nil {
		return nil, err
	}
	schemaParam := openai.ResponseFormatJSONSchemaJSONSchemaParam{
		Name:        "keyword_schema",
		Description: openai.String(me.ResformatConfig.ResformatDesc),
		Schema:      schema,
		Strict:      openai.Bool(true),
	}
	return &schemaParam, nil
}

func (me *MismatchClient) GetResFormat(openaiModel bool) (any, error) {
	if openaiModel { //针对openai兼容api 设置JsonSchema 定制化输出结构
		schemaParam, err := me.BuildJsonSchemaParam()
		if err != nil {
			return nil, err
		}
		return &openai.ResponseFormatJSONSchemaParam{JSONSchema: *schemaParam}, nil
	} else { //针对不兼容比如Deepseek系设置JsonObject
		return &openai.ResponseFormatJSONObjectParam{Type: "json_object"}, nil
	}
}

func (me *MismatchClient) BuildRequest(req *types.CompApiReq) error {

	//设置相关个性化ResponseFormat配置信息
	if !me.ResformatConfig.HaveSet {
		me.ResponseFormatSetting(req)
	}
	openaiModel := IsOpenaiModel(req.Model)
	//重构req.Message
	req.Messages = me.RebuildMessage(openaiModel)
	//设置req.ResponseFormat
	var err error
	req.ResponseFormat, err = me.GetResFormat(openaiModel)

	return err
}

func (me *MismatchClient) CallbackPrepare(params any) ([]byte, error) {

	taskData, ok := params.(*ent.CompapiAsynctask)
	if !ok {
		return nil, errors.New("invalid callback taskdata")
	}
	type OutResult struct {
		InternalID string `json:"internal_id"`
		ExternalID string `json:"external_id"`
		ChatID     string `json:"chat_id"`
		EventType  string `json:"event_type"`
		Content    string `json:"content"`
	}

	res := OutResult{}
	res.InternalID = fmt.Sprintf("%d", taskData.ID)
	res.ExternalID = taskData.ResponseChatItemID
	res.EventType = taskData.EventType
	res.ChatID = taskData.ChatID
	var err error
	res.Content, err = NewChatResult(taskData.ResponseRaw).GetContentJsonStr()
	if err != nil {
		return nil, err
	}
	return WrapJSON(res, "", false)
}
