package service

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"math"
	"math/rand"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"time"

	"SSO/models"
	"SSO/utils"

	"github.com/astaxie/beego"
	"github.com/jinzhu/gorm"
)

type SMSServiceError struct {
	Err string
}

func (e *SMSServiceError) Error() string {
	return e.Err
}

// 有如下两个外部可调用的接口：SendSMSUseTemplate、SendSMSWithCustomContent两个函数
// 但是实际上我打算将 SendSMSUseTemplate 作为底层函数使用，不直接提供外界:
// 这需要提供数个默认模板，通过为这几个模板创建独立便利函数，封装 SendSMSUseTemplate 的参数 defaultTemplateID 和 params

// 发送验证码短信
// 参数 aespass 是加密后的地址信息，用于限制频繁调用
func SendVerificationCodeSMS(mobile string, aespass string) error {
	if len(mobile) == 0 {
		return &SMSServiceError{Err: "手机号为空"}
	}
	if err := checkVerificationCodeSMSLimit(aespass, mobile); err != nil {
		return err
	}

	var code_str string
	for i := 0; i < 6; i++ {
		rand.Seed(time.Now().UnixNano())
		code_str += strconv.Itoa(rand.Intn(10))
	}
	templateID, _ := beego.AppConfig.Int("sms_verification_code_templateid")
	utils.TraceLog("验证码为%v", code_str)
	_, _, err := batchSendMessage(templateID, []string{code_str}, []string{mobile})
	if err == nil {
		redisClient := RedisClient()
		defer redisClient.Close()
		cur_date := time.Now().Format("2006-01-02")
		redisClient.Set("code_msg_"+mobile, code_str, time.Minute*10)
		redisClient.Incr("code_msg_" + mobile + "_" + cur_date).Result()
		// 取出地址信息，因为上面已经验证过，这里就直接解密而不做错误判断了
		bytesPass, _ := base64.StdEncoding.DecodeString(aespass)
		tpass := utils.AESDecrypt(bytesPass)
		redisClient.Incr("ip:host_" + cur_date + "_" + tpass).Result()
	}
	return err
}

func checkVerificationCodeSMSLimit(aespass string, mobile string) error {
	redisClient := RedisClient()
	defer redisClient.Close()
	bytesPass, err := base64.StdEncoding.DecodeString(aespass)
	if err != nil {
		return &SMSServiceError{Err: "缺少关键参数"}
	}
	tpass := utils.AESDecrypt(bytesPass)
	if len(tpass) == 0 {
		return &SMSServiceError{Err: "缺少关键参数"}
	}

	cur_date := time.Now().Format("2006-01-02")
	add_redis, err := redisClient.Get("ip:host_" + cur_date + "_" + tpass).Result()
	if err != nil {
		return &SMSServiceError{Err: "缺少关键参数"}
	}
	ip_max_send_count, _ := beego.AppConfig.Int("ip_max_send_count")
	if add_count, _ := strconv.Atoi(add_redis); add_count >= ip_max_send_count {
		return &SMSServiceError{Err: "当前IP发送短信超过限制"}
	}

	moblie_count, _ := redisClient.Get("code_msg_" + mobile + "_" + cur_date).Result()
	moblie_count_int, _ := strconv.Atoi(moblie_count)
	if moblie_max, _ := beego.AppConfig.Int("moblie_max_send_count"); moblie_count_int >= moblie_max {
		return &SMSServiceError{Err: "当前手机号发送短信超过限制"}
	}

	return nil
}

