package airDrop

import (
	"errors"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/33cn/chain33/common/address"
	"github.com/parnurzeal/gorequest"
	"github.com/tealeg/xlsx"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"courseSign/server/global"
	"courseSign/server/model/airDrop"
	airDropReq "courseSign/server/model/airDrop/request"
	"courseSign/server/model/common/request"
	user2 "courseSign/server/model/user"
	"courseSign/server/utils"
	"courseSign/server/utils/errno"
)

type RedPackageService struct {
}

// CreateRedPackage 创建RedPackage记录
// Author [piexlmax](https://github.com/piexlmax)
func (redPackageService *RedPackageService) CreateRedPackage(redPackage airDrop.RedPackage) (err error) {
	if redPackage.White != "" {
		mapList := make(map[string]string,0)
		orgList := strings.Split(redPackage.White,",")
		list := make([]string,0)
		for _, v := range orgList {
			var addr string
			if err = address.CheckAddress(v,-1);err == nil {
				addr = v
			} else {
				return err
			}
			if addr == "" {
				continue
			}
			if address.IsEthAddress(v) {
				addr = strings.ToLower(v)
			}
			if _,ok := mapList[addr];!ok {
				list = append(list,addr)
			}
		}
		newWhite := ""
		for k, v := range list {
			if k == len(list)-1 {
				newWhite += v
			} else {
				newWhite += v + ","
			}
		}
		redPackage.White = newWhite
	}
	if redPackage.Password != "" {
		redPackage.Password = utils.Sha256String(redPackage.Password)
	}
	err = global.GVA_DB.Create(&redPackage).Error
	return err
}

// DeleteRedPackage 删除RedPackage记录
// Author [piexlmax](https://github.com/piexlmax)
func (redPackageService *RedPackageService)DeleteRedPackage(redPackage airDrop.RedPackage) (err error) {
	err = global.GVA_DB.Delete(&redPackage).Error
	return err
}

// DeleteRedPackageByIds 批量删除RedPackage记录
// Author [piexlmax](https://github.com/piexlmax)
func (redPackageService *RedPackageService)DeleteRedPackageByIds(ids request.IdsReq) (err error) {
	err = global.GVA_DB.Delete(&[]airDrop.RedPackage{},"id in ?",ids.Ids).Error
	return err
}

// UpdateRedPackage 更新RedPackage记录
// Author [piexlmax](https://github.com/piexlmax)
func (redPackageService *RedPackageService)UpdateRedPackage(redPackage airDrop.RedPackage) (err error) {
	err = global.GVA_DB.Save(&redPackage).Error
	return err
}

// GetRedPackage 根据id获取RedPackage记录
// Author [piexlmax](https://github.com/piexlmax)
func (redPackageService *RedPackageService)GetRedPackage(id uint) (redPackage airDrop.RedPackage, err error) {
	err = global.GVA_DB.Where("id = ?", id).First(&redPackage).Error
	return
}

// GetWhiteList 根据id获取RedPackage whitelist记录
func (redPackageService *RedPackageService)GetWhiteList(id uint) (list []string, err error) {
	var redPackage airDrop.RedPackage
	err = global.GVA_DB.Where("id = ?", id).First(&redPackage).Error
	if err != nil {
		return nil,err
	}
	if redPackage.White != "" {
		return strings.Split(redPackage.White,","),nil
	}
	return
}

func (redPackageService *RedPackageService)ConvertWhiteList(file string) (list []string,err error) {
	xfile,err := xlsx.OpenFile(file)
	if err != nil {
		global.GVA_LOG.Error("ConvertWhiteList",zap.Any("openfile",err))
		return nil,err
	}
	for _, rows := range xfile.Sheets[0].Rows {
		for kk,cell := range rows.Cells {
			if kk == 0 {
				if cell.String() == "" {
					continue
				}
				if address.IsEthAddress(cell.String()) {
					list = append(list,strings.ToLower(cell.String()))
					break
				}
				if err = address.CheckAddress(cell.String(),-1);err == nil {
					list = append(list,cell.String())
				} else {
					global.GVA_LOG.Error("ConvertWhiteList",zap.Any("wrong address",cell.String()))
					return nil,err
				}
			}
		}
	}
	return
}

