package ad

import (
	"encoding/base64"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log"
	"ssp/blowfishClient"
	"ssp/constants"
	"ssp/curl"
	"ssp/customapi"
	"ssp/logger"
	"ssp/mongoClient"
	"ssp/redisClient"
	"ssp/request25"
	"ssp/request30"
	"ssp/vastxml"
	"strconv"
	"strings"
	"time"

	//~ b64 "encoding/base64"
	"openrtb/openrtb3"
	//~ "math/rand"
	openrtb "openrtb/openrtb2.5"
)

type bidderResponse struct {
	Bidderid       int
	At             int     `json:"at"`
	SeconBidBuffer float64 `json:"seconBidBuffer"`
	Response       interface{}
}

type result struct {
	cr customapi.CustomResponse
	p  float64
}

// Function for processing ad
func Ssp_adprocessing(channel chan map[string]interface{}, req *customapi.CustomRequest, redisclient *redisClient.RedisClient) {
	var (
		i          int
		req25      *openrtb.BidRequest
		req30      *openrtb3.Request
		jsonReq    []byte
		err        error
		responseId string
		zoneid     string
	)
	zoneid = req.Site.ID
	output := make(map[string]interface{})
	arrRes := make([]bidderResponse, len(req.Dsp))
	req25Channel := make(chan *openrtb.BidRequest)
	req30Channel := make(chan *openrtb3.Request)
	go request25.FormCommonObjects(req25Channel, req)
	go request30.FormCommonObjects(req30Channel, req)
	req25 = <-req25Channel
	req30 = <-req30Channel

	for _, v := range req.Dsp {
		if v.Ver == "3.0" {
			req30.TMax = int64(req.TMax)
			req30.Cur = v.Cur
			req30.AT = openrtb3.AuctionType(v.AT)
			// Source Object
			if v.Source != nil {
				var src openrtb3.Source
				src.TID = v.Source.TID
				src.TS = v.Source.TS
				src.DS = v.Source.DS
				req30.Source = &src
			}

			// Ext Object
			if v.Ext != nil {

				var (
					ext    customapi.Ext
					adstxt customapi.Ads_txt
					google customapi.Google
					dv     customapi.Detected_vertical
				)
				ext.SSP = v.Ext.SSP
				ext.At = v.Ext.At
				if v.Ext.Ads_txt != nil {
					adstxt.Auth_id = v.Ext.Ads_txt.Auth_id
					adstxt.Pub_id = v.Ext.Ads_txt.Pub_id
					adstxt.Status = v.Ext.Ads_txt.Status
					ext.Ads_txt = &adstxt
				}

				if v.Ext.Google != nil {
					if v.Ext.Google.Detected_vertical != nil {
						dv.Id = v.Ext.Google.Detected_vertical.Id
						dv.Weight = v.Ext.Google.Detected_vertical.Weight
						google.Detected_vertical = &dv
						ext.Google = &google
					}
				}

				mainext, err := json.Marshal(ext)
				if err != nil {
					logger.Log.Println("Error ", err.Error())
				}
				req30.Ext = json.RawMessage(mainext)
			}
			jsonReq, err = json.Marshal(req30)
			if err != nil {
				logger.Log.Println("Error :", err.Error())
			}
		} else {
			req25.TMax = req.TMax
			req25.Cur = v.Cur
			req25.AuctionType = v.AT
			// Source Object
			if v.Source != nil {
				var src openrtb.Source
				src.TransactionID = v.Source.TID
				req25.Source = &src
			}
			jsonReq, err = json.Marshal(req25)
			if err != nil {
				logger.Log.Println("Error :", err.Error())
			}
		}

		insertReq := make(chan bool)
		go mongoClient.MongoSaveRequest(insertReq, req.ID, jsonReq, v.ID, v.Ver)

		if <-insertReq != true {
			logger.Log.Println("Error : Request not inserted in Mongodb")
		}
		curlChannel := make(chan []byte)

		fmt.Println("Req", string(jsonReq))

		go curl.SendRequest(curlChannel, jsonReq, v.Ping_url, v.Ver, req.TMax, v.Gzip)
		response := <-curlChannel

		if len(response) > 0 {

			if v.Ver == "3.0" {
				res30 := &openrtb3.Response{}
				err = json.Unmarshal(response, res30)
				if err != nil {
					logger.Log.Println("Error :", err.Error())
				}
				if res30 != nil {
					responseId = res30.ID
					arrRes[i].Bidderid = v.ID
					arrRes[i].Response = res30
					arrRes[i].At = v.AT
					arrRes[i].SeconBidBuffer = v.SeconBidBuffer
				}

			} else {

				res25 := &openrtb.BidResponse{}

				fmt.Println("Res", string(response))

				err = json.Unmarshal(response, res25)

				if err != nil {
					fmt.Println(err)
					logger.Log.Println("Error :", err.Error())
				}
				if res25 != nil {
					responseId = res25.ID
					arrRes[i].Bidderid = v.ID
					arrRes[i].At = v.AT
					arrRes[i].SeconBidBuffer = v.SeconBidBuffer
					arrRes[i].Response = res25

				}

			}
			i++
			insertRes := make(chan bool)

			go mongoClient.MongoSaveResponse(insertRes, responseId, req.ID, jsonReq, v.ID, v.Ver)

			if <-insertRes != true {
				logger.Log.Println("Error : Response not inserted in Mongodb")
			}
		}
	}

	res, lnotices := GetHighestBid(arrRes)

	for k, v := range res {

		ch := make(chan string, 1)

		go replaceMacros(ch, v, v.NURL)

		v.NURL = constants.AppProtocol + constants.AppHost + constants.AppPort + constants.WinNoticeEndPoint + "?id=" + v.ID + "&bidderid=" + strconv.Itoa(v.Bidderid) + "&nurl=" + base64.StdEncoding.EncodeToString([]byte(<-ch))

		curl.SendWNotices(v.NURL)

		go mongoClient.MongoSaveWin(responseId, jsonReq, v.Bidderid, float64(v.Price)/float64(1000))

		go replaceMacros(ch, v, v.BURL)

		v.Adm = strings.Replace(v.Adm, "{ad_network_id}", strconv.Itoa(v.Bidderid), -1)
		v.Adm = strings.Replace(v.Adm, "{days}", "30", -1)

		v.BURL = constants.AppProtocol + constants.AppHost + constants.AppPort + constants.BillingNoticeEndPoint + "?id=" + v.ID + "&bidderid=" + strconv.Itoa(v.Bidderid) + "&burl=" + base64.StdEncoding.EncodeToString([]byte(<-ch))
		if strings.Contains(v.Adm, "<VAST") {

			key := v.AdID + "-" + v.CID + "-" + v.ID
			logurl := constants.AppProtocol + constants.AppHost + constants.AppPort + constants.ImpUrlEndPoint + "?bannerid=" + v.AdID + "&campaignid=" + v.CID + "&zoneid=0&id=" + v.ID
			vasturl := constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastWrapperEndPoint + "?key=" + key

			go vastxml.SaveVastXml(v.Adm, key, redisclient)
			log.Println(vasturl)


			if strings.Contains(v.Adm,"<Linear") && strings.Contains(v.Adm, "<InLine>") {

			v.Adm = `<VAST version="4.1"><Ad id="` + v.AdID + `:0.0-0" >
						<Wrapper>
							<AdSystem><![CDATA[` + constants.AppName + `]]></AdSystem>
							<Impression>
	   							<![CDATA[` + logurl + `]]>
							</Impression>
	 						<Impression>
	   							<![CDATA[` + v.BURL + `]]>
							</Impression>
							<VASTAdTagURI><![CDATA[` + vasturl + `]]></VASTAdTagURI>
							<Creatives>
								<Creative>
	 								<Linear>
										<Duration></Duration>
										<TrackingEvents>
		  									<Tracking event="start"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=start]]></Tracking>
		  									<Tracking event="firstQuartile"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=firstquartile]]></Tracking>
											<Tracking event="midpoint"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=midpoint]]></Tracking>
											<Tracking event="thirdQuartile"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=thirdquartile]]></Tracking>
											<Tracking event="complete"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=complete]]></Tracking>
											<Tracking event="pause"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=pause]]></Tracking>
											<Tracking event="mute"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=mute]]></Tracking>
											<Tracking event="fullscreen"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=fullscreen]]></Tracking>
											<Tracking event="unmute"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=unmute]]></Tracking>
											<Tracking event="creativeView"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=creativeView]]></Tracking>
											<Tracking event="acceptInvitation"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=acceptInvitation]]></Tracking>
											<Tracking event="rewind"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=rewind]]></Tracking>
											<Tracking event="resume"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=resume]]> </Tracking>
										</TrackingEvents>
									</Linear>
								</Creative>
							</Creatives>
						</Wrapper>
					</Ad></VAST>`

				
			} else if strings.Contains(v.Adm, `<NonLinearAds>`) && strings.Contains(v.Adm, `<InLine>`) {


				key1 := v.AdID + "-" + v.CID + "-over" + v.ID

				redisclient.SetKey(key1, v.Adm,time.Minute*constants.RedisExpInMin)
				vasturl1 := constants.AppProtocol + constants.AppHost + constants.AppPort  + "/VastWrapper?overlaykey=" + key1

				log.Println(vasturl1)

				v.Adm = `<VAST version="4.1"><Ad id="` + v.AdID + `:0.0-0" >
						<Wrapper>
							<AdSystem><![CDATA[` + constants.AppName + `]]></AdSystem>
							<Impression>
	   							<![CDATA[` + logurl + `]]>
							</Impression>
	 						<Impression>
	   							<![CDATA[` + v.BURL + `]]>
							</Impression>
							<VASTAdTagURI><![CDATA[` + vasturl1 + `]]></VASTAdTagURI>
							<Creatives>
								<Creative id="` + v.AdID + `" sequence="1">
	 								<NonLinearAds>
										<TrackingEvents>
		  									<Tracking event="creativeView"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=creativeView]]></Tracking>
		  									<Tracking event="adExpand"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=adExpand]]></Tracking>
											<Tracking event="adCollapse"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=adCollapse]]></Tracking>
											<Tracking event="acceptInvitation"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=acceptInvitation]]></Tracking>
											<Tracking event="close"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=close]]></Tracking>
											<Tracking event="overlayViewDuration"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=overlayViewDuration]]></Tracking>
											<Tracking event="otherAdInteraction"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=otherAdInteraction]]></Tracking>
											<Tracking event="minimize"><![CDATA[` + constants.AppProtocol + constants.AppHost + constants.AppPort + constants.VastTrackingEndPoint + `?zoneid=` + zoneid + `&exchangeid=` + strconv.Itoa(v.Bidderid) + `&event=minimize]]></Tracking>
										</TrackingEvents>
									</NonLinearAds>
									<NonLinear> </NonLinear>
								</Creative>
							</Creatives>
						</Wrapper>
					</Ad></VAST>`
				
				

			}
		}

		res[k] = v
	}

	output["ads"] = res
	channel <- output
	if len(lnotices) > 0 {
		curl.SendLNotices(lnotices)
	}
}

