package models

// roll房
import (
	"box-gm/utils"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"sync"
	"time"

	"github.com/astaxie/beego"
	"github.com/astaxie/beego/validation"
	"github.com/garyburd/redigo/redis"
)

type RollConditionInfo struct {
	Conditions string `json:"c"`  // 条件
	CValue     int    `json:"cv"` // 条件值
}
type RollInfo struct {
	Id              int                  // 活动ID
	Name            string               `orm:"size(64)" valid:"Required"` // 房间名
	BandShareCode   string               `orm:"size(10)" valid:"Required"` // 限制绑定推广码
	CreateTime      int64                `valid:"Required"`                // 创建时间
	ShowStartTime   int64                `valid:"Required"`                // 开始展示时间
	SignupStartTime int64                `valid:"Required"`                // 报名开始时间
	SignupEndTime   int64                `valid:"Required"`                // 报名结束时间
	AwardTime       int64                `valid:"Required"`                // 开奖时间
	Conditions      []*RollConditionInfo // 参与条件列表
	ItemIdList      []int                // 奖励物品ID列表
	SignupUidList   []int                // 报名玩家ID列表列表
}

type RollInfoArray []*RollInfo

type RollCondition struct {
	KeyCondition string
	Name         string
}

type RES_ROLL struct {
	Code int `json:"code"` // 错误码
}

type RollConditionArray []*RollCondition

var conditionList RollConditionArray

var rollList RollInfoArray
var riMutex sync.Mutex
var ROLL_MAIN_KEY = "roll:room"

func get_roll_key(id int) string {
	return fmt.Sprintf("%s:%d", ROLL_MAIN_KEY, id)
}

func CreateRollId() int {
	pool := utils.GetAccountRedisPool()
	if pool == nil {
		fmt.Println("get redis pool fail")
		return 0
	}
	rd := pool.Get()
	if rd == nil {
		fmt.Println("get redis conn fail")
		return 0
	}
	defer rd.Close()

	reply, err := rd.Do("hincrby", "global", "max:roll:room:id", 1)
	if err != nil {
		fmt.Printf("redis hincrby err: %v", err)
		return 0
	}
	id, err := redis.Int(reply, err)
	if err != nil {
		fmt.Printf("redis hincrby err: %v", err)
		return 0
	}
	return id
}

// 初始化roll房信息
func InitRollInfo() {
	// 活跃ID列表
	pool := utils.GetAccountRedisPool()
	if pool == nil {
		fmt.Println("get redis pool fail")
		return
	}
	rd := pool.Get()
	defer rd.Close()

	rollList = RollInfoArray{}

	result, _ := redis.Values(rd.Do("smembers", ROLL_MAIN_KEY))
	for _, v := range result {
		id := utils.StringToInt(string(v.([]byte)))
		pKey := get_roll_key(id)
		log.Printf("InitRollInfo v[%v] id[%d] pKey[%s]", v, id, pKey)
		name, _ := redis.String(rd.Do("hget", pKey, "name"))
		bandShareCode, _ := redis.String(rd.Do("hget", pKey, "bandShareCode"))
		creatTime, _ := redis.Int64(rd.Do("hget", pKey, "creatTime"))
		showStartTime, _ := redis.Int64(rd.Do("hget", pKey, "showStartTime"))
		signupStartTime, _ := redis.Int64(rd.Do("hget", pKey, "signupStartTime"))
		signupEndTime, _ := redis.Int64(rd.Do("hget", pKey, "signupEndTime"))
		awardTime, _ := redis.Int64(rd.Do("hget", pKey, "awardTime"))

		conditions, _ := redis.String(rd.Do("hget", pKey, "conditions"))
		var _conditions []*RollConditionInfo
		json.Unmarshal([]byte(conditions), &_conditions)

		itemIdList, _ := redis.String(rd.Do("hget", pKey, "itemIdList"))
		var _itemIdList []int
		json.Unmarshal([]byte(itemIdList), &_itemIdList)

		signupUidList, _ := redis.String(rd.Do("hget", pKey, "signupUidList"))
		var _signupUidList []int
		json.Unmarshal([]byte(signupUidList), &_signupUidList)

		rollList = append(rollList, &RollInfo{
			Id:              id,
			Name:            name,
			BandShareCode:   bandShareCode,
			CreateTime:      creatTime,
			ShowStartTime:   showStartTime,
			SignupStartTime: signupStartTime,
			SignupEndTime:   signupEndTime,
			AwardTime:       awardTime,
			Conditions:      _conditions,
			ItemIdList:      _itemIdList,
			SignupUidList:   _signupUidList,
		})
	}

	conditionList = append(conditionList, &RollCondition{
		KeyCondition: "pay_day_totalAmount",
		Name:         "今日充值(单位:分)",
	})
	beego.Info("load roll info", len(rollList))
}

