// 钉钉开放平台 API 封装
package dingtalk

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"sync"
	"time"

	myconfig "com.sclktx/m/v2/internal/pkg/config"

	"github.com/sirupsen/logrus"
)

// Client 钉钉客户端
type Client struct {
	cfg       *myconfig.DingTalkConfig
	httpCl    *http.Client
	token     string
	tokenExp  time.Time
	tokenLock sync.RWMutex
}

// NewClient 创建钉钉客户端
func NewClient(cfg *myconfig.DingTalkConfig) *Client {
	return &Client{
		cfg:    cfg,
		httpCl: &http.Client{Timeout: 10 * time.Second},
	}
}

// tokenResponse 获取 access_token 的响应
type tokenResponse struct {
	AccessToken string `json:"accessToken"`
	ExpireIn    int64  `json:"expireIn"` // 秒
}

// getAccessToken 获取钉钉 access_token（带缓存）
func (c *Client) getAccessToken() (string, error) {
	c.tokenLock.RLock()
	if c.token != "" && time.Now().Before(c.tokenExp) {
		c.tokenLock.RUnlock()
		return c.token, nil
	}
	c.tokenLock.RUnlock()

	c.tokenLock.Lock()
	defer c.tokenLock.Unlock()

	// 双重检查
	if c.token != "" && time.Now().Before(c.tokenExp) {
		return c.token, nil
	}

	body := map[string]string{
		"appKey":    c.cfg.AppKey,
		"appSecret": c.cfg.AppSecret,
	}
	data, _ := json.Marshal(body)

	logrus.Debug("钉钉获取 access_token 开始")
	resp, err := c.httpCl.Post("https://api.dingtalk.com/v1.0/oauth2/accessToken", "application/json", bytes.NewReader(data))
	if err != nil {
		logrus.WithError(err).Error("钉钉获取 access_token HTTP 请求失败")
		return "", fmt.Errorf("dingtalk getAccessToken request failed: %w", err)
	}
	defer resp.Body.Close()

	respBody, _ := io.ReadAll(resp.Body)
	logrus.WithFields(logrus.Fields{
		"status_code": resp.StatusCode,
		"response":    string(respBody),
	}).Debug("钉钉获取 access_token 响应")

	var tr tokenResponse
	if err := json.Unmarshal(respBody, &tr); err != nil {
		logrus.WithError(err).WithField("response", string(respBody)).Error("钉钉获取 access_token 解析失败")
		return "", fmt.Errorf("dingtalk getAccessToken parse failed: %s", string(respBody))
	}
	if tr.AccessToken == "" {
		logrus.WithField("response", string(respBody)).Error("钉钉获取 access_token 返回空")
		return "", fmt.Errorf("dingtalk getAccessToken empty token: %s", string(respBody))
	}

	c.token = tr.AccessToken
	c.tokenExp = time.Now().Add(time.Duration(tr.ExpireIn-60) * time.Second) // 提前 60 秒刷新
	logrus.WithFields(logrus.Fields{
		"expire_in": tr.ExpireIn,
	}).Info("钉钉 access_token 获取成功")
	return c.token, nil
}

// RobotBatchSendReq 批量发送机器人消息请求
type RobotBatchSendReq struct {
	RobotCode string   `json:"robotCode"`
	UserIDs   []string `json:"userIds"`
	MsgKey    string   `json:"msgKey"`
	MsgParam  string   `json:"msgParam"`
}

// BatchSendRobotMessage 批量发送人与机器人会话中机器人消息
// userIDs: 钉钉用户 unionId 列表
// title: 消息标题
// markdown: 消息内容（Markdown 格式）
func (c *Client) BatchSendRobotMessage(userIDs []string, title, markdown string) error {
	if len(userIDs) == 0 {
		logrus.Warn("钉钉 BatchSendRobotMessage 被调用但 userIDs 为空")
		return nil
	}
	if c.cfg.RobotCode == "" {
		logrus.Warn("钉钉 BatchSendRobotMessage 被调用但 RobotCode 为空")
		return nil
	}

	logrus.WithFields(logrus.Fields{
		"user_ids":   userIDs,
		"title":      title,
		"robot_code": c.cfg.RobotCode,
	}).Info("钉钉 BatchSendRobotMessage 开始")

	token, err := c.getAccessToken()
	if err != nil {
		logrus.WithError(err).Error("钉钉 BatchSendRobotMessage 获取 token 失败")
		return err
	}

	msgParam := map[string]string{
		"title": title,
		"text":  markdown,
	}
	msgParamJSON, _ := json.Marshal(msgParam)

	req := RobotBatchSendReq{
		RobotCode: c.cfg.RobotCode,
		UserIDs:   userIDs,
		MsgKey:    "sampleMarkdown",
		MsgParam:  string(msgParamJSON),
	}
	reqBody, _ := json.Marshal(req)

	logrus.WithFields(logrus.Fields{
		"request_body": string(reqBody),
	}).Debug("钉钉 BatchSendRobotMessage 请求体")

	httpReq, err := http.NewRequest("POST",
		"https://api.dingtalk.com/v1.0/robot/oToMessages/batchSend",
		bytes.NewReader(reqBody))
	if err != nil {
		logrus.WithError(err).Error("钉钉 BatchSendRobotMessage 创建请求失败")
		return fmt.Errorf("dingtalk BatchSendRobotMessage create request failed: %w", err)
	}
	httpReq.Header.Set("Content-Type", "application/json")
	httpReq.Header.Set("x-acs-dingtalk-access-token", token)

	resp, err := c.httpCl.Do(httpReq)
	if err != nil {
		logrus.WithError(err).Error("钉钉 BatchSendRobotMessage HTTP 请求失败")
		return fmt.Errorf("dingtalk BatchSendRobotMessage request failed: %w", err)
	}
	defer resp.Body.Close()

	respBody, _ := io.ReadAll(resp.Body)

	logrus.WithFields(logrus.Fields{
		"status_code": resp.StatusCode,
		"response":    string(respBody),
	}).Debug("钉钉 BatchSendRobotMessage 响应")

	// 成功时钉钉返回 200 空 body 或 processQueryKey
	if resp.StatusCode >= 400 {
		errMsg := fmt.Errorf("dingtalk BatchSendRobotMessage failed: status=%d body=%s", resp.StatusCode, string(respBody))
		logrus.WithFields(logrus.Fields{
			"status_code": resp.StatusCode,
			"response":    string(respBody),
		}).Error("钉钉 BatchSendRobotMessage 请求返回错误状态码")
		return errMsg
	}

	logrus.WithFields(logrus.Fields{
		"user_ids": userIDs,
		"title":    title,
		"response": string(respBody),
	}).Info("钉钉机器人消息发送成功")

	return nil
}
