package services

import (
	"ai_scheduler/internal/biz"
	"ai_scheduler/internal/biz/handle/qywx/json_callback/wxbizjsonmsgcrypt"
	"ai_scheduler/internal/config"
	"ai_scheduler/internal/data/constants"
	errorcode "ai_scheduler/internal/data/error"
	"ai_scheduler/internal/domain/component/callback"
	"ai_scheduler/internal/entitys"
	"ai_scheduler/internal/gateway"
	"ai_scheduler/internal/pkg"
	"ai_scheduler/internal/pkg/dingtalk"
	"ai_scheduler/internal/pkg/util"
	"ai_scheduler/internal/tool_callback"
	"context"
	"encoding/json"
	"fmt"
	"net/url"
	"strings"
	"time"

	"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/card"
	"gitea.cdlsxd.cn/self-tools/l-dingtalk-stream-sdk-go/chatbot"
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/log"
)

// CallbackService 统一回调入口
type CallbackService struct {
	cfg                   *config.Config
	gateway               *gateway.Gateway
	dingtalkOldClient     *dingtalk.OldClient
	dingtalkContactClient *dingtalk.ContactClient
	dingtalkNotableClient *dingtalk.NotableClient
	dingtalkCardClient    *dingtalk.CardClient
	callbackManager       callback.Manager
	dingTalkBotBiz        *biz.DingTalkBotBiz
	callbackBiz           *biz.CallbackBiz
	// ollamaClient          *utils_ollama.Client
	// botConfigImpl         *impl.BotConfigImpl
}

func NewCallbackService(
	cfg *config.Config,
	gateway *gateway.Gateway,
	dingtalkOldClient *dingtalk.OldClient,
	dingtalkContactClient *dingtalk.ContactClient,
	dingtalkNotableClient *dingtalk.NotableClient,
	dingtalkCardClient *dingtalk.CardClient,
	callbackManager callback.Manager,
	dingTalkBotBiz *biz.DingTalkBotBiz,
	callbackBiz *biz.CallbackBiz,
	// ollamaClient *utils_ollama.Client,
	// botConfigImpl *impl.BotConfigImpl,
) *CallbackService {
	return &CallbackService{
		cfg:                   cfg,
		gateway:               gateway,
		dingtalkOldClient:     dingtalkOldClient,
		dingtalkContactClient: dingtalkContactClient,
		dingtalkNotableClient: dingtalkNotableClient,
		dingtalkCardClient:    dingtalkCardClient,
		callbackManager:       callbackManager,
		dingTalkBotBiz:        dingTalkBotBiz,
		callbackBiz:           callbackBiz,
		// ollamaClient:          ollamaClient,
		// botConfigImpl:         botConfigImpl,
	}
}

// Envelope 回调统一请求体
type Envelope struct {
	Action string          `json:"action"`
	TaskID string          `json:"task_id"`
	Data   json.RawMessage `json:"data"`
}

// bug_optimization_submit 工单回调
const (
	ActionBugOptimizationSubmitProcess = "bug_optimization_submit_process" // 工单过程回调
	ActionBugOptimizationSubmitDone    = "bug_optimization_submit_done"    // 工单完成回调
	ActionBugOptimizationSubmitUpdate  = "bug_optimization_submit_update"  // 工单更新回调
)

// BugOptimizationSubmitDoneData 工单完成回调数据
type BugOptimizationSubmitDoneData struct {
	Receivers  []string `json:"receivers"`
	DetailPage string   `json:"detail_page"`
	Msg        string   `json:"msg"`
}

// BugOptimizationSubmitUpdateData 工单更新回调数据
type BugOptimizationSubmitUpdateData struct {
	BaseId   string `json:"base_id"`   // 表格ID
	SheetId  string `json:"sheet_id"`  // 表单ID
	RecordId string `json:"record_id"` // 记录ID
	UnionId  string `json:"union_id"`  // 钉钉用户 UnionID
	Creator  string `json:"creator"`   // 钉钉用户名称
}