// GetRedPackageInfoWebList 分页获取RedPackage记录
// Author [piexlmax](https://github.com/piexlmax)
func (redPackageService *RedPackageService)GetRedPackageInfoWebList(info airDropReq.RedPackageSearch) (list []airDrop.RedPackage, total int64, err error) {
	limit := info.PageSize
	offset := info.PageSize * (info.Page - 1)
	// 创建db
	db := global.GVA_DB.Model(&airDrop.RedPackage{})
	var redPackages []airDrop.RedPackage
	// 如果有条件搜索 下方会自动创建搜索语句
	if info.StartCreatedAt !=nil && info.EndCreatedAt !=nil {
		db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
	}
	if info.IsShow {
		db = db.Where("is_show = ? ",info.IsShow)
	} else {
		db = db.Where("is_show = ?",info.IsShow)
	}
	if info.ID > 0 {
		db = db.Where("id = ?",info.ID)
	}
	err = db.Count(&total).Error
	if err!=nil {
		return
	}

	if info.OrderKey != "" {
		var OrderStr string
		// 设置有效排序key 防止sql注入
		// 感谢 Tom4t0 提交漏洞信息
		orderMap := make(map[string]bool, 1)
		orderMap["id"] = true
		if orderMap[info.OrderKey] {
			if info.Desc {
				OrderStr = info.OrderKey + " desc"
			} else {
				OrderStr = info.OrderKey
			}
		} else { // didn't matched any order key in `orderMap`
			return list, total, err
		}
		err = db.Limit(limit).Offset(offset).Order(OrderStr).Find(&redPackages).Error
	} else {
		err = db.Limit(limit).Offset(offset).Order("id desc").Find(&redPackages).Error
	}


	for _, v:= range redPackages {
		v.Password = ""
	}
	return  redPackages, total, err
}

// GetRedPackageInfoList 分页获取RedPackage记录
// Author [piexlmax](https://github.com/piexlmax)
func (redPackageService *RedPackageService)GetRedPackageInfoList(info airDropReq.RedPackageSearch) (list []airDrop.RedPackage, total int64, err error) {
	limit := info.PageSize
	offset := info.PageSize * (info.Page - 1)
    // 创建db
	db := global.GVA_DB.Model(&airDrop.RedPackage{})
    var redPackages []airDrop.RedPackage
    // 如果有条件搜索 下方会自动创建搜索语句
    if info.StartCreatedAt !=nil && info.EndCreatedAt !=nil {
     db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
    }
    if info.IsShow {
    	db = db.Where("is_show = ? ",info.IsShow)
	}
	if info.ID > 0 {
		db = db.Where("id = ?",info.ID)
	}
	err = db.Count(&total).Error
	if err!=nil {
    	return
    }

    if info.OrderKey != "" {
		var OrderStr string
		// 设置有效排序key 防止sql注入
		// 感谢 Tom4t0 提交漏洞信息
		orderMap := make(map[string]bool, 1)
		orderMap["id"] = true
		if orderMap[info.OrderKey] {
			if info.Desc {
				OrderStr = info.OrderKey + " desc"
			} else {
				OrderStr = info.OrderKey
			}
		} else { // didn't matched any order key in `orderMap`
			return list, total, err
		}
		err = db.Limit(limit).Offset(offset).Order(OrderStr).Find(&redPackages).Error
	} else {
		err = db.Limit(limit).Offset(offset).Order("id desc").Find(&redPackages).Error
	}


	for _, v:= range redPackages {
		v.Password = ""
	}
	return  redPackages, total, err
}


