package model

import (
	"active/constant"
	"active/tools"
	"database/sql"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"

	"git.jiaxianghudong.com/go/gsdkclient"
	"git.jiaxianghudong.com/go/logs"
	"git.jiaxianghudong.com/go/xlog"
	"git.jiaxianghudong.com/webs/pkg/dbx"
	"git.jiaxianghudong.com/webs/pkg/rds"
)

//GoodsCache 调用到的计费点缓存
var GoodsCache sync.Map
var PayBlackCache sync.Map

//PayBlackCacheNew 锁机制
var PayBlackCacheNew *PbCacheMap
var VerIPBlackCacheNew *VerIpCacheMap

const MaxIpNum = 50

type PbCacheMap struct {
	Data map[int]int
	Lock *sync.RWMutex
}

func NewPayBlackCacheNew() *PbCacheMap {
	return &PbCacheMap{
		Data: make(map[int]int),
		Lock: &sync.RWMutex{},
	}
}
func (d *PbCacheMap) Get(k int) (int, bool) {

	d.Lock.RLock()

	defer d.Lock.RUnlock()
	v, ok := d.Data[k]
	return v, ok

}

func (d *PbCacheMap) Set(k int, v int) {

	d.Lock.Lock()

	defer d.Lock.Unlock()

	d.Data[k] = v

}

type VerIpCacheMap struct {
	Data map[string][]string
	Lock *sync.RWMutex
}

func NewVerIpCacheNew() *VerIpCacheMap {
	return &VerIpCacheMap{
		Data: make(map[string][]string),
		Lock: &sync.RWMutex{},
	}
}
func (d *VerIpCacheMap) Get(k string) ([]string, bool) {

	d.Lock.RLock()

	defer d.Lock.RUnlock()
	v, ok := d.Data[k]
	return v, ok

}

func (d *VerIpCacheMap) Set(k string, v []string) {

	d.Lock.Lock()

	defer d.Lock.Unlock()

	d.Data[k] = v

}

//BlackIPDuan 审核人员IP段
var BlackIPDuan = []string{"182.254.115", "59.37.125"}

//BlackIPs 审核人员IP
var BlackIPs = []string{"111.222.19.134"}

var TestUserIDS = []int{101832265, 125660976, 101758654, 101616296, 56409319}

func GetBlackByVerIP(ver, ip string, appid int) bool {
	vkey := fmt.Sprintf("%d-%s", appid, ver)

	//判断大于 50个不同IP就返回 黑名单关闭
	if barr, ok := VerIPBlackCacheNew.Get(vkey); !ok {
		VerIPBlackCacheNew.Set(vkey, []string{ip})
		return true
	} else {

		if len(barr) >= MaxIpNum {
			return false
		} else {

			//未达到50次
			if !tools.InStringArray(ip, barr) {
				barr = append(barr, ip)
				VerIPBlackCacheNew.Set(vkey, barr)
			}

			return true
		}

	}
}

//GetGoods 获取计费点缓存
func GetGoods(vcfrom string) map[string]interface{} {
	//先读取缓存
	var goodsret string
	goodsKey := fmt.Sprintf("%s%s", vcfrom, constant.GOODSKEY)
	if data, ok := GoodsCache.Load(goodsKey); ok {
		logs.Debugf("cache key: %s", goodsKey)
		return data.(map[string]interface{})
	}

	allgoods := make(map[string]interface{})
	goodsret = rds.Redis8.Get(goodsKey).Val()
	err := json.Unmarshal([]byte(goodsret), &allgoods)
	if err != nil {
		logs.Error(fmt.Sprintf("goodsret: %s err: %v", goodsret, err))
		return allgoods
	}
	var goodsconfigs = make(map[string]interface{})
	if data, ok := allgoods[constant.GOODSVERSION]; ok {
		if val, ok := data.(map[string]interface{}); ok {
			goodsconfigs = val
		}
	}

	fmt.Printf("【goods configs】: key:%s %+v", goodsKey, goodsconfigs)
	GoodsCache.Store(goodsKey, goodsconfigs)

	return goodsconfigs
}

