package biz

import (
	"context"
	"encoding/json"
	"fmt"
	"github.com/go-kratos/kratos/v2/log"
	"github.com/wechatpay-apiv3/wechatpay-go/services/cashcoupons"
	"runtime"
	"strconv"
	"strings"
	"time"
	"voucher/internal/biz/bo"
	"voucher/internal/biz/do"
	"voucher/internal/biz/vo"
)

func (this *VoucherBiz) Warning(ctx context.Context, id int32) error {

	product, err := this.ProductRepo.GetById(ctx, id)
	if err != nil {
		return err
	}

	return this.WarningBudget(ctx, product)
}

func (this *VoucherBiz) WarningBudgetIncr(ctx context.Context, key string, ttl time.Duration) (int64, error) {

	// 增加发送计数
	count, err := this.rdb.Rdb.IncrBy(ctx, key, 1).Result()
	if err != nil {
		return 0, err
	}

	// 如果是第一次发送，设置 过期时间
	if count == 1 {
		if err = this.rdb.Rdb.Expire(ctx, key, ttl).Err(); err != nil {
			return 0, fmt.Errorf("设置过期时间失败: %v", err)
		}
	}

	// 如果发送次数超过 “指定” 条，清除再来
	if count > 24 { // 大约2小时
		return 0, this.WarningBudgetIncrDel(ctx, key)
	}

	return count, nil
}

func (this *VoucherBiz) WarningBudgetIncrDel(ctx context.Context, key string) error {

	// 检查键是否存在
	exists, err := this.rdb.Rdb.Exists(ctx, key).Result()
	if err != nil {
		return fmt.Errorf("检查键存在性失败: %w", err)
	}

	// 如果键不存在，直接返回成功
	if exists == 0 {
		return nil
	}

	if _, err = this.rdb.Rdb.Del(ctx, key).Result(); err != nil {
		return err
	}

	return nil
}

func (this *VoucherBiz) CronWarningBudget(ctx context.Context) {

	uid := "warningBudget"

	if b := this.Get(uid); b {
		log.Warn("预警查询,上波还未执行完毕，此次暂不执行")
		return
	}

	this.Add(uid)
	defer this.Remove(uid)

	start := time.Now()
	log.Warnf("预警查询,执行开始: %s", start.Format(time.DateTime))

	if err := this.cronWarningBudget(ctx); err != nil {
		log.Errorf("预警查询,执行失败: %s", err)
	}

	end := time.Now()
	elapsed := end.Sub(start)
	log.Warnf("预警查询,开始执行时间%s,执行结束时间%s,代码块执行耗时: %s", start.Format(time.DateTime), end.Format(time.DateTime), elapsed)

	return
}

func (this *VoucherBiz) cronWarningBudget(ctx context.Context) error {

	return this.ProductRepo.FindWarningBudget(ctx, func(ctx context.Context, rows []*bo.ProductBo) error {

		for _, row := range rows {

			if err := this.WarningBudget(ctx, row); err != nil {
				log.Context(ctx).Errorf("预警查询,处理失败: %s", err)
			}
			time.Sleep(time.Second * 2)
		}

		return nil
	})
}

func (this *VoucherBiz) WarningBudget(ctx context.Context, product *bo.ProductBo) (wErr error) {

	defer func() {
		if err := recover(); err != nil {
			_, file, line, _ := runtime.Caller(1) // 1 表示获取当前调用者的调用信息
			log.Errorf("预警查询,发生错误:req:%s,err:%v,file:%s,line:%d", product.BatchNo, err, file, line)
			wErr = fmt.Errorf("预警查询,panic: %v", err)
		}
	}()

	if product.WarningBudget == 0 {
		return fmt.Errorf("no warning budget")
	}

	if product.WarningBudget == 0 {
		return fmt.Errorf("no warning budget")
	}

	now := time.Now()
	if product.StartTime == nil || product.EndTime == nil {
		return fmt.Errorf("no start or end time")
	}
	if now.Before(*product.StartTime) {
		return fmt.Errorf("not start")
	}
	if now.After(*product.EndTime) {
		return fmt.Errorf("expired")
	}

	wxResp, err := this.WechatCpnRepo.QueryProduct(ctx, product.MchId, product.BatchNo)
	if err != nil {
		return err
	}

	return this.Calculate(ctx, product, wxResp)
}