// GetRedPackageTxList 分页获取RedPackage记录
// Author [piexlmax](https://github.com/piexlmax)
func (redPackageService *RedPackageService)GetRedPackageTxList(info airDropReq.RedPackageSearch) (list []airDrop.RedPackage, total int64, err error) {
	limit := info.PageSize
	offset := info.PageSize * (info.Page - 1)
	// 创建db
	db := global.GVA_DB.Model(&airDrop.RedPackage{}).Where("pid = ?",info.RedPackage.ID)
	var redPackages []airDrop.RedPackage
	// 如果有条件搜索 下方会自动创建搜索语句
	if info.StartCreatedAt !=nil && info.EndCreatedAt !=nil {
		db = db.Where("created_at BETWEEN ? AND ?", info.StartCreatedAt, info.EndCreatedAt)
	}
	if info.IsShow {
		db = db.Where("is_show = ? ",info.IsShow)
	}
	err = db.Count(&total).Error
	if err!=nil {
		return
	}

	err = db.Limit(limit).Offset(offset).Order("id desc").Find(&redPackages).Error
	for _, v:= range redPackages {
		v.Password = ""
	}
	return  redPackages, total, err
}

func (redPackageService *RedPackageService)ResetVerify(addr string,pid int64) error {
	return global.GVA_DB.Model(&airDrop.VoiceVerify{}).Where("addr = ? and pid = ?",addr,pid).Update("status",airDrop.VoiceVerifyNormal).Error
}

// CheckUserPackage
func (redPackageService *RedPackageService)CheckUserPackage(ip string,addr string,pid int64,pwd string) (trans *airDrop.RedPackageTx,err error) {
	var rp airDrop.RedPackage
	err = global.GVA_DB.Table("red_package").Where("id = ?",pid).Find(&rp).Error
	if err != nil {
		global.GVA_LOG.Error(err.Error())
		return
	}
	if !rp.IsShow {
		err = errors.New("红包不能领取")
		return
	}
	if rp.RemainNum == 0 || rp.Remain == 0  {
		err = errors.New("红包已抢完")
		return
	}
	if rp.IsBlack {
		user := &user2.Users{}
		err = global.GVA_DB.Model(user).Where("address = ?",addr).First(user).Error
		if err != nil && err != gorm.ErrRecordNotFound {
			return
		}
		if err == nil && *user.IsBlack {
			err = errors.New("address limited")
			return
		}
	}
	if rp.IsPassword {
		if pwd == "" {
			err = errors.New("password not set")
			return
		}
		global.GVA_LOG.Info("pwd",zap.String("right pwd",rp.Password),zap.String("pwd",pwd),zap.String("sha256",utils.Sha256String(pwd)))
		if rp.Password != utils.Sha256String(pwd) {
			err = errors.New("password not right")
			return
		}
	}
	// 有白名单 地址须在白名单之内
	if rp.White != "" && !strings.Contains(rp.White,addr) {
		err = errors.New("address not allowed")
		return
	}
	var count,count2 int64
	err = global.GVA_DB.Table("red_package_tx").Where("pid = ? and addr = ?",pid,addr).Count(&count).Error
	if err != nil {
		global.GVA_LOG.Error(err.Error())
		return
	}
	if count >= 1 {
		err = global.GVA_DB.Table("red_package_tx").Where("pid = ? and addr = ?",pid,addr).First(&trans).Error
		if err != nil {
			return
		}
		return
	}
	// 已有记录 先显示 再判断redpackage 是否过期
	if rp.EndAt.Unix() < time.Now().Unix() {
		err = errors.New("红包已过期")
		return
	}
	err = global.GVA_DB.Table("red_package_tx").Where("pid = ? and ip = ?",pid,ip).Count(&count2).Error
	if err != nil {
		global.GVA_LOG.Error(err.Error())
		return
	}
	if int(count2) > rp.IpLimit {
		err = errors.New("over ip limit")
		return
	}

	// 直接请求声纹接口
	type voiceRes struct {
		ID string `json:"id"`
		Result int64 `json:"result"`
		Error string `json:"error"`
		Timestamp int64 `json:"timestamp"`
	}
	var res voiceRes
	var status,ppid int
	ppid = int(pid)
	var verify airDrop.VoiceVerify
	verify.Pid = &ppid
	verify.Addr = addr
	resp,_, errs := gorequest.New().Get("https://voice.bityuan.com/upload/result?address="+addr).EndStruct(&res)
	if len(errs) > 0  {
		status = airDrop.VoiceVerifyNotPass
		verify.Status = &status
		verify.Msg = errs[0].Error()
		global.GVA_LOG.Error("req voice result err",zap.Error(errs[0]),zap.String("addr",addr))
		_ = global.GVA_DB.Create(&verify).Error
		err = errno.ErrVoiceVerifyNotPass
		return
	}
	global.GVA_LOG.Info("code",zap.Any("RESULT",resp.StatusCode),zap.Any("voice result",res))
	if resp.StatusCode == 200 {
		rid,_ := strconv.Atoi(res.ID)
		if int64(rid) != pid {
			err = errors.New("声纹未通过 id 不一致")
			status = airDrop.VoiceVerifyNotPass
			verify.Status = &status
			verify.Msg = fmt.Sprintf("result id %d not eq server id %d",rid,pid)
			global.GVA_LOG.Error("req voice result ok but id not eq",zap.Any("err",errs),zap.String("addr",addr))
			_ = global.GVA_DB.Create(&verify).Error
			return
		}
		verify.VerifyTime = res.Timestamp
		// 声纹通过
		if res.Result == 0 || res.Result == 1 {
			status = airDrop.VoiceVerifyPass
		} else {
			status =airDrop.VoiceVerifyNotPass
		}
		verify.Status = &status
		verify.Msg = res.Error
		err = global.GVA_DB.Create(&verify).Error
		if err != nil {
			return
		}
	} else {
		err = errno.InternalServerError
		return
	}

	// 声纹通过
	if err == nil && *verify.Status == airDrop.VoiceVerifyPass {
		// todo 是否验证
		if time.Now().Unix() - res.Timestamp > 3600 {
			err = errors.New("声纹验证已过期 请重新验证！")
		}
		return
	} else if err == nil && *verify.Status == airDrop.VoiceVerifyNotPass {
		err = errno.ErrVoiceVerifyNotPass
	 	return
	}

	amount := rp.GetRandOne()
	if amount == 0 {
		err = errors.New("红包余额已抢完")
		return
	}

	return
}