// 指定模板群发短信
func SendSMSUseTemplate(orgID int, orgShortName string, defaultTemplateID int, params []string, mobiles []string) error {

	if len(mobiles) == 0 {
		return nil
	}

	defaultTemplate, getDefaultTemplateErr := getDefaultTemplate(defaultTemplateID)
	if getDefaultTemplateErr != nil {
		utils.WarningLog("获取默认模板信息失败: %v", getDefaultTemplateErr)
		return getDefaultTemplateErr
	}
	smsContent, matchErr := getSMSFullContent(defaultTemplate, orgShortName, params)
	if matchErr != nil {
		return matchErr
	}
	if len(smsContent) > 500 {
		return &SMSServiceError{Err: "短信太长"}
	}

	// 若发送，是否会超过短信额度
	willConsumeLimit := getWillConsumeLimit(smsContent, len(mobiles))
	if isSMSCountOverLimit(orgID, willConsumeLimit) {
		utils.WarningLog("短信额度不足")
		return &SMSServiceError{"短信额度不足"}
	}

	sendTime := time.Now()

	batch := &models.SMSBatch{
		OrgId:       orgID,
		Autograph:   orgShortName,
		Params:      strings.Join(params, ","),
		FullContent: smsContent,
		Mobiles:     strings.Join(mobiles, ","),
		SendTime:    sendTime.Unix(),
		Status:      models.SMSBatchStatusUnsend,
		CreateTime:  sendTime.Unix(),
		ModifyTime:  sendTime.Unix(),
	}

	template, getTemplateErr := getSMSTemplateID(orgID, orgShortName, defaultTemplateID)
	if getTemplateErr != nil {
		return getTemplateErr
	}
	if template.TemplateId != 0 {
		if template.Status == 0 {
			return &SMSServiceError{Err: "短信模板无效"}
		}
		// 扣除短信额度
		if updateLimitErr := updateLimit(orgID, sendTime, willConsumeLimit); updateLimitErr != nil {
			return updateLimitErr
		}
		// 插入“未发送”状态的短信批次记录
		batch.TemplateId = template.TemplateId
		saveSMSBatch(batch, models.SMSBatchStatusUnsend)
		// 发送短信
		sendCount, report, sendErr := batchSendMessage(template.TemplateId, params, mobiles)
		// 根据发送结果,修改短信批次记录的状态,调整扣除额度
		if sendErr == nil { // 发送成功
			utils.InfoLog("发送了短信数量: %v", sendCount)
			// 修改短信批次记录的状态为“已发送”
			saveSMSBatch(batch, models.SMSBatchStatusDidSend)
			// 返回发送失败部分的额度
			if sendCount < willConsumeLimit {
				if updateLimitErr := updateLimit(orgID, sendTime, sendCount-willConsumeLimit); updateLimitErr != nil {
					// 记录更新失败的操作
				}
			}
			// 插入发送状态
			if batch.Id != 0 {
				saveSMSSendReport(batch.Id, report)
			} else {
				utils.ErrorLog("因为短信批次插入失败，导致无法插入发送状态，report: %v", report)
			}
			return nil

		} else { // 发送失败
			utils.ErrorLog("发送短信失败: %v", sendErr)
			// 修改短信批次记录的状态为“发送失败”
			saveSMSBatch(batch, models.SMSBatchStatusSendFailed)
			// 返回扣除的额度
			if updateLimitErr := updateLimit(orgID, sendTime, -willConsumeLimit); updateLimitErr != nil {
				// 记录更新失败的操作
			}
			return sendErr
		}

	} else {
		// 扣除短信额度
		if updateLimitErr := updateLimit(orgID, sendTime, willConsumeLimit); updateLimitErr != nil {
			return updateLimitErr
		}
		// 创建模板 成功：保存模板ID; 失败：需要返回扣除的额度
		if newTemplateID, createErr := createTemplateToSMSPlatform(orgShortName, defaultTemplate.Content, defaultTemplate.Type); createErr == nil { // 创建成功
			// 插入“待审核”状态的短信批次记录
			batch.TemplateId = newTemplateID
			saveSMSBatch(batch, models.SMSBatchStatusInReview)
			// 保存模板ID
			if saveErr := setSMSTemplateIDFor(orgID, orgShortName, defaultTemplateID, newTemplateID); saveErr != nil {
				utils.ErrorLog("保存模板ID失败: %v", saveErr)
				return saveErr
			} else {
				// 等待短信平台对模板的审核回调
				utils.TraceLog("等待短信平台对模板的审核回调")
				return nil
			}

		} else { // 创建失败
			utils.ErrorLog("短信平台创建模板失败: %v", createErr)
			// 需要返回扣除的额度
			if updateLimitErr := updateLimit(orgID, sendTime, -willConsumeLimit); updateLimitErr != nil {
				// 记录更新失败的操作
			}
			return createErr
		}
	}
}