func checkRollInfo(hf *RollInfo) (err error) {
	valid := validation.Validation{}
	b, _ := valid.Valid(&hf)
	if !b {
		for _, err := range valid.Errors {
			log.Println(err.Key, err.Message)
			return errors.New(err.Message)
		}
	}
	return nil
}

// 新增roll房信息
func AddRollInfo(hf *RollInfo) error {
	pool := utils.GetAccountRedisPool()
	if pool == nil {
		return fmt.Errorf("get redis pool fail")
	}
	rd := pool.Get()
	defer rd.Close()

	if err := checkRollInfo(hf); err != nil {
		return err
	}

	if hf.Id == 0 {
		hf.Id = CreateRollId()
	}
	if hf.Id == 0 {
		return fmt.Errorf("分配roll房ID失败")
	}
	// 插入数据库
	db, err := utils.GetMysqlHandler()
	if err != nil {
		log.Println("GetMysqlHandler, Error: ", err)
		return err
	}

	hf.CreateTime = time.Now().Unix()

	bConditions, _ := json.Marshal(hf.Conditions)
	bItemIdList, _ := json.Marshal(hf.ItemIdList)
	columns := "id,name,bandShareCode,createTime,showStartTime,signupStartTime,signupEndTime,awardTime,conditions,itemIdList"
	values := fmt.Sprintf("%d, '%s', '%s', %d, %d, %d, %d,  %d, '%s', '%s'", hf.Id, hf.Name, hf.BandShareCode, hf.CreateTime, hf.ShowStartTime, hf.SignupStartTime, hf.SignupEndTime, hf.AwardTime, bConditions, bItemIdList)
	sql := fmt.Sprintf("insert into mdl_roll (%s) value (%s);", columns, values)
	log.Println("AddRollInfo sql:", sql)
	_, err = db.Exec(sql)
	if err != nil {
		log.Println("AddRollInfo Insert data Exec Failed, Error: ", err)
		return err
	}

	pKey := get_roll_key(hf.Id)
	rd.Do("hset", pKey, "name", hf.Name)
	rd.Do("hset", pKey, "bandShareCode", hf.BandShareCode)
	rd.Do("hset", pKey, "createTime", hf.CreateTime)
	rd.Do("hset", pKey, "showStartTime", hf.ShowStartTime)
	rd.Do("hset", pKey, "signupStartTime", hf.SignupStartTime)
	rd.Do("hset", pKey, "signupEndTime", hf.SignupEndTime)
	rd.Do("hset", pKey, "awardTime", hf.AwardTime)
	rd.Do("hset", pKey, "conditions", bConditions)
	rd.Do("hset", pKey, "itemIdList", bItemIdList)

	rd.Do("sadd", ROLL_MAIN_KEY, hf.Id)

	riMutex.Lock()
	rollList = append(rollList, hf)
	riMutex.Unlock()

	SendGameAddRoll(hf.Id)
	return nil
}