//InBlacklist 获取黑名单
func InBlacklist(userid string) bool {
	if data, ok := PayBlackCache.Load(constant.PAYBLACKLIST); ok {
		logs.Debugf("PayBlackCache cache key: %s", constant.PAYBLACKLIST)
		return tools.InStringArray(userid, data.([]string))
	}

	blist := rds.Redis8.SMembers(constant.PAYBLACKLIST).Val()
	fmt.Printf(" \n【pay black configs】:  %+v", blist)
	PayBlackCache.Store(constant.PAYBLACKLIST, blist)

	return tools.InStringArray(userid, blist)
}

//GetBlackList 获取黑名单
func GetBlackList(userid string, ip, UA string) bool {
	var ipDuan string
	ipArr := strings.Split(ip, ".")
	if len(ipArr) >= 3 {
		ipDuan = strings.Join(ipArr[0:3], ".")
	}

	// 1. 根据ip段来判断如果是黑名单IP段， 用户直接拉入黑名单
	if GetBlackIPDuanCache(ipDuan) {
		var count int
		uID, _ := strconv.Atoi(userid)
		userInfo := gsdkclient.Client().UserInfoNoLoginData(uID)
		temp := gsdkclient.Client().GetEffortEx(uint(uID), []int{2}, false) //获取局数
		if temp != nil {
			count = int(temp[2]) //局数
		}

		xlog.Infof("【 黑名单网段玩家 】 %v ip:%s agent:%v", userid, ip, UA)
		if userIp := rds.Redis8.HGet(constant.PAYBLACKUSERHASH, userid).Val(); userIp != "" {
			rds.Redis8.HSet(constant.PAYBLACKUSERHASHTMP, userid, fmt.Sprintf("%s|局数:%d|注册时间:%s", ip, count, userInfo.RegDate))
		}
		return true
	}

	if userIp := rds.Redis8.HGet(constant.PAYBLACKUSERHASH, userid).Val(); userIp != "" {
		xlog.Infof("【 审核玩家 】 %v ip:%s agent:%v", userid, ip, UA)
		rds.Redis8.HIncrBy(constant.PAYBLACKIPHASH, ip, 1) //黑名单玩家计入使用该ip次数
		return true
	}

	// hour := time.Now().Hour() //高峰期不执行查询操作, 小于晚上8点，或者大于等于10点 进行特定IP认证
	// if hour < 20 || hour >= 22 {
	// 	//2. 不是IP段再确认玩家是否黑名单玩家
	// 	if userIp := rds.Redis8.HGet(constant.PAYBLACKUSERHASH, userid).Val(); userIp != "" {
	// 		rds.Redis8.HIncrBy(constant.PAYBLACKIPHASH, ip, 1) //黑名单玩家计入使用该ip次数
	// 		return true
	// 	}

	// 	// //3. 不是黑名单玩家再判断是否 具体黑名单IP并且达到拉黑值
	// 	// if blackip := rds.Redis8.HGet(constant.PAYBLACKIPHASH, ip).Val(); blackip != "" { //观察黑名单IP列表名单
	// 	// 	if c, _ := strconv.Atoi(blackip); c >= 5 { //超过黑名单IP次数，加入黑名单用户
	// 	// 		rds.Redis8.HSet(constant.PAYBLACKUSERHASH, userid, ip)
	// 	// 		return true
	// 	// 	}
	// 	// }
	// }

	return false
}

//GetBlackIPDuanCache 获取黑名单ip段缓存
func GetBlackIPDuanCache(ipDuan string) bool {
	// if data, ok := PayBlackIPDuanCache.Load(constant.PAYBLACKIPRANGE); ok {
	// 	logs.Debugf("PayBlackIPDuanCache cache key: %s", constant.PAYBLACKIPRANGE)
	// 	return tools.InStringArray(ipDuan, data.([]string))
	// }

	//blist := rds.Redis8.SMembers(constant.PAYBLACKIPRANGE).Val()
	//fmt.Printf(" \n【PayBlackIPDuanCache configs】:  %+v", blist)
	//PayBlackCache.Store(constant.PAYBLACKIPRANGE, blist)

	//return tools.InStringArray(ipDuan, blist)
	return tools.InStringArray(ipDuan, BlackIPDuan)
}

