package dingtalk

import (
	"ai_scheduler/internal/data/constants"
	"ai_scheduler/internal/pkg"
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"sync"
	"time"

	openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
	dingtalkim_1_0 "github.com/alibabacloud-go/dingtalk/im_1_0"
	util "github.com/alibabacloud-go/tea-utils/v2/service"
	"github.com/alibabacloud-go/tea/tea"
	"github.com/gofiber/fiber/v2/log"
	"github.com/google/uuid"
)

const DefaultInterval = 100 * time.Millisecond
const HeardBeatX = 100

type SendCardClient struct {
	Auth       *Auth
	CardClient *sync.Map
	mu         sync.RWMutex  // 保护 CardClient 的并发访问
	logger     log.AllLogger // 日志记录
	botOption  *Bot
}

func NewSendCardClient(auth *Auth, logger log.AllLogger) *SendCardClient {
	return &SendCardClient{
		Auth:       auth,
		CardClient: &sync.Map{},
		logger:     logger,
		botOption:  &Bot{},
	}
}

// initClient 初始化或复用 DingTalk 客户端
func (s *SendCardClient) initClient(robotCode string) (*dingtalkim_1_0.Client, error) {
	if client, ok := s.CardClient.Load(robotCode); ok {
		return client.(*dingtalkim_1_0.Client), nil
	}
	s.botOption.BotCode = robotCode
	config := &openapi.Config{
		Protocol: tea.String("https"),
		RegionId: tea.String("central"),
	}
	client, err := dingtalkim_1_0.NewClient(config)
	if err != nil {
		s.logger.Error("failed to init DingTalk client")
		return nil, fmt.Errorf("init client failed: %w", err)
	}

	s.CardClient.Store(robotCode, client)
	return client, nil
}

func (s *SendCardClient) NewCard(ctx context.Context, cardSend *CardSend) error {
	// 参数校验
	if (len(cardSend.ContentSlice) == 0 || cardSend.ContentSlice == nil) && cardSend.ContentChannel == nil {
		return errors.New("卡片内容不能为空")
	}
	if cardSend.UpdateInterval == 0 {
		cardSend.UpdateInterval = DefaultInterval // 默认更新间隔
	}
	if cardSend.Title == "" {
		cardSend.Title = "钉钉卡片"
	}
	//替换标题
	replace, err := pkg.SafeReplace(string(cardSend.Template), "${title}", cardSend.Title)
	if err != nil {
		return err
	}
	cardSend.Template = constants.CardTemp(replace)
	// 初始化客户端
	client, err := s.initClient(cardSend.RobotCode)
	if err != nil {
		return fmt.Errorf("初始化client失败: %w", err)
	}

	// 生成卡片实例ID
	cardInstanceId, err := uuid.NewUUID()
	if err != nil {
		return fmt.Errorf("创建uuid失败: %w", err)
	}

	// 构建初始请求
	request, err := s.buildBaseRequest(cardSend, cardInstanceId.String())
	if err != nil {
		return fmt.Errorf("请求失败: %w", err)
	}

	// 发送初始卡片
	if _, err := s.SendInteractiveCard(ctx, request, cardSend.RobotCode, client); err != nil {
		return fmt.Errorf("发送初始卡片失败: %w", err)
	}

	// 处理切片内容（同步）
	if len(cardSend.ContentSlice) > 0 {
		if err := s.processContentSlice(ctx, cardSend, cardInstanceId.String(), client); err != nil {
			return fmt.Errorf("内容同步失败: %w", err)
		}
	}

	// 处理通道内容（异步）
	if cardSend.ContentChannel != nil {
		var wg sync.WaitGroup
		wg.Add(1)
		go func() {
			defer wg.Done()
			s.processContentChannel(ctx, cardSend, cardInstanceId.String(), client)
		}()
		wg.Wait()
		log.Info("处理通道结束")
	}

	return nil
}

// buildBaseRequest 构建基础请求
func (s *SendCardClient) buildBaseRequest(cardSend *CardSend, cardInstanceId string) (*dingtalkim_1_0.SendRobotInteractiveCardRequest, error) {
	cardData := fmt.Sprintf(string(cardSend.Template), "") // 初始空内容
	request := &dingtalkim_1_0.SendRobotInteractiveCardRequest{
		CardTemplateId: tea.String("StandardCard"),
		CardBizId:      tea.String(cardInstanceId),
		CardData:       tea.String(cardData),
		RobotCode:      tea.String(cardSend.RobotCode),
		SendOptions:    &dingtalkim_1_0.SendRobotInteractiveCardRequestSendOptions{},
		PullStrategy:   tea.Bool(false),
	}

	switch cardSend.ConversationType {
	case constants.ConversationTypeGroup:
		request.SetOpenConversationId(cardSend.ConversationId)
	case constants.ConversationTypeSingle:
		receiver, err := json.Marshal(map[string]string{"userId": cardSend.SenderStaffId})
		if err != nil {
			return nil, fmt.Errorf("数据整理失败: %w", err)
		}
		request.SetSingleChatReceiver(string(receiver))
	default:
		return nil, errors.New("未知的聊天场景")
	}

	return request, nil
}