// 修改roll房信息
func UpdateRollInfo(hf *RollInfo) error {
	pool := utils.GetAccountRedisPool()
	if pool == nil {
		return fmt.Errorf("get redis pool fail")
	}
	rd := pool.Get()
	defer rd.Close()

	if err := checkRollInfo(hf); err != nil {
		return err
	}

	// 更新数据库
	db, err := utils.GetMysqlHandler()
	if err != nil {
		log.Println("GetMysqlHandler, Error: ", err)
		return err
	}
	bConditions, _ := json.Marshal(hf.Conditions)
	bItemIdList, _ := json.Marshal(hf.ItemIdList)
	sets := fmt.Sprintf("name='%s',bandShareCode='%s',showStartTime=%d,signupStartTime=%d,signupEndTime=%d,awardTime=%d,conditions='%s',itemIdList='%s'",
		hf.Name, hf.BandShareCode, hf.ShowStartTime, hf.SignupStartTime, hf.SignupEndTime, hf.AwardTime, bConditions, bItemIdList)
	sql := fmt.Sprintf("UPDATE mdl_roll SET %s WHERE id=%d;", sets, hf.Id)
	// log.Println("order_insert_db sql:", sql)
	_, err = db.Exec(sql)
	if err != nil {
		log.Println("UpdateRollInfo update data Exec Failed, Error: ", err)
		return err
	}

	pKey := get_roll_key(hf.Id)
	rd.Do("hset", pKey, "name", hf.Name)
	rd.Do("hset", pKey, "bandShareCode", hf.BandShareCode)
	rd.Do("hset", pKey, "showStartTime", hf.ShowStartTime)
	rd.Do("hset", pKey, "signupStartTime", hf.SignupStartTime)
	rd.Do("hset", pKey, "signupEndTime", hf.SignupEndTime)
	rd.Do("hset", pKey, "awardTime", hf.AwardTime)
	rd.Do("hset", pKey, "conditions", bConditions)
	rd.Do("hset", pKey, "itemIdList", bItemIdList)

	riMutex.Lock()
	for i := 0; i < len(rollList); i++ {
		if hf.Id == rollList[i].Id {
			old := rollList[i]

			old.Name = hf.Name
			old.BandShareCode = hf.BandShareCode
			old.ShowStartTime = hf.ShowStartTime
			old.SignupStartTime = hf.SignupStartTime
			old.SignupEndTime = hf.SignupEndTime
			old.AwardTime = hf.AwardTime
			old.Conditions = hf.Conditions
			old.ItemIdList = hf.ItemIdList
			break
		}
	}
	riMutex.Unlock()

	SendGameUpdateRoll(hf.Id)

	return nil
}

// 更新参与玩家列表
func UpdateSignupUidList(id int) error {
	pool := utils.GetAccountRedisPool()
	if pool == nil {
		return fmt.Errorf("get redis pool fail")
	}
	rd := pool.Get()
	defer rd.Close()

	pKey := get_roll_key(id)
	signupUidList, _ := redis.String(rd.Do("hget", pKey, "signupUidList"))
	var _signupUidList []int
	json.Unmarshal([]byte(signupUidList), &_signupUidList)

	for i := 0; i < len(rollList); i++ {
		if id == rollList[i].Id {
			old := rollList[i]

			old.SignupUidList = _signupUidList
			break
		}
	}
	return nil
}

// 删除roll房信息
func DelRollInfoById(id int) error {
	pool := utils.GetAccountRedisPool()
	if pool == nil {
		return fmt.Errorf("get redis pool fail")
	}
	rd := pool.Get()
	defer rd.Close()

	riMutex.Lock()
	defer riMutex.Unlock()

	pKey := get_roll_key(id)
	_, err := rd.Do("del", pKey)
	if err != nil {
		log.Println(err)
		return err
	}
	rd.Do("srem", ROLL_MAIN_KEY, id)

	for i := 0; i < len(rollList); i++ {
		if id == rollList[i].Id {
			ii := i + 1
			rollList = append(rollList[:i], rollList[ii:]...)
		}
	}
	SendGameDeleteRoll(id)

	return nil
}