func replaceMacros(ch chan string, wn customapi.CustomResponse, s string) {
	wNotice := s
	wNotice = strings.Replace(wNotice, "${AUCTION_ID}", wn.ID, -1)
	wNotice = strings.Replace(wNotice, "${OPENRTB_ID}", wn.ID, -1)
	wNotice = strings.Replace(wNotice, "${AUCTION_BID_ID}", wn.BidID, -1)
	wNotice = strings.Replace(wNotice, "${OPENRTB_BID_ID}", wn.BidID, -1)
	wNotice = strings.Replace(wNotice, "${AUCTION_IMP_ID}", wn.Imp, -1)
	wNotice = strings.Replace(wNotice, "${OPENRTB_ITEM_ID}", wn.Imp, -1)
	if strings.Contains(wNotice, "${AUCTION_PRICE:BF}") {
		fmt.Println(wn.Price)

		p := blowfishClient.BlowfishEncrypt([]byte(strconv.FormatFloat(wn.Price, 'f', 2, 64)), []byte(constants.BlowfishKey))
		fmt.Println(p)

		wNotice = strings.Replace(wNotice, "${AUCTION_PRICE:BF}", hex.EncodeToString(p), -1)
	} else {
		wNotice = strings.Replace(wNotice, "${AUCTION_PRICE}", strconv.FormatFloat(wn.Price, 'f', 2, 64), -1)
	}
	if strings.Contains(wNotice, "${OPENRTB_PRICE:BF}") {
		p := blowfishClient.BlowfishEncrypt([]byte(strconv.FormatFloat(wn.Price, 'f', 2, 64)), []byte(constants.BlowfishKey))
		wNotice = strings.Replace(wNotice, "${OPENRTB_PRICE:BF}", hex.EncodeToString(p), -1)
	} else {
		wNotice = strings.Replace(wNotice, "${OPENRTB_PRICE}", strconv.FormatFloat(wn.Price, 'f', 2, 64), -1)
	}
	wNotice = strings.Replace(wNotice, "${AUCTION_SEAT_ID}", wn.Seat, -1)
	wNotice = strings.Replace(wNotice, "${OPENRTB_SEAT_ID}", wn.Seat, -1)
	wNotice = strings.Replace(wNotice, "${AUCTION_AD_ID}", wn.AdID, -1)
	wNotice = strings.Replace(wNotice, "${OPENRTB_MEDIA_ID}", wn.AdID, -1)
	wNotice = strings.Replace(wNotice, "${AUCTION_CURRENCY}", wn.Currency, -1)
	wNotice = strings.Replace(wNotice, "${OPENRTB_CURRENCY}", wn.Currency, -1)
	wNotice = strings.Replace(wNotice, "${AUCTION_LOSS}", "102", -1)
	wNotice = strings.Replace(wNotice, "${OPENRTB_LOSS}", "102", -1)
	ch <- wNotice
}

