package addata

import (
	"encoding/json"
	"fmt"
	"html"
	"io/ioutil"
	"math/rand"
	"miads/adslib"
	"miads/adslib/device"
	"miads/adslib/redis_data"
	"miads/adslib/utils"
	"net/http"
	"net/url"
	"strconv"
	"strings"
)

type XiaomiAdData struct {
	DetailURL   string `json:"detail_url"`
	DisplayType struct {
		Delay    int64  `json:"delay"`
		Name     string `json:"name"`
		RowCount int64  `json:"row_count"`
	} `json:"display_type"`
	Duration   int64  `json:"duration"`
	EmcType    string `json:"emc_type"`
	ID         string `json:"id"`
	ImageURL   string `json:"image_url"`
	Proportion string `json:"proportion"`
	Settings   struct {
		ClickType   string `json:"click_type"`
		HideCloseAt int64  `json:"hide_close_at"`
		LogTime     string `json:"log_time"`
		ShowCloseAt int64  `json:"show_close_at"`
	} `json:"settings"`
	SkipTime        int64    `json:"skip_time"`
	TagID           string   `json:"tag_id"`
	Target          string   `json:"target"`
	TargetAddition  []string `json:"target_addition"`
	TargetAddition1 string   `json:"target_addition1"`
	Title           string   `json:"title"`
	VideoURL        string   `json:"video_url"`
}

type XiaoMiAdInfoRsp struct {
	Data   []XiaomiAdData `json:"data"`
	Msg    string         `json:"msg"`
	Result int64          `json:"result"`
}

type AdAction struct {
	Type     string   `json:"type"`
	Duration int      `json:"duration"`
	Urls     []string `json:"urls"`
}

type AdInfo struct {
	AdActions    []AdAction `json:"ads_infos"`
	PercentBegin int        `json:"percent_begin"`
	PercentEnd   int        `json:"percent_end"`
}

type AdData struct {
	TargetAddition []AdAction `json:"target_addition"`
	Target         string
	ImageUrl       string `json:"image_url"`
	Duration       int64
	VideoUrl       string `json:"video_url"`
	JsOrderId      int64  `json:"js_order_id"`
	UserAgent      string `json:"user_agent"`
	DpReport       string `json:"dp_report"`
	Dp             string `json:"dp"`
	OrderName      string `json:"order_name"`
	Result         int    `json:"result"`
	Msg            string `json:"msg"`
}

func getAdsReqUrl(dsp *utils.DspParam) string {
	reqUrl := "https://m.video.xiaomi.com/api/a3/otv_emc?"
	ref := "yilin"
	sn := "05817a33d4210ad2c67f4b869b5eedde"
	// 组装 emcp
	reqUrl = reqUrl + "_emcp=pre_play"
	// 组装 devid
	reqUrl = reqUrl + "&_devid=" + dsp.RealMd5Imei
	//  组装 ref
	reqUrl = reqUrl + "&ref=" + ref
	// 组装 sn
	reqUrl = reqUrl + "&_sn=" + sn
	// 组装 ip
	reqUrl = reqUrl + "&__ip__=" + dsp.Ip
	rad := rand.Intn(100)
	if rad < 15 {
		dsp.SendPhoneType = 1
	} else {
		dsp.SendPhoneType = 0
	}
	reqUrl = reqUrl + fmt.Sprintf("&_phonetype=%d", dsp.SendPhoneType)
	return reqUrl
}

