package cache

import (
	"q5"
	"f5"
	"main/mt"
	"main/common"
)

type cacheMgr struct {
	nftHash *q5.ConcurrentMap[string, *nft] //net_id contract_address token_id -> nft
}

func (this *cacheMgr) Init() {
	this.nftHash = new(q5.ConcurrentMap[string, *nft])
}

func (this *cacheMgr) UnInit() {

}

func (this *cacheMgr) GetNfts(nfts []*common.NftDto) {
	{
		missNfts := []*common.NftDto{}
		for _, val := range nfts {
			this.fastGetNft(val)
			if val.NftCache == nil {
				if len(missNfts) < 20 {
					q5.AppendSlice(&missNfts, val)
				}
			}
		}
		//this.batchGetNfts(missNfts)
	}

	for _, val := range nfts {
		this.internalGetNft(val)
	}
}

func (this *cacheMgr) GetIngameHero(accountId string) (error, []interface{}) {
	rspObj := struct {
		ErrCode interface{} `json:"errcode"`
		ErrMsg string `json:"errmsg"`
		Rows []interface{}
	}{}
	var err error
	rows := []interface{}{}
	f5.GetHttpCliMgr().SendGoStyleRequest(
		mt.Table.Config.GetGameApiUrl() + "/webapp/index.php",
		map[string]string{
			"c": "OutAppNft",
			"a": "getHeroList",
			"account_id": accountId,
		},
		func (rsp f5.HttpCliResponse) {
			err = rsp.GetErr()
			if rsp.GetErr() != nil {
				return
			}
			err = q5.DecodeJson(rsp.GetRawData(), &rspObj)
			if err != nil {
				return
			}
		})
	if err == nil {
		if q5.SafeToInt32(rspObj.ErrCode) == 0 {
			rows = rspObj.Rows
		}
	}
	return err, rows
}

func (this *cacheMgr) fastGetNft(dto *common.NftDto) {
	key := dto.GetKey()
	if p, ok := this.nftHash.Load(key); ok {
		dto.NftCache = *p
	}
}

func (this *cacheMgr) internalGetNft(dto *common.NftDto) {
	key := dto.GetKey()
	if p, ok := this.nftHash.Load(key); ok {
		if q5.GetTickCount() - (*p).refreshTick < 1000 * 30 {
			dto.NftCache = *p
			return
		}
		this.nftHash.Delete(key)
		if !f5.IsOnlineEnv() {
			f5.GetSysLog().Warning("remove cache %s", dto.TokenId)
		}
	}
	l := f5.AllocLock(key)
	defer f5.ReleaseLock(l)
	if p, ok := this.nftHash.Load(key); ok {
		dto.NftCache = *p
	} else {
		f5.GetHttpCliMgr().SendGoStyleRequest(
			mt.Table.Config.GetGameApiUrl() + "/webapp/index.php",
			map[string]string{
				"c": "OutAppNft",
				"a": "nftDetailByContractAddress",
				"contract_address": dto.ContractAddress,
				"net_id": q5.ToString(dto.NetId),
				"token_id": dto.TokenId,
			},
			func (rsp f5.HttpCliResponse) {
				if rsp.GetErr() == nil {
					jsonRsp := map[string]interface{}{}
					q5.DecodeJson(rsp.GetRawData(), &jsonRsp)
					dto.NftCache = this.addNft(dto.NetId, dto.ContractAddress, dto.TokenId, jsonRsp)
				}
			})
	}
}

func (this *cacheMgr) batchGetNfts(nfts []*common.NftDto) {
	keys := []string{}
	for _, val := range nfts {
		q5.AppendSlice(&keys, val.GetKey())
	}
	if len(keys) <= 0 {
		return
	}
	locks := f5.BatchAllocLock(keys)
	defer f5.BatchReleaseLock(locks)
	rspObj := struct {
		ErrCode interface{} `json:"errcode"`
		ErrMsg string `json:"errmsg"`
		Nfts []map[string]interface{} `json:"nfts"`
		/*
		Nfts [] struct {
			NetId interface{} `json:"errmsg"`
			ContractAddress string `json:"contract_address"`
			TokenId interface{} `json:"token_id"`
		} `json:"nfts"`
		*/
	}{}
	params := struct {
		Nfts []struct {
			NetId string `json:"net_id"`
			ContractAddress string `json:"contract_address"`
			TokenId string `json:"token_id"`
		} `json:"nfts"`
	}{}
	paramsStr := q5.EncodeJson(params)
	f5.GetHttpCliMgr().SendGoStyleJsonRspPost(
		mt.Table.Config.GetGameApiUrl() + "/webapp/index.php",
		map[string]string{
			"c": "OutAppNft",
			"a": "nftDetailList",
		},
		&rspObj,
		"application/json",
		paramsStr,
		func (rsp f5.HttpCliResponse) {
			if rsp.GetErr() != nil {
				return
			}
			if rsp.JsonParseOk() {
				for _, val := range rspObj.Nfts {
					var netId int32
					var contractAddress string
					var tokenId string
					if p, ok := val["net_id"]; ok {
						netId = q5.SafeToInt32(p)
					} else {
						continue
					}
					if p, ok := val["contract_address"]; ok {
						contractAddress = q5.SafeToString(p)
					} else {
						continue
					}
					if p, ok := val["token_id"]; ok {
						tokenId = q5.SafeToString(p)
					} else {
						continue
					}
					this.addNft(netId, contractAddress, tokenId, val)
				}
			}
		})
}

func (this *cacheMgr) addNft(netId int32, contractAddress string, tokenId string,
	data map[string]interface{}) *nft {
	dto := new(common.NftDto)
	dto.NetId = netId
	dto.ContractAddress = contractAddress
	dto.TokenId = tokenId
	p := newNft()
	p.jsonData = data
	this.nftHash.Store(dto.GetKey(), p)
	return p
}