func GetHighestBid(arrRes []bidderResponse) (map[string]customapi.CustomResponse, []string) {

	var lnotices []string
	mp := make(map[string]customapi.CustomResponse)

	for _, v := range arrRes {

		if v.Response != nil {

			var cr customapi.CustomResponse
			switch t := v.Response.(type) {

			case *openrtb3.Response:
				for _, v1 := range t.SeatBid {
					if len(v1.Bid) > 0 {
						var p float64
						ch := make(chan result, 1)
						go new30(ch, t.ID, t.BidID, t.Cur, v1, v.Bidderid)
						for res := range ch {
							p = res.p
							cr = res.cr

						}
						if val, ok := mp[v1.Bid[0].Item]; ok {

							if val.Price < p {

								if v.At == 2 {
									cr.Price = cr.Price + v.SeconBidBuffer
								}

								mp[v1.Bid[0].Item] = cr
							} else {
								ch := make(chan string, 1)
								go replaceMacros(ch, cr, v1.Bid[0].LURL)
								url := constants.AppProtocol + constants.AppHost + constants.AppPort + constants.LossNoticeEndPoint + "?id=" + t.ID + "&bidderid=" + strconv.Itoa(v.Bidderid) + "&lurl=" + base64.StdEncoding.EncodeToString([]byte(<-ch))
								lnotices = append(lnotices, url)
							}
						} else {
							ch := make(chan result, 1)
							go new30(ch, t.ID, t.BidID, t.Cur, v1, v.Bidderid)
							for res := range ch {
								p = res.p
								cr = res.cr
							}
							if v.At == 2 {
								cr.Price = cr.Price + v.SeconBidBuffer
							}

							mp[v1.Bid[0].Item] = cr
						}
					}
				}
			case *openrtb.BidResponse:
				for _, v1 := range t.SeatBid {
					if len(v1.Bid) > 0 {
						var p float64
						ch := make(chan result, 1)
						go new25(ch, t.ID, t.BidID, t.Currency, v1, v.Bidderid)
						for res := range ch {
							p = res.p
							cr = res.cr
						}
						if val, ok := mp[v1.Bid[0].ImpID]; ok {
							if val.Price < p {

								if v.At == 2 {
									cr.Price = cr.Price + v.SeconBidBuffer
								}
								mp[v1.Bid[0].ImpID] = cr
							} else {
								ch := make(chan string, 1)
								go replaceMacros(ch, cr, v1.Bid[0].LURL)
								url := constants.AppProtocol + constants.AppHost + constants.AppPort + constants.LossNoticeEndPoint + "?id=" + t.ID + "&bidderid=" + strconv.Itoa(v.Bidderid) + "&lurl=" + base64.StdEncoding.EncodeToString([]byte(<-ch))
								lnotices = append(lnotices, url)
							}
						} else {
							ch := make(chan result, 1)
							go new25(ch, t.ID, t.BidID, t.Currency, v1, v.Bidderid)
							for res := range ch {
								p = res.p
								cr = res.cr
							}
							if v.At == 2 {
								cr.Price = cr.Price + v.SeconBidBuffer
							}
							mp[v1.Bid[0].ImpID] = cr
						}
					}
				}
			}
		}
	}

	return mp, lnotices
}