// 发送自定义内容短信，content 不需要包括“退订请回复TD”字样
func SendSMSWithCustomContent(orgID int, orgShortName string, content string, mobiles []string) error {
	if len(mobiles) == 0 {
		return nil
	}

	content = fmt.Sprintf("%v退订请回复TD", content)
	fullContent := fmt.Sprintf("【%v】%v", orgShortName, content)
	if len(fullContent) > 500 {
		return &SMSServiceError{Err: "短信太长"}
	}

	// 若发送，是否会超过短信额度
	willConsumeLimit := getWillConsumeLimit(fullContent, len(mobiles))
	if isSMSCountOverLimit(orgID, willConsumeLimit) {
		utils.WarningLog("短信额度不足")
		return &SMSServiceError{"短信额度不足"}
	}

	sendTime := time.Now()

	batch := &models.SMSBatch{
		OrgId:       orgID,
		Autograph:   orgShortName,
		Params:      "",
		FullContent: fullContent,
		Mobiles:     strings.Join(mobiles, ","),
		SendTime:    sendTime.Unix(),
		Status:      models.SMSBatchStatusUnsend,
		CreateTime:  sendTime.Unix(),
		ModifyTime:  sendTime.Unix(),
	}

	// 扣除短信额度
	if updateLimitErr := updateLimit(orgID, sendTime, willConsumeLimit); updateLimitErr != nil {
		return updateLimitErr
	}
	// 创建模板 成功：保存模板ID; 失败：需要返回扣除的额度
	if newTemplateID, createErr := createTemplateToSMSPlatform(orgShortName, content, 5); createErr == nil { // 创建成功
		// 插入“待审核”状态的短信批次记录
		batch.TemplateId = newTemplateID
		saveSMSBatch(batch, models.SMSBatchStatusInReview)
		// 保存模板ID
		if saveErr := setSMSTemplateIDFor(orgID, orgShortName, 0, newTemplateID); saveErr != nil {
			utils.ErrorLog("保存模板ID失败: %v", saveErr)
			return saveErr
		} else {
			// 等待短信平台对模板的审核回调
			utils.TraceLog("等待短信平台对模板的审核回调")
			return nil
		}

	} else { // 创建失败
		utils.ErrorLog("短信平台创建模板失败: %v", createErr)
		// 需要返回扣除的额度
		if updateLimitErr := updateLimit(orgID, sendTime, -willConsumeLimit); updateLimitErr != nil {
			// 记录更新失败的操作
		}
		return createErr
	}
}

// 短信批次重发
func ResendSMSBatch(templateID int) error {
	batchs, getBatchErr := getInReviewSMSBatchWithTemplateID(templateID)
	if getBatchErr != nil {
		if getBatchErr != gorm.ErrRecordNotFound {
			utils.ErrorLog("获取短信批次失败: %v", getBatchErr)
			return getBatchErr
		} else {
			return nil
		}
	}

	for _, batch := range batchs {
		params := strings.Split(batch.Params, ",")
		mobiles := strings.Split(batch.Mobiles, ",")

		// 若发送，是否会超过短信额度
		willConsumeLimit := getWillConsumeLimit(batch.FullContent, len(mobiles))
		// 更新批次状态为"未发送"
		saveSMSBatch(batch, models.SMSBatchStatusUnsend)
		// 发送短信
		sendCount, report, sendErr := batchSendMessage(templateID, params, mobiles)
		// 根据发送结果,修改短信批次记录的状态,调整扣除额度
		if sendErr == nil { // 发送成功
			utils.InfoLog("发送了短信数量: %v", sendCount)
			// 修改短信批次记录的状态为“已发送”
			saveSMSBatch(batch, models.SMSBatchStatusDidSend)
			// 返回发送失败部分的额度
			if sendCount < willConsumeLimit {
				if updateLimitErr := updateLimit(batch.OrgId, time.Unix(batch.SendTime, 0), sendCount-willConsumeLimit); updateLimitErr != nil {
					// 记录更新失败的操作
				}
			}
			// 插入发送状态
			saveSMSSendReport(batch.Id, report)
			return nil

		} else { // 发送失败
			utils.ErrorLog("发送短信失败: %v", sendErr)
			// 修改短信批次记录的状态为“发送失败”
			saveSMSBatch(batch, models.SMSBatchStatusSendFailed)
			// 返回扣除的额度
			if updateLimitErr := updateLimit(batch.OrgId, time.Unix(batch.SendTime, 0), -willConsumeLimit); updateLimitErr != nil {
				// 记录更新失败的操作
			}
		}
	}
	return nil
}

