package sms

import (
	"context"
	"crypto/md5"
	"encoding/hex"
	"fmt"
	"time"

	"github.com/bytedance/sonic"
	"go.uber.org/zap"
)

// Client 短信客户端
type Client struct {
	gatewayURL string        // 短信网关地址
	username   string        // 账号用户名
	password   string        // 账号密码
	signature  string        // 短信签名
	timeout    time.Duration // 请求超时时间
	logger     *zap.Logger   // 日志记录器
	httpClient HTTPClient    // HTTP 客户端接口
}

// HTTPClient HTTP 客户端接口（便于测试）
type HTTPClient interface {
	Post(ctx context.Context, url string, body []byte) ([]byte, error)
}

// NewClient 创建短信客户端
func NewClient(gatewayURL, username, password, signature string, timeout time.Duration, logger *zap.Logger, httpClient HTTPClient) *Client {
	return &Client{
		gatewayURL: gatewayURL,
		username:   username,
		password:   password,
		signature:  signature,
		timeout:    timeout,
		logger:     logger,
		httpClient: httpClient,
	}
}

// SendMessage 发送短信
// content: 短信内容（不包含签名，签名会自动添加）
// phones: 接收手机号列表
func (c *Client) SendMessage(ctx context.Context, content string, phones []string) (*SendResponse, error) {
	// 生成时间戳（毫秒）
	timestamp := time.Now().UnixMilli()

	// 计算签名
	sign := c.calculateSign(timestamp)

	// 构造完整的短信内容（添加签名）
	fullContent := c.signature + content

	// 构造请求
	req := &SendRequest{
		UserName:  c.username,
		Content:   fullContent,
		PhoneList: phones,
		Timestamp: timestamp,
		Sign:      sign,
	}

	// 序列化请求
	reqBody, err := sonic.Marshal(req)
	if err != nil {
		c.logger.Error("序列化短信请求失败",
			zap.Error(err),
			zap.Strings("phones", phones),
		)
		return nil, fmt.Errorf("序列化短信请求失败: %w", err)
	}

	// 记录请求日志（脱敏处理）
	c.logger.Info("发送短信请求",
		zap.Strings("phones", phones),
		zap.String("content_preview", c.truncateContent(content)),
		zap.Int64("timestamp", timestamp),
	)

	// 发送请求
	url := c.gatewayURL + "/sms/api/sendMessageMass"

	// 创建带超时的上下文
	reqCtx, cancel := context.WithTimeout(ctx, c.timeout)
	defer cancel()

	respBody, err := c.httpClient.Post(reqCtx, url, reqBody)
	if err != nil {
		c.logger.Error("发送短信请求失败",
			zap.Error(err),
			zap.String("url", url),
			zap.Strings("phones", phones),
		)
		return nil, fmt.Errorf("发送短信请求失败: %w", err)
	}

	// 解析响应
	var resp SendResponse
	if err := sonic.Unmarshal(respBody, &resp); err != nil {
		c.logger.Error("解析短信响应失败",
			zap.Error(err),
			zap.ByteString("response_body", respBody),
		)
		return nil, fmt.Errorf("解析短信响应失败: %w", err)
	}

	// 记录响应日志
	if resp.Code == CodeSuccess {
		c.logger.Info("短信发送成功",
			zap.Int64("msg_id", resp.MsgID),
			zap.Int("sms_count", resp.SMSCount),
			zap.Strings("phones", phones),
		)
	} else {
		c.logger.Warn("短信发送失败",
			zap.Int("code", resp.Code),
			zap.String("message", resp.Message),
			zap.Strings("phones", phones),
		)
	}

	// 检查响应状态
	if resp.Code != CodeSuccess {
		return &resp, NewSMSError(resp.Code, resp.Message)
	}

	return &resp, nil
}

// calculateSign 计算签名
// 规则: MD5(userName + timestamp + MD5(password))
func (c *Client) calculateSign(timestamp int64) string {
	// 第一步：计算 MD5(password)
	passwordMD5 := c.md5Hash(c.password)

	// 第二步：组合字符串
	combined := fmt.Sprintf("%s%d%s", c.username, timestamp, passwordMD5)

	// 第三步：计算最终签名
	return c.md5Hash(combined)
}

// md5Hash 计算 MD5 哈希值（小写）
func (c *Client) md5Hash(s string) string {
	h := md5.New()
	h.Write([]byte(s))
	return hex.EncodeToString(h.Sum(nil))
}

// truncateContent 截断内容用于日志记录
func (c *Client) truncateContent(content string) string {
	const maxLen = 50
	runes := []rune(content)
	if len(runes) > maxLen {
		return string(runes[:maxLen]) + "..."
	}
	return content
}