func (RedPackageService *RedPackageService) BindFeature(addr string,feature string) (err error) {
	var ft airDrop.VoiceAddr
	err = global.GVA_DB.Model(&ft).Where("eth_addr = ?",addr).First(&ft).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return err
	}
	if err == nil && ft.ID > 0 {
		global.GVA_LOG.Info("BindFeature query bind failed",zap.Any("info",ft))
		return errors.New("地址已绑定声纹")
	}
	err = global.GVA_DB.Model(&ft).Where("feature = ?",addr).First(&ft).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return err
	}
	if err == nil && ft.ID > 0 {
		global.GVA_LOG.Info("BindFeature query2 bind failed",zap.Any("info",ft))
		return errors.New("声纹已绑定地址")
	}
	ft.FeatureId = feature
	ft.EthAddr = addr
	return global.GVA_DB.Save(&ft).Error
}

// AddNewUserPackage
func (redPackageService *RedPackageService)AddNewUserPackage(ip string,addr string,pid int64,feature string) (trans *airDrop.RedPackageTx,err error) {
	trans = new(airDrop.RedPackageTx)
	tx := global.GVA_DB.Begin()
	var rp airDrop.RedPackage
	err = tx.Table("red_package").Where("id = ?",pid).Find(&rp).Error
	if err != nil {
		tx.Rollback()
		global.GVA_LOG.Error(err.Error())
		return
	}
	if rp.VerifyType != airDrop.PackageVerifyTele {
		if !rp.IsShow {
			tx.Rollback()
			err = errors.New("红包不能领取")
			return
		}
	}

	if rp.RemainNum == 0 || rp.Remain == 0  {
		tx.Rollback()
		err = errors.New("红包已抢完")
		return
	}

	// 有白名单 地址须在白名单之内
	if rp.White != "" && !strings.Contains(rp.White,addr) {
		tx.Rollback()
		err = errors.New("address not allowed")
		return
	}
	var count,count2 int64
	err = tx.Table("red_package_tx").Where("pid = ? and addr = ?",pid,addr).Count(&count).Error
	if err != nil {
		tx.Rollback()
		global.GVA_LOG.Error(err.Error())
		return
	}
	if count >= 1 {
		err = tx.Table("red_package_tx").Where("pid = ? and addr = ?",pid,addr).First(&trans).Error
		if err != nil {
			tx.Rollback()
			return
		}
		tx.Commit()
		return
	}

	// 已有记录 先显示 再判断redpackage 是否过期
	if rp.EndAt.Unix() < time.Now().Unix() {
		tx.Rollback()
		err = errors.New("红包已过期")
		return
	}
	if rp.VerifyType == airDrop.PackageVerifyGoogle {
		err = tx.Table("red_package_tx").Where("pid = ? and ip = ?",pid,ip).Count(&count2).Error
		if err != nil {
			tx.Rollback()
			global.GVA_LOG.Error(err.Error())
			return
		}
		if int(count2) > rp.IpLimit {
			tx.Rollback()
			err = errors.New("over ip limit")
			return
		}
	}



	amount := rp.GetRandOne()
	if amount == 0 {
		tx.Rollback()
		err = errors.New("红包余额已抢完")
		return
	}
	err = tx.Table("red_package").Where("id = ?",pid).UpdateColumns(map[string]interface{}{"remain":gorm.Expr("remain - ?",amount),"remain_num":gorm.Expr("remain_num - ?",1)}).Error
	if err != nil {
		tx.Rollback()
		global.GVA_LOG.Error(err.Error())
		return
	}

	trans.Amount = amount
	trans.Addr = addr
	trans.Pid = int(pid)
	trans.Ip = ip
	trans.Feature = feature
	err = tx.Table("red_package_tx").Save(trans).Error
	if err != nil {
		tx.Rollback()
		global.GVA_LOG.Error(err.Error())
		err = errors.New("奖励生成失败")
		return
	}
	tx.Commit()
	return
}