func GetAllDefaultTemplates() []*models.SMSDefaultTemplate {
	var templates []*models.SMSDefaultTemplate
	err := readDb.Find(&templates).Error
	if err != nil {
		return make([]*models.SMSDefaultTemplate, 0)
	}
	return templates
}

// 更新"sgj_patient_sms_template_id"模板状态为无效
func DisableTemplate(templateID int) error {
	tx := writeDb.Begin()
	if err := tx.Exec("UPDATE sgj_patient_sms_template_id SET status = ?, mtime = ? Where sms_template_id = ?;", 0, time.Now().Unix(), templateID).Error; err != nil {
		utils.ErrorLog("无效化模板失败: templateID: %v", templateID)
		tx.Rollback()
		// 记录更新失败的操作
	}
	return tx.Commit().Error
}

// 更新批次状态为”审核未通过“
func SetSMSBatchUnapprovedWithTemplateID(templateID int) error {
	tx := writeDb.Begin()
	if err := tx.Exec("UPDATE sgj_patient_sms_batch SET status = ?, mtime = ? Where sms_template_id = ?;", models.SMSBatchStatusUnapproved, time.Now().Unix(), templateID).Error; err != nil {
		utils.ErrorLog("更新短信批次状态为”审核未通过“失败: templateID: %v", templateID)
		tx.Rollback()
		// 记录更新失败的操作
	}
	return tx.Commit().Error
}

// private methods
// 获取数据库中 orgID & orgShortName & defaultTemplateID 对应的短信平台上的模板 ID，如果没有，会返回 0
func getSMSTemplateID(orgID int, orgShortName string, defaultTemplateID int) (*models.SMSTemplateID, error) {
	var template models.SMSTemplateID
	err := readDb.Where("org_id = ? AND org_short_name = ? AND custom_template_id = ?", orgID, orgShortName, defaultTemplateID).First(&template).Error
	if err != nil {
		utils.WarningLog("getSMSTemplateID err: %v", err)
		return nil, err
	} else {
		return &template, nil
	}
}

// 将获取到的短信平台上的模板 ID插入数据库
func setSMSTemplateIDFor(orgID int, orgShortName string, defaultTemplateID int, templateID int) error {
	tx := writeDb.Begin()
	now := time.Now().Unix()
	template := &models.SMSTemplateID{
		OrgId:            orgID,
		OrgShortName:     orgShortName,
		CustomTemplateId: defaultTemplateID,
		TemplateId:       templateID,
		Status:           1,
		CreateTime:       now,
		ModifyTime:       now,
	}
	if err := tx.Create(template).Error; err != nil {
		tx.Rollback()
	}
	return tx.Commit().Error
}

// 获取默认模板
func getDefaultTemplate(defaultTemplateID int) (*models.SMSDefaultTemplate, error) {
	var template models.SMSDefaultTemplate
	err := readDb.Where("id = ?", defaultTemplateID).First(&template).Error
	if err != nil {
		return nil, err
	} else {
		return &template, nil
	}
}

func getDefaultTemplateIDWithSMSTemplateID(smsTemplateID int) (int, error) {
	var template models.SMSTemplateID
	err := readDb.Where("sms_template_id = ?", smsTemplateID).First(template).Error
	if err != nil {
		return 0, err
	} else {
		return template.CustomTemplateId, nil
	}
}

// 判断短信参数是否含有非法参数
func isSMSParamValid(params []string) bool {
	if params == nil {
		return true
	}
	for _, param := range params {
		if strings.Contains(param, ",") || strings.Contains(param, "】") || strings.Contains(param, "【") {
			return false
		}
	}
	return true
}

// 计算即将消耗的额度
func getWillConsumeLimit(smsContent string, mobileCount int) int {
	limitEachMessage := math.Ceil(float64(len(smsContent)) / 67.0)
	return int(limitEachMessage) * mobileCount
}

// 拼接短信模板和参数为完整短信内容
func getSMSFullContent(template *models.SMSDefaultTemplate, autograph string, params []string) (string, error) {
	reg, _ := regexp.Compile("{\\d}")
	placeholders := reg.FindAllString(template.Content, -1)
	if len(placeholders) != len(params) {
		utils.WarningLog("短信模板和参数不匹配")
		return template.Content, &SMSServiceError{"短信模板和参数不匹配"}
	}
	tplContent := template.Content
	for index, placeholder := range placeholders {
		tplContent = strings.Replace(tplContent, placeholder, params[index], 1)
	}
	smsContent := fmt.Sprintf("【%v】%v", autograph, tplContent)
	return smsContent, nil
}

