package push

import (
	"encoding/json"
	"errors"
	"fmt"
	"git.u8t.cn/open/gosdk/util"
	log "github.com/sirupsen/logrus"
	"github.com/smbrave/goutil"
	"github.com/spf13/cast"
	"time"
)

var (
	ErrCidNotExist = errors.New("cid not exist")
)

type GetuiConfig struct {
	AppId        string
	AppKey       string
	MasterSecret string
	XMChannelId  string
}

type GetuiStatus struct {
	Status        string
	LastLoginTime int64
}

type GetuiDetail struct {
	ClientAppId        string
	PackageName        string
	DeviceToken        string
	PhoneType          int
	PhoneModel         string
	NotificationSwitch bool
	CreateTime         int64
	LoginFreq          int
	Brand              string
}

type GetuiMessage struct {
	Cid      string
	Title    string //通知消息标题,长度 ≤ 50字
	Message  string //通知消息内容,长度 ≤ 256字
	NotifyId string
	Intent   string
}

type GetuiPushRsp struct {
	TaskId string `json:"task_id"`
	Status string `json:"status"`
}

type Getui struct {
	config      *GetuiConfig
	token       string
	tokenExpire int64
}

func NewGetui(c *GetuiConfig) *Getui {
	return &Getui{
		config:      c,
		tokenExpire: 0,
	}
}

func (g *Getui) getResult(rspBody []byte) (map[string]interface{}, error) {
	result := make(map[string]interface{})
	if err := json.Unmarshal(rspBody, &result); err != nil {
		log.Errorf("json[%s] error :%s", string(rspBody), err.Error())
		return nil, err
	}
	code := cast.ToInt(result["code"])
	msg := cast.ToString(result["msg"])
	data := cast.ToStringMap(result["data"])

	if code == 20001 && msg == "successed_ignore" {
		return data, nil
	}

	if code != 0 {
		log.Errorf("json[%s] rsp error", string(rspBody))
		return nil, errors.New(string(rspBody))
	}
	return data, nil
}

func (g *Getui) Token() string {
	nowTime := time.Now().Unix()
	if nowTime < g.tokenExpire {
		return g.token
	}

	timestamp := cast.ToString(time.Now().UnixMilli())
	signStr := fmt.Sprintf("%s%s%s", g.config.AppKey, timestamp, g.config.MasterSecret)
	reqUrl := fmt.Sprintf("https://restapi.getui.com/v2/%s/auth", g.config.AppId)
	params := make(map[string]string)
	params["timestamp"] = timestamp
	params["appkey"] = g.config.AppKey
	params["sign"] = util.Sha256(signStr)
	reqBody, _ := json.Marshal(params)
	rspBody, err := HttpPostJson(reqUrl, nil, reqBody)
	if err != nil {
		log.Errorf("http post [%s] error :%s", string(reqBody), err.Error())
		return ""
	}

	result, err := g.getResult(rspBody)
	if err != nil {
		log.Errorf("get Result error :%s", err.Error())
		return ""
	}

	token := cast.ToString(result["token"])
	tokenExpire := cast.ToInt64(result["expire_time"])
	g.token = token
	g.tokenExpire = tokenExpire / 1000
	return g.token
}