//GetBlackListVer2 黑名单新算法尝试  黑名单 A，B，C，D, E
func GetBlackListVer2(userid, ip, UA string, totalmoney int, nickname string) (int, bool) {
	var ipDuan string
	ipArr := strings.Split(ip, ".")
	if len(ipArr) >= 3 {
		ipDuan = strings.Join(ipArr[0:3], ".")
	}

	uID, _ := strconv.Atoi(userid)

	if tools.InStringArray(ip, BlackIPs) {
		return 0, true
	}

	// 1. 根据ip段来判断如果是黑名单IP段， 用户直接拉入黑名单分区
	if GetBlackIPDuanCache(ipDuan) {
		xlog.Infof("【 黑名单网段玩家 】 %v ip:%s agent:%v", userid, ip, UA)
		//获取用户信息
		userInfo := gsdkclient.Client().UserInfoNoLoginData(uID)
		var count int
		temp := gsdkclient.Client().GetEffortEx(uint(uID), []int{2}, false) //获取局数
		if temp != nil {
			count = int(temp[2]) //局数
		}

		//CheckBlackAB 判断黑名单AB
		if typ, ok := CheckBlackABE(ip, userInfo.NickName, userInfo.RegDate, uID, 2, totalmoney, count); ok {
			return typ, ok
		}

		if totalmoney == 0 { //判断黑名单C 无特征，无支付 不影响审核人员判断依据，但是不开放支付功能
			b := &BlackUsers{
				Type:       3,
				Ip:         ip,
				Count:      count,
				Createtime: userInfo.RegDate,
				Nickname:   userInfo.NickName,
				Totalmoney: totalmoney,
				Userid:     uID,
			}
			b.Add()
			return b.Type, true
		} else { //黑名单D 无特征，有支付过玩家，不开放支付，进入审核名单没问题可以对外开放
			b := &BlackUsers{
				Type:       4,
				Ip:         ip,
				Count:      count,
				Createtime: userInfo.RegDate,
				Nickname:   userInfo.NickName,
				Totalmoney: totalmoney,
				Userid:     uID,
			}
			b.Add()
			return b.Type, true
		}
	} else {
		//非黑名单认证用户nick是不是审核人员
		if typ, ok := CheckBlackA(ip, nickname, uID); ok {
			return typ, ok
		}
	}

	//判断是不是黑名单成员 如果是等级AB 记录他使用的IP, 等级CD只限制不统计IP
	//规则  A|182.254.115.151|局数:0|注册时间:2021-07-15 17:22:33.000000|昵称:WL_368879456|历史支付:10元
	if typ := GetBlackLevel(uID); typ > 0 {

		if tools.InIntArray(typ, []int{1, 2}) {
			xlog.Infof("【 黑名单明确审核玩家切换IP 】 %v ip:%s agent:%v", userid, ip, UA)
			//	rds.Redis8.HIncrBy(constant.PAYBLACKLEVELIP, ip, 1) //黑名单玩家计入使用该ip次数
			return 0, true
		}

		xlog.Infof("【 黑名单不确定审核人员玩家切换IP 】 %v ip:%s agent:%v", userid, ip, UA)
		return 0, true
	}

	hour := time.Now().Hour() //高峰期不执行查询操作, 小于晚上8点，或者大于等于10点 进行特定IP认证
	if hour < 20 || hour >= 23 {
		//判断用户IP是不是 黑名单用户使用过的IP
		countX := rds.Redis8.HGet(constant.PAYBLACKLEVELIP, ip).Val()
		if c, _ := strconv.Atoi(countX); c >= 5 {
			userInfo := gsdkclient.Client().UserInfoNoLoginData(uID)
			var count int
			temp := gsdkclient.Client().GetEffortEx(uint(uID), []int{2}, false) //获取局数
			if temp != nil {
				count = int(temp[2]) //局数
			}
			//CheckBlackAE 判断黑名单AE
			if typ, ok := CheckBlackABE(ip, userInfo.NickName, userInfo.RegDate, uID, 5, totalmoney, count); ok {
				xlog.Infof("【 通过黑名单IP进来的审核人员 】 %v ip:%s agent:%v", userid, ip, UA)
				return typ, ok
			}

			var day int
			regTime := strings.Replace(userInfo.RegDate, "T", " ", -1)
			if len(regTime) >= 19 {
				parse, _ := time.Parse("2006-01-02 15:04:05", regTime)
				day = CheckSubDay(parse)
			}
			//2个月未支付玩家，拉入黑名单C
			if day >= 60 && totalmoney == 0 {
				b := &BlackUsers{
					Type:       3,
					Ip:         ip,
					Count:      count,
					Createtime: userInfo.RegDate,
					Nickname:   userInfo.NickName,
					Totalmoney: totalmoney,
					Userid:     uID,
				}
				b.Add()
				return b.Type, true
			} else {
				//用了黑名单用户ip, 但是充值过，或者只是近期没充值加入观察名单
				r := &RedUsers{
					Type:       1,
					Ip:         ip,
					Count:      count,
					Createtime: userInfo.RegDate,
					Nickname:   userInfo.NickName,
					Totalmoney: totalmoney,
					Userid:     uID,
				}
				r.Add()
				return 0, false
			}
		}
	}
	return 0, false
}