// Callback 统一回调处理
// 头部：X-Source-Key / X-Timestamp
func (s *CallbackService) Callback(c *fiber.Ctx) error {
	// 读取头
	sourceKey := strings.TrimSpace(c.Get("X-Source-Key"))
	ts := strings.TrimSpace(c.Get("X-Timestamp"))

	// 时间窗口（如果提供了 ts 则校验，否则跳过），窗口 5 分钟
	// if ts != "" && !validateTimestamp(ts, 5*time.Minute) {
	if ts != "" && !util.IsInTimeWindow(ts, 5*time.Minute) {
		return errorcode.AuthNotFound
	}

	// 解析 Envelope
	var env Envelope
	if err := json.Unmarshal(c.Body(), &env); err != nil {
		return errorcode.ParamErrf("invalid json: %v", err)
	}
	if env.Action == "" || env.TaskID == "" {
		return errorcode.ParamErrf("missing action/task_id")
	}
	if env.Data == nil {
		return errorcode.ParamErrf("missing data")
	}

	switch sourceKey {
	case "dingtalk":
		return s.handleDingTalkCallback(c, env)
	default:
		return errorcode.AuthNotFound
	}
}

func (s *CallbackService) CallbackQr(c *fiber.Ctx) error {
	// 读取头
	sourceKey := strings.TrimSpace(c.Get("X-Source-Key"))
	ts := strings.TrimSpace(c.Get("X-Timestamp"))

	// 时间窗口（如果提供了 ts 则校验，否则跳过），窗口 5 分钟
	// if ts != "" && !validateTimestamp(ts, 5*time.Minute) {
	if ts != "" && !util.IsInTimeWindow(ts, 5*time.Minute) {
		return errorcode.AuthNotFound
	}

	// 解析 Envelope
	var env Envelope
	if err := json.Unmarshal(c.Body(), &env); err != nil {
		return errorcode.ParamErrf("invalid json: %v", err)
	}
	if env.Action == "" || env.TaskID == "" {
		return errorcode.ParamErrf("missing action/task_id")
	}
	if env.Data == nil {
		return errorcode.ParamErrf("missing data")
	}

	switch sourceKey {
	case "dingtalk":
		return s.handleDingTalkCallback(c, env)
	default:
		return errorcode.AuthNotFound
	}
}

func (s *CallbackService) handleDingTalkCallback(c *fiber.Ctx, env Envelope) error {
	// 校验taskId
	ctx := c.Context()
	sessionID, err := s.callbackManager.GetSession(ctx, env.TaskID)
	if err != nil {
		return errorcode.ParamErrf("failed to get session for task_id: %s, err: %v", env.TaskID, err)
	}
	if sessionID == "" {
		return errorcode.ParamErrf("missing session_id for task_id: %s", env.TaskID)
	}

	switch env.Action {
	case ActionBugOptimizationSubmitUpdate:
		// 业务处理
		msg, businessErr := s.handleBugOptimizationSubmitUpdate(ctx, env.Data)
		if businessErr != nil {
			return businessErr
		}

		s.sendStreamLog(sessionID, msg)

		return c.JSON(fiber.Map{"code": 0, "message": "ok"})
	case ActionBugOptimizationSubmitDone:
		// 业务处理
		msg, businessErr := s.handleBugOptimizationSubmitDone(ctx, env.Data)
		if businessErr != nil {
			return businessErr
		}

		// 发送日志
		s.sendStreamTxt(sessionID, msg)

		// 通知等待者
		if err := s.callbackManager.Notify(ctx, env.TaskID, msg); err != nil {
			// 记录错误但继续
		}

		return c.JSON(fiber.Map{"code": 0, "message": "ok"})
	case ActionBugOptimizationSubmitProcess:
		type processData struct {
			Process string `json:"process"`
		}
		var data processData
		if err := json.Unmarshal(env.Data, &data); err != nil {
			return errorcode.ParamErrf("invalid json: %v", err)
		}

		s.sendStreamLoading(sessionID, data.Process)

		return c.JSON(fiber.Map{"code": 0, "message": "ok"})
	default:
		return errorcode.ParamErrf("unknown action: %s", env.Action)
	}
}

// getDingtalkReceivers 解析接收者字符串为 DingTalk 用户 ID 列表
func (s *CallbackService) getDingtalkReceivers(ctx context.Context, receiverIds []string) string {
	var receiverNames []string
	for _, receiverId := range receiverIds {
		userDetails, err := s.dingtalkOldClient.QueryUserDetails(ctx, receiverId)
		if err != nil {
			return ""
		}
		if userDetails == nil {
			return ""
		}
		receiverNames = append(receiverNames, "@"+userDetails.Name)
	}

	receivers := strings.Join(receiverNames, " ")

	return receivers
}