// CheckUserPackageTele
func (redPackageService *RedPackageService)CheckUserPackageTele(ip string,addr string,pid int64,pwd string) (trans *airDrop.RedPackageTx,err error) {
	var rp airDrop.RedPackage
	err = global.GVA_DB.Table("red_package").Where("id = ?",pid).Find(&rp).Error
	if err != nil {
		global.GVA_LOG.Error(err.Error())
		return
	}
	if rp.VerifyType != airDrop.PackageVerifyTele {
		global.GVA_LOG.Error("CheckUserPackageTele",zap.Any("红包类型错误",rp.VerifyType))
		return
	}
	if rp.IpWhite == "" {
		global.GVA_LOG.Error("CheckUserPackageTele",zap.String("ip地址白名单","没有设置"))
		return
	}
	if !strings.Contains(rp.IpWhite,ip) {
		global.GVA_LOG.Error("CheckUserPackageTele",zap.String("ip地址未授权",ip))
		return
	}
	if rp.IsShow {
		err = errors.New("红包配置异常")
		global.GVA_LOG.Error("CheckUserPackageTele",zap.Any("红包不是隐藏模式",rp.IsShow))
		return
	}
	if rp.IsPassword {
		if pwd == "" {
			err = errors.New("password not set")
			return
		}
		global.GVA_LOG.Info("pwd",zap.String("right pwd",rp.Password),zap.String("pwd",pwd),zap.String("sha256",utils.Sha256String(pwd)))
		if rp.Password != utils.Sha256String(pwd) {
			err = errors.New("password not right")
			return
		}
	}
	if rp.RemainNum == 0 || rp.Remain == 0  {
		err = errors.New("红包已抢完")
		return
	}
	if rp.IsBlack {
		user := &user2.Users{}
		err = global.GVA_DB.Model(user).Where("address = ?",addr).First(user).Error
		if err != nil && err != gorm.ErrRecordNotFound {
			return
		}
		if err == nil && *user.IsBlack {
			err = errors.New("address limited")
			return
		}
	}

	// 有白名单 地址须在白名单之内
	if rp.White != "" && !strings.Contains(rp.White,addr) {
		err = errors.New("address not allowed")
		return
	}
	var count,count2 int64
	err = global.GVA_DB.Table("red_package_tx").Where("pid = ? and addr = ?",pid,addr).Count(&count).Error
	if err != nil {
		global.GVA_LOG.Error(err.Error())
		return
	}
	if count >= 1 {
		err = global.GVA_DB.Table("red_package_tx").Where("pid = ? and addr = ?",pid,addr).First(&trans).Error
		if err != nil {
			return
		}
		return
	}
	// 已有记录 先显示 再判断redpackage 是否过期
	if rp.EndAt.Unix() < time.Now().Unix() {
		err = errors.New("红包已过期")
		return
	}
	err = global.GVA_DB.Table("red_package_tx").Where("pid = ? and ip = ?",pid,ip).Count(&count2).Error
	if err != nil {
		global.GVA_LOG.Error(err.Error())
		return
	}
	if int(count2) > rp.IpLimit {
		err = errors.New("over ip limit")
		return
	}
	amount := rp.GetRandOne()
	if amount == 0 {
		err = errors.New("红包余额已抢完")
		return
	}

	return
}