func CheckBlackA(ip, nickname string, userid int) (int, bool) {
	if len(nickname) > 7 {
		//判断是否符合黑名单A特征 含有关键字
		if strings.HasPrefix(nickname, "tencent_game") ||
			strings.HasPrefix(nickname, "rdgztest_") ||
			strings.HasPrefix(nickname, "minigamecheck") {
			b := &BlackUsers{
				Type:       1,
				Ip:         ip,
				Count:      0,
				Createtime: "2022-01-01 00:00:00",
				Nickname:   nickname,
				Totalmoney: 0,
				Userid:     userid,
			}
			b.Add()
			return b.Type, true
		}
	}

	return 0, false
}

func CheckBlackABE(ip, nickname, reg string, userid, paytype, totalmoney, count int) (int, bool) {

	//判断是否符合黑名单A特征 含有关键字
	if strings.Contains(nickname, "tencent_game") ||
		strings.Contains(nickname, "rdgztest_") ||
		strings.Contains(nickname, "minigamecheck") ||
		strings.Contains(nickname, "city") {
		b := &BlackUsers{
			Type:       1,
			Ip:         ip,
			Count:      count,
			Createtime: reg,
			Nickname:   nickname,
			Totalmoney: totalmoney,
			Userid:     userid,
		}
		b.Add()
		return b.Type, true
	}

	//判断黑名单B,E情况 局数0注册时间久
	regTime := strings.Replace(reg, "T", " ", -1)
	if len(regTime) >= 19 {
		parse, _ := time.Parse("2006-01-02 15:04:05", regTime)
		if day := CheckSubDay(parse); day >= 180 && count == 0 {
			b := &BlackUsers{
				Type:       paytype,
				Ip:         ip,
				Count:      count,
				Createtime: reg,
				Nickname:   nickname,
				Totalmoney: totalmoney,
				Userid:     userid,
			}
			b.Add()
			return b.Type, true
		}
	}

	return 0, false
}

//GetBlackLevel 获取黑名单等级
func GetBlackLevel(userid int) int {
	if v, ok := PayBlackCacheNew.Get(userid); ok {
		return v
	}

	return -1
}

//RedUsers 观察名单
type RedUsers struct {
	Id         int    `json:"id" sql:"id"`                   //自增ID
	Userid     int    `json:"userid" sql:"userid"`           //用户ID
	Nickname   string `json:"nickname" sql:"nickname"`       //用户昵称
	Count      int    `json:"count" sql:"count"`             //局数
	Totalmoney int    `json:"total_money" sql:"total_money"` //支付历史金额
	Ip         string `json:"ip" sql:"ip"`                   //ip
	Createtime string `json:"create_time" sql:"create_time"` //注册时间
	Time       string `json:"time" sql:"time"`               //创建时间
	Type       int    `json:"type" sql:"type"`               //等级	 1观察名单 3 排除名单
}