// 获取广告信息
func GetAdsInfos(dsp *utils.DspParam, advertiser string) (*AdData, error) {
	reqUrl := getAdsReqUrl(dsp)

	if dsp.SendPhoneType == 0 {
		device.SetAdsTagLog("xiaomi", dsp.ReqSource, "ADS_XIAOMI", dsp.DspCityCode)
	} else if dsp.SendPhoneType == 1 {
		device.SetAdsTagLog("xiaomi", dsp.ReqSource, "ADS_XIAOMI_1", dsp.DspCityCode)
	}

	client := &http.Client{}
	fmt.Printf("req url: %s\n", reqUrl)
	req, err := http.NewRequest("GET", reqUrl, nil)
	if err != nil {
		return nil,  err
	}

	req.Header.Set(",authority", "m.video.xiaomi.com")
	req.Header.Set(",method", "GET")
	req.Header.Set(",path", reqUrl[26:])
	req.Header.Set(",scheme", "https")
	req.Header.Set("content-type", "application/json")
	req.Header.Set("accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3")
	req.Header.Set("accept-encoding", "gzip, deflate, br")
	req.Header.Set("accept-language", "zh-CN,zh;q=0.9")
	req.Header.Set("cache-control", "max-age=0")
	req.Header.Set("upgrade-insecure-requests", "1")
	req.Header.Set("user-agent", dsp.Ua)

	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	fmt.Printf("rsp body: %s\n", body)
	if err != nil {
		return nil,  err
	}
	rsp := XiaoMiAdInfoRsp{}
	err = json.Unmarshal(body, &rsp)
	if err != nil {
		return nil, err
	}

	adsDataNum := len(rsp.Data)
	xiaomiResponseAction := map[string]int{
		"xiaomi_view": 0, "xiaomi_click": 0, "xiaomi_close": 0, "xiaomi_video_finish": 0, "xiaomi_video_timer": 0,
	}

	if rsp.Result != 1 {
		return nil, nil
	}

	// bom渠道不处理
	if dsp.ReqSource != "bom" {
		rad := rand.Intn(100)
		if rad < 2 {
			dsp.ReqSource = "kk_h5"
		} else if rad < 4 {
			dsp.ReqSource = "day_09"
		} else if rad < 8 {
			dsp.ReqSource = "fu008"
		} else if rad < 9 {
			dsp.ReqSource = "yzh01"
		} else if rad < 11 {
			dsp.ReqSource = "qihuang88"
		}
	}

	if len(rsp.Data) == 0 {
		return nil, nil
	}
	///last_infos = xiaomi_mix.xiaomi_fuse(data, dsp.req_source)

	dataInfo := rsp.Data[0]
	dsp.AllDuration = dataInfo.Duration
	dsp.VideoTimeDuration = 7

	showUrls := make([]string, 0, 100)
	clickUrls := make([]string, 0, 100)
	closeUrls := make([]string, 0, 100)
	videoFinishUrls := make([]string, 0, 100)
	videoTimerUrls := make([]string, 0, 100)

	for _, target := range rsp.Data[0].TargetAddition {
		target := html.UnescapeString(target)
		targetParseUrl, _ := url.Parse(target)
		if err != nil {
			fmt.Printf("parse url failed, url: %s, err: %s", target, err)
			continue
		}

		targetParams := targetParseUrl.Query()

		targetUrl := targetParams.Get("url")
		event := targetParams.Get("event")

		if targetUrl != "" {
			switch event {
			case "VIEW":
				showUrls = append(showUrls, targetUrl)
				xiaomiResponseAction["xiaomi_view"] = 1
			case "CLICK":
				clickUrls = append(clickUrls, targetUrl)
				xiaomiResponseAction["xiaomi_click"] = 1
			case "CLOSE":
				closeUrls = append(closeUrls, targetUrl)
				xiaomiResponseAction["xiaomi_close"] = 1
			case "VIDEO_FINISH":
				videoFinishUrls = append(videoFinishUrls, targetUrl)
				xiaomiResponseAction["xiaomi_video_finish"] = 1
			case "VIDEO_TIMER":
				videoTimerUrls = append(videoTimerUrls, targetUrl)
			}
		}

		if event == "VIDEO_TIMER" {
			timeStr := targetParams.Get("time")
			if timeStr != "" {
				videoTimeDuration, _ := strconv.Atoi(timeStr)
				dsp.VideoTimeDuration = videoTimeDuration
				xiaomiResponseAction["xiaomi_video_timer"] = 1
			}
		}
	}

	gotoUrls := make([]string, 0, 100)
	target := rsp.Data[0].Target
	if target != "" {
		targetUrlObj, _ := url.Parse(target)
		targetParams := targetUrlObj.Query()
		targetUrl := targetParams.Get("link_url")
		if targetUrl == "" {
			targetUrl = strings.ReplaceAll(targetUrl, "mv:", "")
		}

		targetUrl = html.UnescapeString(targetUrl)
		if targetUrl != "" {
			gotoUrls = append(gotoUrls, targetUrl)
		}
	}
	if dsp.ReqSource == "mh" {
		closeUrls = videoFinishUrls
	}

	videoUrls := make([]string, 0, 100)
	videoUrl := rsp.Data[0].VideoURL
	if videoUrl != "" {
		videoUrls = append(videoUrls, videoUrl)
	}

	imageUrls := make([]string, 0, 100)
	imageUrl := rsp.Data[0].ImageURL
	if imageUrl != "" {
		imageUrls = append(imageUrls, imageUrl)
	}

	fmt.Printf("%+v, addata_num: %d\n", xiaomiResponseAction, adsDataNum)
	if len(gotoUrls) == 0 {
		return nil, nil
	}

	fmt.Printf("goto urls: %+v\nshow urls: %+v\nclick_urls: %+v\nclose urls: %+v\nvideo finish urls: %+v\nimagurls: %+v\nvideo urls:%+v\n",
		gotoUrls, showUrls, clickUrls, closeUrls, videoFinishUrls, imageUrls, videoUrls)

	adsData, err := CombineLastAdsInfos(dsp, advertiser, gotoUrls, showUrls, clickUrls, closeUrls, videoFinishUrls, videoTimerUrls, imageUrls, videoUrls)
	if err != nil {
		return nil, err
	}

	fmt.Printf("addata: %+v\n", adsData)

	if dsp.SendPhoneType == 0 {
		redis_data.SetAdsRequestNum(advertiser, 0)
	} else if dsp.SendPhoneType == 1 {
		redis_data.SetAdsRequestNum(advertiser, 1)
	}
	return adsData, nil
}