// 删除非活跃roll房
func del_unactive_roll_info(id int) error {
	pool := utils.GetAccountRedisPool()
	if pool == nil {
		return fmt.Errorf("get redis pool fail")
	}
	rd := pool.Get()
	defer rd.Close()

	pKey := get_roll_key(id)
	_, err := rd.Do("del", pKey)
	if err != nil {
		log.Println(err)
		return err
	}
	rd.Do("srem", ROLL_MAIN_KEY, id)

	SendGameDeleteRoll(id)

	return nil
}

// 获取roll房信息信息
func GetRollInfoById(id int) (hf *RollInfo) {
	riMutex.Lock()
	defer riMutex.Unlock()

	// 更新参与列表
	UpdateSignupUidList(id)
	for i := 0; i < len(rollList); i++ {
		if id == rollList[i].Id {
			return rollList[i]
		}
	}

	return nil
}
func GetRollInfoList() RollInfoArray {
	riMutex.Lock()
	defer riMutex.Unlock()

	// 更新参与列表
	for _, v := range rollList {
		UpdateSignupUidList(v.Id)
	}
	// 已开奖
	var _rollList RollInfoArray
	currTime := time.Now().Unix()
	for _, v := range rollList {
		if v.AwardTime > currTime {
			_rollList = append(_rollList, v)
		} else {
			del_unactive_roll_info(v.Id)
		}
	}
	rollList = _rollList

	return rollList
}
func GetRollConditionList() RollConditionArray {
	return conditionList
}

// 游戏服 - 新增
func SendGameAddRoll(id int) error {
	params := make(map[string]interface{})
	params["name"] = "gm_add_roll"
	data := make(map[string]interface{})
	data["id"] = id
	params["data"] = data

	body, _ := json.Marshal(params)
	host := utils.GetKeyConf("gameservers", "web")
	sign := utils.Strtomd5(string(body) + utils.GetKeyConf("app", "gameKey"))
	url := "http://" + host + "/json?sign=" + sign
	buff, err := utils.SendAndRecvHTTP("POST", url, string(body))
	if err != nil {
		return err
	}

	var res RES_ROLL
	err = json.Unmarshal(buff, &res)
	if err != nil {
		return err
	}
	if res.Code != 200 {
		return fmt.Errorf("code:%d", res.Code)
	}
	return nil
}

// 游戏服 - 更新
func SendGameUpdateRoll(id int) error {
	params := make(map[string]interface{})
	params["name"] = "gm_update_roll"
	data := make(map[string]interface{})
	data["id"] = id
	params["data"] = data

	body, _ := json.Marshal(params)
	host := utils.GetKeyConf("gameservers", "web")
	sign := utils.Strtomd5(string(body) + utils.GetKeyConf("app", "gameKey"))
	url := "http://" + host + "/json?sign=" + sign
	buff, err := utils.SendAndRecvHTTP("POST", url, string(body))
	if err != nil {
		return err
	}

	var res RES_ROLL
	err = json.Unmarshal(buff, &res)
	if err != nil {
		return err
	}
	if res.Code != 200 {
		return fmt.Errorf("code:%d", res.Code)
	}
	return nil
}

// 游戏服 - 更新
func SendGameDeleteRoll(id int) error {
	params := make(map[string]interface{})
	params["name"] = "gm_del_roll"
	data := make(map[string]interface{})
	data["id"] = id
	params["data"] = data

	body, _ := json.Marshal(params)
	host := utils.GetKeyConf("gameservers", "web")
	sign := utils.Strtomd5(string(body) + utils.GetKeyConf("app", "gameKey"))
	url := "http://" + host + "/json?sign=" + sign
	buff, err := utils.SendAndRecvHTTP("POST", url, string(body))
	if err != nil {
		return err
	}

	var res RES_ROLL
	err = json.Unmarshal(buff, &res)
	if err != nil {
		return err
	}
	if res.Code != 200 {
		return fmt.Errorf("code:%d", res.Code)
	}
	return nil
}