// sendStreamLog 发送流式日志
func (s *CallbackService) sendStreamLog(sessionID string, content string) {
	if content == "" {
		return
	}

	streamLog := entitys.Response{
		Index:   string(constants.BotToolsBugOptimizationSubmit),
		Content: content,
		Type:    entitys.ResponseLog,
	}
	streamLogBytes := pkg.JsonByteIgonErr(streamLog)
	s.gateway.SendToUid(sessionID, streamLogBytes)
}

// sendStreamTxt 发送流式文本
func (s *CallbackService) sendStreamTxt(sessionID string, content string) {
	if content == "" {
		return
	}

	streamLog := entitys.Response{
		Index:   string(constants.BotToolsBugOptimizationSubmit),
		Content: content,
		Type:    entitys.ResponseText,
	}
	streamLogBytes := pkg.JsonByteIgonErr(streamLog)
	s.gateway.SendToUid(sessionID, streamLogBytes)
}

// sendStreamLoading 发送流式加载过程
func (s *CallbackService) sendStreamLoading(sessionID string, content string) {
	if content == "" {
		return
	}

	streamLog := entitys.Response{
		Index:   string(constants.BotToolsBugOptimizationSubmit),
		Content: content,
		Type:    entitys.ResponseLoading,
	}
	streamLogBytes := pkg.JsonByteIgonErr(streamLog)
	s.gateway.SendToUid(sessionID, streamLogBytes)
}

// handleBugOptimizationSubmitUpdate 处理 bug 优化提交更新回调
func (s *CallbackService) handleBugOptimizationSubmitUpdate(ctx context.Context, taskData json.RawMessage) (string, *errorcode.BusinessErr) {
	var data BugOptimizationSubmitUpdateData
	if err := json.Unmarshal(taskData, &data); err != nil {
		return "", errorcode.ParamErrf("invalid data type: %v", err)
	}

	if data.Creator == "" {
		return "", errorcode.ParamErrf("empty creator")
	}

	// 获取创建者uid
	accessToken, _ := s.dingtalkOldClient.GetAccessToken()
	creatorId, err := s.dingtalkContactClient.SearchUserOne(dingtalk.AppKey{AccessToken: accessToken}, data.Creator)
	if err != nil {
		return "", errorcode.ParamErrf("invalid data type: %v", err)
	}

	// 获取用户详情
	userDetails, err := s.dingtalkOldClient.QueryUserDetails(ctx, creatorId)
	if err != nil {
		return "", errorcode.ParamErrf("invalid data type: %v", err)
	}
	if userDetails == nil {
		return "", errorcode.ParamErrf("user details not found")
	}
	unionId := userDetails.UnionID

	// 更新记录
	ok, err := s.dingtalkNotableClient.UpdateRecord(dingtalk.AppKey{AccessToken: accessToken}, &dingtalk.UpdateRecordReq{
		BaseId:         data.BaseId,
		SheetId:        data.SheetId,
		RecordId:       data.RecordId,
		OperatorId:     tool_callback.BotBugOptimizationSubmitAdminUnionId,
		CreatorUnionId: unionId,
	})
	if err != nil {
		return "", errorcode.ParamErrf("invalid data type: %v", err)
	}
	if !ok {
		return "", errorcode.ParamErrf("update record failed")
	}

	return "问题记录即将完成", nil
}

// handleBugOptimizationSubmitDone 处理 bug 优化提交完成回调
func (s *CallbackService) handleBugOptimizationSubmitDone(ctx context.Context, taskData json.RawMessage) (string, *errorcode.BusinessErr) {
	var data BugOptimizationSubmitDoneData
	if err := json.Unmarshal(taskData, &data); err != nil {
		return "", errorcode.ParamErrf("invalid data type: %v", err)
	}

	if len(data.Receivers) == 0 {
		return "", errorcode.ParamErrf("empty receivers")
	}
	// 构建接收者
	receivers := s.getDingtalkReceivers(ctx, data.Receivers)
	if receivers == "" {
		return "", errorcode.ParamErrf("invalid receivers")
	}

	// 构建跳转链接
	var detailPage string
	if data.DetailPage != "" {
		detailPage = util.BuildJumpLink(data.DetailPage, "去查看")
	}

	msg := data.Msg
	msg = util.ReplacePlaceholder(msg, "receivers", receivers)
	msg = util.ReplacePlaceholder(msg, "detail_page", detailPage)

	return msg, nil
}