// processContentChannel 处理通道内容（异步更新）
func (s *SendCardClient) processContentChannel(ctx context.Context, cardSend *CardSend, cardInstanceId string, client *dingtalkim_1_0.Client) {
	defer func() {
		if r := recover(); r != nil {
			s.logger.Error("panic in processContentChannel")
		}
	}()

	ticker := time.NewTicker(cardSend.UpdateInterval)
	defer ticker.Stop()
	heartbeatTicker := time.NewTicker(time.Duration(HeardBeatX) * DefaultInterval)
	defer heartbeatTicker.Stop()

	var (
		contentBuilder strings.Builder
		lastUpdate     = time.Now()
	)
	for {

		select {
		case content, ok := <-cardSend.ContentChannel:
			if !ok {
				// 通道关闭，发送最终内容
				if contentBuilder.Len() > 0 {
					if err := s.updateCardContent(ctx, cardSend, cardInstanceId, contentBuilder.String(), client); err != nil {
						log.Info("contentBuilder.Len()修改失败1")
						s.logger.Errorf("更新卡片失败1:%s", err.Error())
					}
				}
				return
			}
			contentBuilder.WriteString(content)
			if contentBuilder.Len() > 0 {
				if err := s.updateCardContent(ctx, cardSend, cardInstanceId, contentBuilder.String(), client); err != nil {
					log.Info("contentBuilder.Len()修改失败2")
					s.logger.Errorf("更新卡片失败2：%s", err.Error())
				}
			}
			lastUpdate = time.Now()

		case <-heartbeatTicker.C:
			if time.Now().Unix()-lastUpdate.Unix() >= HeardBeatX {
				log.Infof("心跳超时,当前时间：%d,最后时间：%d", time.Now().Unix(), lastUpdate.Unix())
				return
			}

		case <-ctx.Done():
			log.Info("send_card上下文失效")
			s.logger.Info("context canceled, stop channel processing")
			return
		}
	}

}

// processContentSlice 处理切片内容（同步更新）
func (s *SendCardClient) processContentSlice(ctx context.Context, cardSend *CardSend, cardInstanceId string, client *dingtalkim_1_0.Client) error {
	var contentBuilder strings.Builder
	for _, content := range cardSend.ContentSlice {

		contentBuilder.WriteString(content)
		err := s.updateCardRequest(ctx, &UpdateCardRequest{
			Template:       string(cardSend.Template),
			Content:        contentBuilder.String(),
			Client:         client,
			RobotCode:      cardSend.RobotCode,
			CardInstanceId: cardInstanceId,
		})
		if err != nil {
			return fmt.Errorf("更新卡片失败: %w", err)
		}
		time.Sleep(cardSend.UpdateInterval) // 控制更新频率
	}
	return nil
}

// updateCardContent 封装卡片更新逻辑
func (s *SendCardClient) updateCardContent(ctx context.Context, cardSend *CardSend, cardInstanceId, content string, client *dingtalkim_1_0.Client) error {
	err := s.updateCardRequest(ctx, &UpdateCardRequest{
		Template:       string(cardSend.Template),
		Content:        content,
		Client:         client,
		RobotCode:      cardSend.RobotCode,
		CardInstanceId: cardInstanceId,
	})

	return err
}

func (s *SendCardClient) updateCardRequest(ctx context.Context, updateCardRequest *UpdateCardRequest) error {
	content, err := pkg.SafeReplace(updateCardRequest.Template, "%s", updateCardRequest.Content)
	if err != nil {
		return err
	}
	updateRequest := &dingtalkim_1_0.UpdateRobotInteractiveCardRequest{
		CardBizId: tea.String(updateCardRequest.CardInstanceId),
		CardData:  tea.String(content),
	}
	_, err = s.UpdateInteractiveCard(ctx, updateRequest, updateCardRequest.RobotCode, updateCardRequest.Client)
	return err
}

// UpdateInteractiveCard 更新交互卡片（封装错误处理）
func (s *SendCardClient) UpdateInteractiveCard(ctx context.Context, request *dingtalkim_1_0.UpdateRobotInteractiveCardRequest, robotCode string, client *dingtalkim_1_0.Client) (*dingtalkim_1_0.UpdateRobotInteractiveCardResponse, error) {
	authInfo, err := s.Auth.GetTokenFromBotOption(ctx, WithBot(s.botOption))
	if err != nil {
		return nil, fmt.Errorf("get token failed: %w", err)
	}

	headers := &dingtalkim_1_0.UpdateRobotInteractiveCardHeaders{
		XAcsDingtalkAccessToken: tea.String(authInfo.AccessToken),
	}

	response, err := client.UpdateRobotInteractiveCardWithOptions(request, headers, &util.RuntimeOptions{})
	if err != nil {
		return nil, fmt.Errorf("API call failed: %w,request:%v", err, request.String())
	}
	return response, nil
}

// SendInteractiveCard 发送交互卡片（封装错误处理）
func (s *SendCardClient) SendInteractiveCard(ctx context.Context, request *dingtalkim_1_0.SendRobotInteractiveCardRequest, robotCode string, client *dingtalkim_1_0.Client) (res *dingtalkim_1_0.SendRobotInteractiveCardResponse, err error) {
	err = s.Auth.GetBotConfigFromModel(s.botOption)
	if err != nil {
		return nil, fmt.Errorf("初始化bot失败: %w", err)
	}
	authInfo, err := s.Auth.GetTokenFromBotOption(ctx, WithBot(s.botOption))
	if err != nil {
		return nil, fmt.Errorf("get token failed: %w", err)
	}

	headers := &dingtalkim_1_0.SendRobotInteractiveCardHeaders{
		XAcsDingtalkAccessToken: tea.String(authInfo.AccessToken),
	}

	response, err := client.SendRobotInteractiveCardWithOptions(request, headers, &util.RuntimeOptions{})
	if err != nil {
		return nil, fmt.Errorf("API call failed: %w", err)
	}
	return response, nil
}