// 向短信平台创建模板，返回新模板的 ID
// autograph: 签名，即为机构简称
// content: 模板内容
// type_: 短信类型：0:通知短信、5:会员服务短信、4:验证码短信
func createTemplateToSMSPlatform(autograph string, content string, type_ int) (int, error) {
	sms_api := beego.AppConfig.String("sms_baseUrl") + "addsmstemplate"
	appID, sid, token := getSMSConfig()
	params := make(map[string]interface{})
	params["appid"] = appID
	params["sid"] = sid
	params["token"] = token
	params["type"] = type_
	params["autograph"] = autograph
	params["content"] = content

	paramsBytes, _ := json.Marshal(params)
	resp, requestErr := http.Post(sms_api, "application/json", bytes.NewBuffer(paramsBytes))

	if requestErr != nil {
		utils.ErrorLog("短信平台增加模板接口调用失败: %v", requestErr)
		return 0, requestErr
	}
	defer resp.Body.Close()
	body, ioErr := ioutil.ReadAll(resp.Body)
	if ioErr != nil {
		utils.ErrorLog("短信平台增加模板接口返回数据读取失败: %v", ioErr)
		return 0, ioErr
	}
	var respJSON map[string]interface{}
	utils.InfoLog(string(body))
	if err := json.Unmarshal([]byte(string(body)), &respJSON); err != nil {
		utils.ErrorLog("短信平台增加模板接口返回数据解析JSON失败: %v", err)
		return 0, err
	}
	if respJSON["code"].(string) != "000000" {
		msg := respJSON["msg"].(string)
		utils.ErrorLog("短信平台增加模板接口请求失败: %v", msg)
		return 0, &SMSServiceError{"短信平台增加模板接口请求失败"}
	}

	templateID, _ := strconv.Atoi(respJSON["templateid"].(string))
	utils.SuccessLog("新短信模板 ID: %v", templateID)
	return templateID, nil
}

// 指定模板群发短信
// 返回值为发送了 n 条短信、短信平台返回的 report 数组[{"code":"0", "msg":"OK", "smsid":"f96f79240e372587e9284cd580d8f953", "mobile":"18011984299", "count":"1"}]
func batchSendMessage(templateID int, params []string, mobiles []string) (int, []interface{}, error) {
	sms_api := beego.AppConfig.String("sms_baseUrl") + "sendsms"
	mobileStr := strings.Join(mobiles, ",")
	appID, sid, token := getSMSConfig()
	requestParams := make(map[string]interface{})
	requestParams["appid"] = appID
	requestParams["sid"] = sid
	requestParams["token"] = token
	requestParams["templateid"] = strconv.Itoa(templateID)
	requestParams["mobile"] = mobileStr
	if params != nil && len(params) != 0 {
		paramStr := strings.Join(params, ",")
		requestParams["param"] = paramStr
	}

	paramsBytes, _ := json.Marshal(requestParams)
	resp, requestErr := http.Post(sms_api, "application/json", bytes.NewBuffer(paramsBytes))

	if requestErr != nil {
		utils.ErrorLog("短信平台模板群发接口调用失败: %v", requestErr)
		return 0, nil, requestErr
	}
	defer resp.Body.Close()
	body, ioErr := ioutil.ReadAll(resp.Body)
	if ioErr != nil {
		utils.ErrorLog("短信平台模板群发接口返回数据读取失败: %v", ioErr)
		return 0, nil, ioErr
	}
	var respJSON map[string]interface{}
	utils.InfoLog(string(body))
	if err := json.Unmarshal([]byte(string(body)), &respJSON); err != nil {
		utils.ErrorLog("短信平台模板群发接口返回数据解析JSON失败: %v", err)
		return 0, nil, err
	}
	if respJSON["code"].(string) != "000000" {
		msg := respJSON["msg"].(string)
		utils.ErrorLog("短信平台模板群发接口请求失败: %v", msg)
		return 0, nil, &SMSServiceError{"短信平台模板群发接口请求失败"}

	} else {
		utils.SuccessLog("短信发送成功 report: %v", respJSON["report"])
		if len(mobiles) > 1 {
			count, _ := strconv.Atoi(respJSON["count_sum"].(string))
			return count, respJSON["report"].([]interface{}), nil
		} else {
			return 1, nil, nil
		}
	}
}