//BlackUsers 黑名单
type BlackUsers struct {
	Id         int    `json:"id" sql:"id"`                   //自增ID
	Userid     int    `json:"userid" sql:"userid"`           //用户ID
	Nickname   string `json:"nickname" sql:"nickname"`       //用户昵称
	Count      int    `json:"count" sql:"count"`             //局数
	Totalmoney int    `json:"total_money" sql:"total_money"` //支付历史金额
	Ip         string `json:"ip" sql:"ip"`                   //ip
	Createtime string `json:"create_time" sql:"create_time"` //注册时间
	Time       string `json:"time" sql:"time"`               //创建时间
	Type       int    `json:"type" sql:"type"`               //等级 1(关键字) 2(无游戏) 3(无支付) 4(IP段其他) 5(单IP无游戏)
}

func (b *BlackUsers) Add() (int64, error) {
	var last int64
	var err error
	sqlQuery := fmt.Sprintf("INSERT IGNORE INTO black_users (`userid`,`nickname`,`count`,`total_money`,`ip`, `create_time`, `type`) VALUE(?,?,?,?,?,?,?)")
	last, err = dbx.MySQL.Insert(sqlQuery, b.Userid, b.Nickname, b.Count, b.Totalmoney, b.Ip, strings.Replace(b.Createtime, "T", " ", -1), b.Type)
	if err != nil {
		logs.Error(fmt.Sprintf("black_users param:%#v err:%v", b, err))
		return 0, err
	}
	return last, nil
}

func GetBlackUsersType(userid int) int {
	var typ int
	err := dbx.MySQL.QueryRow("SELECT type from `black_users` where userid = ? ",
		userid).Scan(&typ)
	if nil != err {
		if err == sql.ErrNoRows {
			return typ
		}

		logs.Errorf("GetBlackUsersType err:%v", err)
	}
	return typ
}

func GetBlackAlLUsers() {
	PayBlackCacheNew = NewPayBlackCacheNew()
	t1 := time.Now()
	rows, err := dbx.MySQL.Query("SELECT userid, type from `black_users`  where type in (1,2)")
	defer rows.Close()
	if err != nil && err != sql.ErrNoRows {
		logs.Errorf("GetBlackAlLUsers err:%v", err)
		return
	}

	var userid, typ int
	for rows.Next() {
		err = rows.Scan(&userid, &typ)
		if err != nil {
			logs.Errorf("GetBlackAlLUsers err:%v", err)
			return
		}

		PayBlackCacheNew.Set(userid, typ)
	}

	t2 := time.Now()
	xlog.Infof("加载黑名单配置时长:%s 数据:%+v", t2.Sub(t1), PayBlackCacheNew)
}

func (b *RedUsers) Add() (int64, error) {
	var last int64
	var err error
	sqlQuery := "INSERT IGNORE INTO red_users (`userid`,`nickname`,`count`,`total_money`,`ip`, `create_time`, `type`) VALUE(?,?,?,?,?,?,?)"
	last, err = dbx.MySQL.Insert(sqlQuery, b.Userid, b.Nickname, b.Count, b.Totalmoney, b.Ip, strings.Replace(b.Createtime, "T", " ", -1), b.Type)
	if err != nil {
		logs.Error(fmt.Sprintf("red_users param:%#v err:%v", b, err))
		return 0, err
	}
	return last, nil
}

func GetBUserInfo(tag string, st int64, level string) []BlackUsers {

	start := tools.TimeParseForMat(st, "2006-01-02 15:04:05")
	end := tools.TimeParseForMat(st+86400, "2006-01-02 15:04:05")

	var table, where string
	if tag == "red" {
		table = "red_users"
	} else if tag == "black" {
		table = "black_users"
	} else {
		return nil
	}

	if level != "" {
		l, _ := strconv.Atoi(level)
		where = fmt.Sprintf(" and type = %d", l)
	}
	var bu []BlackUsers
	sql := fmt.Sprintf("select * from `%s` where time >= '%s' and time <= '%s' %s ORDER BY time DESC", table, start, end, where)

	dbx.MySQL.Query2(sql, &bu)

	return bu
}