func (g *Getui) Push(req *GetuiMessage) (*GetuiPushRsp, error) {
	if len(req.Title) > 50 {
		req.Title = string([]rune(req.Title)[:16])
	}
	if len(req.Message) > 256 {
		req.Message = string([]rune(req.Message)[:80])
	}
	reqUrl := fmt.Sprintf("https://restapi.getui.com/v2/%s/push/single/cid", g.config.AppId)

	params := make(map[string]interface{})
	params["request_id"] = time.Now().Format("20060102150405") + "_" + goutil.RandomStr(6)
	params["settings"] = map[string]interface{}{
		"ttl": 2 * 3600 * 24 * 1000,
	}
	params["audience"] = map[string]interface{}{
		"cid": []string{req.Cid},
	}
	params["push_message"] = map[string]interface{}{
		"notification": map[string]interface{}{
			"title":         req.Title,
			"body":          req.Message,
			"channel_level": 4,
			"click_type":    "intent",
			"notify_id":     cast.ToInt64(req.NotifyId),
			"intent":        req.Intent,
		},
	}
	params["push_channel"] = map[string]interface{}{
		"android": map[string]interface{}{
			"ups": map[string]interface{}{
				"notification": map[string]interface{}{
					"title":      req.Title,
					"body":       req.Message,
					"click_type": "intent",
					"notify_id":  cast.ToInt64(req.NotifyId),
					"intent":     req.Intent,
				},
				"options": map[string]interface{}{
					"ALL": map[string]interface{}{
						"channel": "default",
					},
					"XM": map[string]interface{}{
						"/extra.channel_id": g.config.XMChannelId,
					},
					"VV": map[string]interface{}{
						"/category": "ACCOUNT",
					},
				},
			},
		},
	}
	reqBody, err := json.Marshal(params)
	if err != nil {
		log.Errorf("json.Marshal error :%s", err.Error())
		return nil, err
	}

	rspBody, err := HttpPostJson(reqUrl, map[string]string{"token": g.Token()}, reqBody)
	if err != nil {
		log.Errorf("goutil http post error :%s", err.Error())
		return nil, err
	}
	data, err := g.getResult(rspBody)
	if err != nil {
		log.Errorf("get result[%s] error :%s", rspBody, err.Error())
		return nil, err
	}

	rsp := new(GetuiPushRsp)
	for taskId, task := range data {
		rsp.TaskId = taskId
		cidStatus := cast.ToStringMap(task)
		for _, v := range cidStatus {
			rsp.Status = cast.ToString(v)
		}
	}
	return rsp, nil
}

func (g *Getui) GetUserStatus(cid string) (*GetuiStatus, error) {
	reqUrl := fmt.Sprintf("https://restapi.getui.com/v2/%s/user/status/%s", g.config.AppId, cid)
	rspBody, err := HttpGet(reqUrl, map[string]string{
		"token": g.Token(),
	})

	if err != nil {
		log.Warningf("goutil http get error :%s", err.Error())
		return nil, err
	}
	result, err := g.getResult(rspBody)
	if err != nil {
		log.Errorf("get result[%s] error :%s", string(rspBody), err.Error())
		return nil, err
	}
	if _, ok := result[cid]; !ok {
		return nil, ErrCidNotExist
	}

	status := new(GetuiStatus)
	cidResult := cast.ToStringMap(result[cid])
	status.Status = cast.ToString(cidResult["status"])
	status.Status = cast.ToString(cidResult["status"])
	status.LastLoginTime = cast.ToInt64(cidResult["last_login_time"]) / 1000
	return status, nil
}

func (g *Getui) GetUserDetail(cid string) (*GetuiDetail, error) {
	reqUrl := fmt.Sprintf("https://restapi.getui.com/v2/%s/user/detail/%s", g.config.AppId, cid)
	rspBody, err := HttpGet(reqUrl, map[string]string{
		"token": g.Token(),
	})

	if err != nil {
		log.Errorf("goutil http get error :%s", err.Error())
		return nil, err
	}
	result, err := g.getResult(rspBody)
	if err != nil {
		log.Errorf("get result[%s] error :%s", string(rspBody), err.Error())
		return nil, err
	}
	validCids := cast.ToStringMap(result["validCids"])
	if _, ok := validCids[cid]; !ok {
		return nil, ErrCidNotExist
	}
	cidDetail := cast.ToStringMap(validCids[cid])

	detail := new(GetuiDetail)
	detail.ClientAppId = cast.ToString(cidDetail["client_app_id"])
	detail.PackageName = cast.ToString(cidDetail["package_name"])
	detail.DeviceToken = cast.ToString(cidDetail["device_token"])
	detail.PhoneModel = cast.ToString(cidDetail["phone_model"])
	detail.Brand = cast.ToString(cidDetail["brand"])
	detail.PhoneType = cast.ToInt(cidDetail["phone_type"])
	detail.LoginFreq = cast.ToInt(cidDetail["login_freq"])
	detail.NotificationSwitch = cast.ToBool(cidDetail["notification_switch"])

	return detail, nil
}