func getActionsConf(advertiser string) []string {
	defaultActions := []string{"VIEW", "CLICK", "CLOSE", "VIDEO_FINISH", "VIDEO_TIMER"}

	svrConf := adslib.GetConf()
	actions, ok := svrConf.AdsActions[advertiser]
	if !ok || len(actions) == 0 {
		return defaultActions
	}
	return actions
}

func getDurationConf(advertiser string) int {
	svrConf := adslib.GetConf()
	duration, ok := svrConf.AdsDuration[advertiser]
	if !ok {
		return 0
	}
	return duration
}

func getCombinationActionsConf(advertiser string) []adslib.CombinationActionsConf {
	svrConf := adslib.GetConf()
	combinationActions, ok := svrConf.CombinationActions[advertiser]
	if !ok {
		return []adslib.CombinationActionsConf{}
	}
	return combinationActions
}

// 组装最后的广告信息
func CombineLastAdsInfos(dsp *utils.DspParam, advertiser string, gotoUrls []string, showUrls []string,
	clickUrls []string, closeUrls []string, videoFinishUrls []string,
	videoTimerUrls []string, imageUrls []string, videoUrls []string) (*AdData, error) {

	// 判断是不是关掉所有Action
	actions := getActionsConf(advertiser)
	if len(actions) == 0 {
		return nil, nil
	}

	clickChannelFlag, err := redis_data.GetChannelFlag(dsp.ReqSource, "ads_click")
	if err != nil {
		return nil, err
	}

	fmt.Printf("click channel flag: %+v\n", clickChannelFlag)

	clickRandom := rand.Intn(100)
	needClick := false
	if clickChannelFlag.ChannelFlag == 1 && clickRandom <= clickChannelFlag.Weigth {
		needClick = true
	}

	lastInfos := make([]AdInfo, 0, 50)
	combinationActions := getCombinationActionsConf(advertiser)
	for _, combAction := range combinationActions {
		adActionDatas := make([]AdAction, 0, 20)
		percentBegin := combAction.PercentBegin
		percentEnd := combAction.PercentEnd
		actionGroup := combAction.GroupValue
		for _, action := range actionGroup {
			urls := make([]string, 0, 50)
			if dsp.ReqSource == "mh" {
				if action == "CLOSE" {
					action = "VIDEO_FINISH"
				}
			}
			switch action {
			case "VIEW":
				urls = showUrls
			case "CLICK":
				urls = clickUrls
			case "CLOSE":
				urls = closeUrls
			case "VIDEO_FINISH":
				urls = videoFinishUrls
			case "VIDEO_TIMER":
				urls = videoTimerUrls
			default:
				continue
			}

			// click的控制 控制bom
			if action == "CLICK" && dsp.RealReqSource == "bom" && dsp.SupClickFlag == 0 {
				continue
			}
			// show的控制 控制bom
			if action == "VIEW" && dsp.RealReqSource == "bom" && dsp.SupShowFlag == 0 {
				continue
			}
			if action == "CLICK" && !needClick {
				continue
			}

			if len(urls) == 0 && action == "VIDEO_TIMER" {
				continue
			}

			adActionData, err := genAdsAction(urls, advertiser, dsp, action, true)
			if err != nil {
				fmt.Printf("gen ads action data failed, err: %s\n", err)
				continue
			}
			// 没有url的就不要下发了
			if len(adActionData.Urls) == 0 {
				continue
			}

			adActionDatas = append(adActionDatas, *adActionData)
		}

		minValue := 2
		if len(videoTimerUrls) != 0 {
			minValue = 3
		}

		// 大于minValue才进行投放
		if len(adActionDatas) >= minValue {
			adInfo := AdInfo{
				AdActions:    adActionDatas,
				PercentBegin: percentBegin,
				PercentEnd:   percentEnd,
			}
			lastInfos = append(lastInfos, adInfo)
		}
	}

	targetUrl := ""
	if len(gotoUrls) != 0 {
		targetUrl = gotoUrls[0]
	}

	videoUrl := ""
	if len(videoUrls) != 0 {
		videoUrl = videoUrls[0]
	}

	randAdData := make([]AdAction, 0, 50)
	if len(lastInfos) != 0 {
		randIdx := rand.Intn(len(lastInfos))
		randAdData = lastInfos[randIdx].AdActions
	}

	hasClickAction := false
	for _, adData := range randAdData {
		if adData.Type == "CLICK" {
			hasClickAction = true
			break
		}
	}

	if !hasClickAction {
		targetUrl = ""
	}

	// 重新组装duration
	videoTimerUrls = make([]string, 0, 100)
	if len(randAdData) >= 3 {
		if randAdData[1].Type == "VIDEO_TIMER" {
			videoTimerUrls = randAdData[1].Urls
		}
	}

	rspAdDatas := make([]AdAction, 0, 20)
	for _, adData := range randAdData {
		if len(adData.Urls) == 0 || adData.Type == "" {
			continue
		}

		if adData.Type == "VIDEO_TIMER" && len(videoTimerUrls) > 0 {
			adData.Duration = int(dsp.AllDuration) - dsp.VideoTimeDuration
		} else if adData.Type == "VIDEO_TIMER" && len(videoTimerUrls) == 0 {
			adData.Duration = dsp.VideoTimeDuration
		} else if adData.Type == "VIEW" {
			if len(videoTimerUrls) > 0 {
				adData.Duration = dsp.VideoTimeDuration
			} else {
				adData.Duration = int(dsp.AllDuration)
			}
		}

		if adData.Type != "VIEW" {
			redis_data.SetDistributeActionNum(advertiser, adData.Type, dsp.SendPhoneType)
		}

		canReport, err := redis_data.GetDeviceIpReport(dsp.Imei, dsp.Ip)
		if err != nil {
			return nil, err
		}

		md5Imei := ""
		// 返回的曝光, 如果没有返回过秒针的，那么加入
		if adData.Type == "VIEW" && canReport {
			if dsp.ReplaceFlag == 0 {
				md5Imei = utils.Md5(dsp.Imei)
			} else {
				md5Imei = dsp.Imei
			}

			conf := adslib.GetConf()
			url := strings.ReplaceAll(conf.HostMiao, "__IMEI__", md5Imei)
			adData.Urls = append(adData.Urls, url)
			redis_data.SetDeviceIpReport(dsp.Imei, dsp.Ip)
		}
		rspAdDatas = append(rspAdDatas, adData)
	}

	adData := AdData{
		TargetAddition: rspAdDatas,
		Target:         targetUrl,
		Duration:       dsp.AllDuration,
		VideoUrl:       videoUrl,
	}
	return &adData, nil
}