func (s *CallbackService) QywxCallback(c *fiber.Ctx) (err error) {
	// 读取头
	httpstr := string(c.Request().URI().QueryString())

	start := strings.Index(httpstr, "msg_signature=")
	start += len("msg_signature=")
	var msgSignature string
	next := getString(httpstr, "&timestamp=", start, &msgSignature)
	var timestamp string
	next = getString(httpstr, "&nonce=", next, &timestamp)
	var nonce string
	next = getString(httpstr, "&echostr=", next, &nonce)
	echostr := httpstr[next:len(httpstr)]
	echostr, _ = url.QueryUnescape(echostr)
	fmt.Println(httpstr, msgSignature, timestamp, nonce, echostr)
	wxcpt := wxbizjsonmsgcrypt.NewWXBizMsgCrypt(s.cfg.Qywx.Token, s.cfg.Qywx.AES_KEY, s.cfg.Qywx.CorpId, wxbizjsonmsgcrypt.JsonType)
	echoStr, cryptErr := wxcpt.VerifyURL(msgSignature, timestamp, nonce, echostr)
	if nil != cryptErr {
		log.Errorf("%v", cryptErr)
		return fmt.Errorf("%v", cryptErr)
	}
	fmt.Println("verifyUrl success echoStr", string(echoStr))
	err = c.Send(echoStr)
	return err

}

func getString(str, endstr string, start int, msg *string) int {
	end := strings.Index(str, endstr)
	*msg = str[start:end]
	return end + len(endstr)
}

// CallbackDingtalkRobot 钉钉机器人回调
// 钉钉 callbackRouteKey: gateway.dev.cdlsxd.cn-dingtalk-robot
// 钉钉 apiSecret: aB3dE7fG9hI2jK4L5M6N7O8P9Q0R1S2T
func (s *CallbackService) CallbackDingtalkRobot(c *fiber.Ctx) (err error) {
	// 获取body中的参数
	body := c.Request().Body()
	var data chatbot.BotCallbackDataModel
	if err := json.Unmarshal(body, &data); err != nil {
		return fmt.Errorf("invalid body: %v", err)
	}

	// token 校验 ? token 好像没带？

	// 通过机器人ID路由到不同能力
	switch data.RobotCode {
	case s.cfg.Dingtalk.SceneGroup.GroupTemplateRobotIDIssueHandling:
		// 问题处理群机器人
		// err := s.issueHandling(data)
		err := s.callbackBiz.IssueHandlingGroup(data)
		if err != nil {
			return fmt.Errorf("IssueHandlingGroup failed: %v", err)
		}
	default:
		// 其他机器人
		return nil
	}

	return nil
}

// CallbackDingtalkCard 处理钉钉卡片回调
// 钉钉 callbackRouteKey: gateway.dev.cdlsxd.cn-dingtalk-card
// 钉钉 apiSecret: aB3dE7fG9hI2jK4L5M6N7O8P9Q0R1S2T
func (s *CallbackService) CallbackDingtalkCard(c *fiber.Ctx) error {
	// 获取body中的参数
	body := c.Request().Body()

	// HTTP 回调结构与SDK结构体不符，包装结构体
	tmp := struct {
		card.CardRequest                   // 嵌入原结构体
		UserIdType       util.FlexibleType `json:"userIdType"` // 重写type字段
	}{}
	if err := json.Unmarshal(body, &tmp); err != nil {
		return fmt.Errorf("invalid body: %v", err)
	}
	// 异常字段覆盖
	data := tmp.CardRequest
	data.UserIdType = tmp.UserIdType.Int()
	if err := json.Unmarshal([]byte(data.Content), &data.CardActionData); err != nil {
		return fmt.Errorf("invalid content: %v", err)
	}
	log.Debug("body：%s", string(body))

	// 非回调类型不处理
	if data.Type != constants.CardActionCallbackTypeAction {
		return nil
	}

	// 处理卡片回调
	var resp *card.CardResponse
	for _, actionId := range data.CardActionData.CardPrivateData.ActionIdList {
		switch actionId {
		case "collect_qa":
			// 问题处理群机器人 QA 收集
			resp = s.callbackBiz.IssueHandlingCollectQA(data)
		}
	}

	// 跳过响应包装
	c.Locals("skip_response_wrap", true)
	return c.JSON(resp)
}