// 判断短信额度是否充足
func isSMSCountOverLimit(orgID int, willSendCount int) bool {
	freeLimit, getFreeErr := getUserSMSFreeLimit(orgID, time.Now())
	if getFreeErr != nil {
		return true
	}
	if willSendCount+freeLimit.UsedCount <= freeLimit.TotalCount {
		// 免费额度充足
		return false
	}
	// 免费额度不足时，需要加上已购额度来判断
	buyLimit, getBuyErr := getUserSMSBuyLimit(orgID)
	if getBuyErr != nil {
		return true
	}
	if buyLimit.UsedCount+freeLimit.UsedCount+willSendCount <= freeLimit.TotalCount+buyLimit.TotalCount {
		// 可用额度充足
		return false
	} else {
		return true
	}
}

// 获取短信平台信息
func getSMSConfig() (string, string, string) {
	return beego.AppConfig.String("sms_appId"),
		beego.AppConfig.String("sms_sid"),
		beego.AppConfig.String("sms_token")
}

// 获取用户的免费短信额度
func getUserSMSFreeLimit(orgID int, date time.Time) (*models.UserSMSFreeLimit, error) {
	month := date.Format("200601")
	var freeLimit models.UserSMSFreeLimit
	if readErr := readDb.Where("org_id = ? AND valid_month = ?", orgID, month).First(&freeLimit).Error; gorm.IsRecordNotFoundError(readErr) == true {
		// 创建
		now := time.Now().Unix()
		freeLimit = models.UserSMSFreeLimit{
			OrgId:      orgID,
			TotalCount: 100,
			UsedCount:  0,
			ValidMonth: month,
			Status:     1,
			CreateTime: now,
			ModifyTime: now,
		}
		tx := writeDb.Begin()
		if createErr := tx.Create(&freeLimit).Error; createErr != nil {
			tx.Rollback()
			utils.ErrorLog("用户短信免费额度创建失败: %v", createErr)
			tx.Commit()
			return nil, createErr
		} else {
			tx.Commit()
			return &freeLimit, nil
		}

	} else if readErr != nil {
		utils.ErrorLog("获取用户短信免费额度信息失败: %v", readErr)
		return nil, readErr

	} else {
		return &freeLimit, nil
	}
}

// 获取用户的已购短信额度
func getUserSMSBuyLimit(orgID int) (*models.UserSMSBuyLimit, error) {
	var buyLimit models.UserSMSBuyLimit
	if readErr := readDb.Where("org_id = ?", orgID).First(&buyLimit).Error; gorm.IsRecordNotFoundError(readErr) == true {
		// 创建
		now := time.Now().Unix()
		buyLimit = models.UserSMSBuyLimit{
			OrgId:      orgID,
			TotalCount: 0,
			UsedCount:  0,
			Status:     1,
			CreateTime: now,
			ModifyTime: now,
		}
		tx := writeDb.Begin()
		if createErr := tx.Create(&buyLimit).Error; createErr != nil {
			tx.Rollback()
			utils.ErrorLog("用户短信已购额度创建失败: %v", createErr)
			tx.Commit()
			return nil, createErr
		} else {
			tx.Commit()
			return &buyLimit, nil
		}

	} else if readErr != nil {
		utils.ErrorLog("获取用户短信已购额度信息失败: %v", readErr)
		return nil, readErr

	} else {
		return &buyLimit, nil
	}
}