// 组装展示
func genAdsAction(urls []string, advertiser string,
	dsp *utils.DspParam, action string, needControl bool) (*AdAction, error) {
	// 获取advertiser上报的总量
	allSendNum, err := redis_data.GetAdsRequestNum(advertiser, dsp.SendPhoneType)
	if err != nil {
		return nil, err
	}
	allShowNum, err := redis_data.GetAdsFeedbackNum(advertiser, action, dsp.SendPhoneType)
	if err != nil {
		return nil, err
	}

	flowControlConf := redis_data.GetFlowPercentDuration(advertiser, action)
	fmt.Printf("%+v\n", flowControlConf)

	flowPercent := 0
	duration := 0
	if flowControlConf != nil {
		flowPercent = flowControlConf.Percent
		duration = flowControlConf.Duration
	}

	lastUrls := make([]string, 0, 20)
	if !needControl || allSendNum == 0 || int(
		float32(allShowNum)/float32(allSendNum)*100) < flowPercent {
		reportUrl := getReportUrl(action, advertiser, dsp)
		lastUrls = append(urls, reportUrl)
	}

	fmt.Printf("%+v\n", lastUrls)

	if len(lastUrls) == 0 {
		duration = 0
	}
	if action == "VIDEO_TIMER" {
		duration = 7
	}
	adAction := AdAction{
		Type:     action,
		Duration: duration,
		Urls:     lastUrls,
	}
	return &adAction, nil
}

// 组装上报的url
func getReportUrl(action string, advertiser string, dsp *utils.DspParam) string {
	urlHost := adslib.GetConf().HostIos
	url := fmt.Sprintf("%s?action=%s&advertiser=video&req_source=%s&brand=%s&city_code=%d&request_id=%s&spefial_flag=%d",
		urlHost, action, dsp.ReqSource, dsp.Brand, dsp.DspCityCode, dsp.RequestId, dsp.SendPhoneType)
	return url
}