func new25(ch chan result, id string, bidid string, cur string, v1 openrtb.SeatBid, bidderid int) {
	defer close(ch)
	res := new(result)
	res.p = v1.Bid[0].Price

	for _, v2 := range v1.Bid {

		if v2.Price >= res.p {
			res.cr.ID = id
			res.cr.BidID = bidid
			res.cr.Currency = cur
			res.cr.Seat = v1.Seat
			res.cr.Imp = v2.ImpID
			res.cr.AdID = v2.AdID
			res.cr.CID = v2.CampaignID
			res.cr.Adm = v2.AdMarkup
			res.p = v2.Price
			res.cr.Price = v2.Price
			res.cr.BURL = v2.BURL
			res.cr.NURL = v2.NURL
			res.cr.Bidderid = bidderid
		}
	}
	ch <- *res
}

func new30(ch chan result, id string, bidid string, cur string, v1 openrtb3.SeatBid, bidderid int) {
	defer close(ch)
	res := new(result)
	res.p = v1.Bid[0].Price
	for _, v2 := range v1.Bid {
		if v2.Price >= res.p {
			res.cr.ID = id
			res.cr.BidID = bidid
			res.cr.Currency = cur
			res.cr.Seat = v1.Seat
			res.cr.Imp = v2.Item
			if v2.Media.Display.Adm != "" {
				res.cr.Adm = v2.Media.Display.Adm
			} else if v2.Media.Video.Adm != "" {
				res.cr.Adm = v2.Media.Video.Adm
			} else if v2.Media.Audio.Adm != "" {
				res.cr.Adm = v2.Media.Audio.Adm
			}
			res.cr.AdID = v2.Media.Ad.ID
			res.cr.CID = v2.CID
			res.p = v2.Price
			res.cr.Price = v2.Price
			res.cr.BURL = v2.BURL
			res.cr.NURL = v2.NURL
			res.cr.Bidderid = bidderid
		}
	}
	ch <- *res
}