// 更新用户短信额度
func updateLimit(orgID int, date time.Time, consumeLimit int) error {
	freeLimit, getFreeLimitErr := getUserSMSFreeLimit(orgID, date)
	if getFreeLimitErr != nil {
		return getFreeLimitErr
	}
	if freeLimit.UsedCount+consumeLimit <= freeLimit.TotalCount {
		freeLimit.UsedCount += consumeLimit
		freeLimit.ModifyTime = time.Now().Unix()
		tx := writeDb.Begin()
		if err := tx.Save(freeLimit).Error; err != nil {
			utils.ErrorLog("更新短信免费额度失败: %v\nOrgID: %v; 额度生效月份: %v; 总额度: %v; 已用额度: %v; 扣完后已用额度应当为: %v;", err, orgID, freeLimit.ValidMonth, freeLimit.TotalCount, freeLimit.UsedCount+consumeLimit, freeLimit.UsedCount)
			tx.Rollback()
		}
		return tx.Commit().Error

	} else {
		buyLimit, getBuyLimitErr := getUserSMSBuyLimit(orgID)
		if getBuyLimitErr != nil {
			return getBuyLimitErr
		}
		consumeFreeLimit := freeLimit.TotalCount - freeLimit.UsedCount
		consumeBuyLimit := consumeLimit - consumeFreeLimit
		now := time.Now().Unix()
		tx := writeDb.Begin()
		freeLimit.UsedCount = freeLimit.TotalCount
		freeLimit.ModifyTime = now
		updateFreeLimitErr := tx.Save(freeLimit)
		if updateFreeLimitErr != nil {
			utils.ErrorLog("更新短信免费额度失败: %v\nOrgID: %v; 额度生效月份: %v; 当前剩余免费额度: %v; 当前剩余已购额度: %v; 当前应当扣去额度: %v;", err, orgID, freeLimit.ValidMonth, freeLimit.UsedCount-consumeFreeLimit, buyLimit.TotalCount-buyLimit.UsedCount, consumeLimit)
			tx.Rollback()

		} else {
			buyLimit.UsedCount += consumeBuyLimit
			buyLimit.ModifyTime = now
			updateBuyLimitErr := tx.Save(buyLimit)
			if updateBuyLimitErr != nil {
				utils.ErrorLog("更新短信已购额度失败: %v\nOrgID: %v; 额度生效月份: %v; 当前剩余免费额度: %v; 当前剩余已购额度: %v; 当前应当扣去额度: %v;", err, orgID, freeLimit.ValidMonth, freeLimit.UsedCount-consumeFreeLimit, buyLimit.TotalCount-buyLimit.UsedCount, consumeLimit)
				tx.Rollback()
			}
		}
		return tx.Commit().Error
	}
}

// 创建/更新 短信批次
func saveSMSBatch(batch *models.SMSBatch, status int8) error {
	batch.Status = status
	batch.ModifyTime = time.Now().Unix()
	if err := _saveSMSBatch(batch); err != nil {
		// 记录更新失败的操作

		return err
	} else {
		return nil
	}
}

func _saveSMSBatch(batch *models.SMSBatch) error {
	tx := writeDb.Begin()
	if err := tx.Save(batch).Error; err != nil {
		tx.Rollback()
	}
	return tx.Commit().Error
}

// 获取短信批次
func getInReviewSMSBatchWithTemplateID(templateID int) ([]*models.SMSBatch, error) {
	var batchs []*models.SMSBatch
	err := readDb.Where("sms_template_id = ? AND status = ?", templateID, models.SMSBatchStatusInReview).Find(&batchs).Error
	if err != nil {
		return nil, err
	} else {
		return batchs, nil
	}
}

// 插入短信发送状态
// report: [{"code":"000000","count":"1","mobile":"13632250447","msg":"OK","smsid":"8a978fe2eb3e69c3e5983b6e1cd33427"}]
func saveSMSSendReport(batchID int, report []interface{}) error {
	if report == nil || len(report) == 0 {
		return nil
	}
	now := time.Now().Unix()
	valueStrs := make([]string, 0, len(report))
	values := make([]interface{}, 0, len(report))
	for _, item := range report {
		json := item.(map[string]interface{})
		valueStrs = append(valueStrs, "(?, ?, ?, ?, ?, ?, ?)")

		values = append(values, batchID)
		values = append(values, json["mobile"])
		if json["code"].(string) == "000000" {
			values = append(values, 1)
		} else {
			values = append(values, 0)
		}
		values = append(values, json["code"])
		values = append(values, json["msg"])
		values = append(values, now)
		values = append(values, now)
	}
	sql := fmt.Sprintf("INSERT INTO sgj_patient_sms_send_status (batch_id, mobile, status, code, msg, ctime, mtime) VALUES %v;", strings.Join(valueStrs, ", "))

	tx := writeDb.Begin()
	if err := tx.Exec(sql, values...).Error; err != nil {
		utils.ErrorLog("插入短信发送状态失败: %v", err)
		tx.Rollback()
		// 记录插入失败的操作
	}
	return tx.Commit().Error
}