// AddNewUserPackage
func (redPackageService *RedPackageService)AddNewUserPackageTele(ip string,addr string,pid int64) (trans *airDrop.RedPackageTx,err error) {
	trans = new(airDrop.RedPackageTx)
	tx := global.GVA_DB.Begin()
	var rp airDrop.RedPackage
	err = tx.Table("red_package").Where("id = ?",pid).Find(&rp).Error
	if err != nil {
		tx.Rollback()
		global.GVA_LOG.Error(err.Error())
		return
	}
	if rp.RemainNum == 0 || rp.Remain == 0  {
		tx.Rollback()
		err = errors.New("红包已抢完")
		return
	}

	// 有白名单 地址须在白名单之内
	if rp.White != "" && !strings.Contains(rp.White,addr) {
		tx.Rollback()
		err = errors.New("address not allowed")
		return
	}
	var count int64
	err = tx.Table("red_package_tx").Where("pid = ? and addr = ?",pid,addr).Count(&count).Error
	if err != nil {
		tx.Rollback()
		global.GVA_LOG.Error(err.Error())
		return
	}
	if count >= 1 {
		err = tx.Table("red_package_tx").Where("pid = ? and addr = ?",pid,addr).First(&trans).Error
		if err != nil {
			tx.Rollback()
			return
		}
		tx.Commit()
		return
	}

	// 已有记录 先显示 再判断redpackage 是否过期
	if rp.EndAt.Unix() < time.Now().Unix() {
		tx.Rollback()
		err = errors.New("红包已过期")
		return
	}

	amount := rp.GetRandOne()
	if amount == 0 {
		tx.Rollback()
		err = errors.New("红包余额已抢完")
		return
	}
	err = tx.Table("red_package").Where("id = ?",pid).UpdateColumns(map[string]interface{}{"remain":gorm.Expr("remain - ?",amount),"remain_num":gorm.Expr("remain_num - ?",1)}).Error
	if err != nil {
		tx.Rollback()
		global.GVA_LOG.Error(err.Error())
		return
	}

	trans.Amount = amount
	trans.Addr = addr
	trans.Pid = int(pid)
	trans.Ip = ip
	trans.Source = "tele"
	err = tx.Table("red_package_tx").Save(trans).Error
	if err != nil {
		tx.Rollback()
		global.GVA_LOG.Error(err.Error())
		err = errors.New("奖励生成失败")
		return
	}
	tx.Commit()
	return
}
