package ding

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"
)

// TalkClient 钉钉消息推送客户端结构体
type TalkClient struct {
	webhookURL string
	secret     string
}

// NewDingTalkClient 创建一个新的钉钉消息推送客户端实例
func NewDingTalkClient(webhookURL, secret string) *TalkClient {
	return &TalkClient{
		webhookURL: webhookURL,
		secret:     secret,
	}
}

// generateSign 生成加签
func (c *TalkClient) generateSign(timestamp string) (string, error) {
	stringToSign := timestamp + "\n" + c.secret
	h := hmac.New(sha256.New, []byte(c.secret))
	_, err := h.Write([]byte(stringToSign))
	if err != nil {
		return "", err
	}
	signData := h.Sum(nil)
	return base64.StdEncoding.EncodeToString(signData), nil
}

// sendRequest 发送 HTTP 请求
func (c *TalkClient) sendRequest(message map[string]interface{}) error {
	timestamp := strconv.FormatInt(time.Now().UnixNano()/1e6, 10)

	fullURL := fmt.Sprintf("%s&timestamp=%s", c.webhookURL, timestamp)

	if len(c.secret) > 0 {
		sign, err := c.generateSign(timestamp)
		if err != nil {
			return err
		}
		fullURL = fmt.Sprintf("%s&sign=%s", fullURL, sign)
	}

	messageJSON, err := json.Marshal(message)
	if err != nil {
		return err
	}

	req, err := http.NewRequest("POST", fullURL, bytes.NewBuffer(messageJSON))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")

	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return err
	}
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("failed to send message, status code: %d, body: %s", resp.StatusCode, string(body))
	}

	return nil
}

// SendTextMessage 发送文本消息并可 @ 人员
func (c *TalkClient) SendTextMessage(content string, atMobiles []string, isAtAll bool) error {
	message := map[string]interface{}{
		"msgtype": "text",
		"text": map[string]string{
			"content": content,
		},
		"at": map[string]interface{}{
			"atMobiles": atMobiles,
			"isAtAll":   isAtAll,
		},
	}
	return c.sendRequest(message)
}

// SendLinkMessage 发送链接消息并可 @ 人员
func (c *TalkClient) SendLinkMessage(title, text, messageURL, picURL string, atMobiles []string, isAtAll bool) error {
	message := map[string]interface{}{
		"msgtype": "link",
		"link": map[string]string{
			"title":      title,
			"text":       text,
			"messageUrl": messageURL,
			"picUrl":     picURL,
		},
		"at": map[string]interface{}{
			"atMobiles": atMobiles,
			"isAtAll":   isAtAll,
		},
	}
	return c.sendRequest(message)
}

// SendMarkdownMessage 发送 Markdown 消息并可 @ 人员
func (c *TalkClient) SendMarkdownMessage(title, text string, atMobiles []string, isAtAll bool) error {

	if len(atMobiles) > 0 {
		var atStr string
		for _, mobile := range atMobiles {
			atStr += fmt.Sprintf("<font color='red'>@%s</font>", mobile)
		}
		text += fmt.Sprintf("\n<font color='black'>请尽快处理</font>%s", atStr)
	}

	message := map[string]interface{}{
		"msgtype": "markdown",
		"markdown": map[string]string{
			"title": title,
			"text":  text,
		},
		"at": map[string]interface{}{
			"atMobiles": atMobiles,
			"isAtAll":   isAtAll,
		},
	}
	return c.sendRequest(message)
}

// SendJSONAsMarkdown 发送JSON数据（转换为Markdown格式）
func (c *TalkClient) SendJSONAsMarkdown(data interface{}, title string) error {
	// 将JSON数据转换为格式化的字符串
	jsonBytes, err := json.MarshalIndent(data, "", "  ")
	if err != nil {
		return fmt.Errorf("序列化JSON失败: %w", err)
	}

	// 构建Markdown内容（使用代码块格式）
	markdownText := fmt.Sprintf("### %s\n\n```json\n%s\n```", title, string(jsonBytes))

	// 发送Markdown消息
	return c.SendMarkdownMessage(title, markdownText, nil, false)
}