func (this *VoucherBiz) Calculate(ctx context.Context, product *bo.ProductBo, wxResp *cashcoupons.Stock) error {

	w := this.WxResp(wxResp)

	b := vo.WarningBudgetSendIncr.BuildCache([]string{product.BatchNo})
	key := b.Key
	ttl := b.TTL

	if w.AllBudget > product.AllBudget {
		if err := this.WarningBudgetIncrDel(ctx, key); err != nil {
			return err
		}
	}

	if err := this.ProductRepo.UpdateByWxResp(ctx, product.ID, w); err != nil {
		return err
	}

	if product.WarningBudget >= w.AvailableBudget {

		count, err2 := this.WarningBudgetIncr(ctx, key, ttl)
		if err2 != nil {
			return err2
		}

		if count == 1 {
			return this.WarningSend(ctx, product, w)
		} else {
			log.Warnf("预警查询,当前达到预警第[%d]次,暂不做通知", count)
		}
	}

	return nil
}

func (this *VoucherBiz) WarningSend(ctx context.Context, product *bo.ProductBo, wxResp *do.WxResp) error {

	title := fmt.Sprintf("券预算预警通知:%s", product.BatchName)
	if err := this.DingMixRepo.SendMessage(ctx, title, formatAsCard(product, wxResp)); err != nil {
		return err
	}

	var warningPerson []do.WarningPerson
	if err := json.Unmarshal([]byte(product.WarningPerson), &warningPerson); err != nil {
		return err
	}

	var mobileList []string
	for _, person := range warningPerson {
		mobileList = append(mobileList, person.Mobile)
	}

	if len(mobileList) > 0 {
		return this.SmsMixRepo.Send(ctx, mobileList, this.bc.AliYunSms.TemplateWarning, buildTemplateParams(product, wxResp))
	}

	return nil
}

func buildTemplateParams(product *bo.ProductBo, req *do.WxResp) map[string]string {
	return map[string]string{
		"stock_name":        product.BatchName,
		"stock_no":          product.ProductNo,
		"amount":            strconv.Itoa(int(req.Amount)),
		"all_stock":         strconv.Itoa(int(req.AllStock)),
		"all_budget":        strconv.Itoa(int(req.AllBudget)),
		"used_stock":        strconv.Itoa(int(req.UsedStock)),
		"used_budget":       strconv.Itoa(int(req.UsedBudget)),
		"available_stock":   strconv.Itoa(int(req.AvailableStock)),
		"available_budget":  strconv.Itoa(int(req.AvailableBudget)),
		"budget_usage_rate": fmt.Sprintf("%.1f", req.StockUsageRate),
	}
}

func formatAsCard(product *bo.ProductBo, req *do.WxResp) string {
	var card strings.Builder

	card.WriteString("### " + product.BatchName + "\n\n")

	// 基本信息
	card.WriteString("#### 🎫 基本信息\n")
	card.WriteString(fmt.Sprintf("- **批次号**: %s\n", product.BatchNo))
	card.WriteString(fmt.Sprintf("- **活动号**: %s\n", product.ProductNo))
	card.WriteString(fmt.Sprintf("- **预警值**: %d元\n", product.WarningBudget))
	card.WriteString(fmt.Sprintf("- **面额**: %d元\n", req.Amount))
	card.WriteString(fmt.Sprintf("- **总预算**: %d元\n", req.AllBudget))
	card.WriteString(fmt.Sprintf("- **总库存**: %d张\n", req.AllStock))
	card.WriteString("\n")

	// 使用情况
	card.WriteString("#### 📊 使用情况\n")
	card.WriteString(fmt.Sprintf("- **已发券数**: %d张\n", req.UsedStock))
	card.WriteString(fmt.Sprintf("- **已发券金额**: %d元\n", req.UsedBudget))
	card.WriteString(fmt.Sprintf("- **剩余库存**: %d张\n", req.AvailableStock))
	card.WriteString(fmt.Sprintf("- **剩余预算**: %d元\n", req.AvailableBudget))
	card.WriteString("\n")

	card.WriteString("#### 📈 使用率\n")
	card.WriteString(fmt.Sprintf("- **使用率**: %.1f%%\n", req.StockUsageRate))

	return card.String()
}
