package jxutils

import (
	"bytes"
	"crypto/md5"
	"encoding/base64"
	"errors"
	"fmt"
	beego "github.com/astaxie/beego/server/web"
	"image"
	"image/png"
	"io/ioutil"
	"math"
	"net/http"
	"reflect"
	"regexp"
	"strings"
	"sync"
	"time"

	"git.rosy.net.cn/baseapi/platformapi"
	"git.rosy.net.cn/baseapi/utils"
	"git.rosy.net.cn/jx-callback/business/model"
	"git.rosy.net.cn/jx-callback/globals"
	"github.com/boombuler/barcode"
	"github.com/boombuler/barcode/code128"
	"github.com/boombuler/barcode/qr"
)

const (
	MaxCodeWidth  = 400
	MaxCodeHeight = 400
	CodeTypeQr    = "qr"
	CodeTypeBar   = "bar"
)

var (
	storeNamePat = regexp.MustCompile(`([^(\[（【)\]）】\-\s]*)[(\[（【\-\s]+([^(\[（【)\]）】\-]*)[)\]）】]*`)
	mobilePat    = regexp.MustCompile(`^1\d{10}$`)
)

// 合并得到最终的门店状态
func MergeStoreStatus(status int, vendorStatus int) int {
	if status < vendorStatus {
		return status
	}
	return vendorStatus
}

func MergeSkuStatus(skuStatus int, storeSkuStatus int) int {
	if skuStatus < storeSkuStatus {
		return skuStatus
	}
	return storeSkuStatus
}

func SplitSlice(list interface{}, batchCount int) (listInList [][]interface{}) {
	typeInfo := reflect.TypeOf(list)
	if typeInfo.Kind() != reflect.Slice {
		panic("list must be slice")
	}
	valueInfo := reflect.ValueOf(list)
	len := valueInfo.Len()
	if len > 0 {
		listInListLen := (len-1)/batchCount + 1
		listInList = make([][]interface{}, listInListLen)
		index := 0
		for i := 0; i < len; i++ {
			if i%batchCount == 0 {
				index = i / batchCount
				arrLen := len - i
				if arrLen > batchCount {
					arrLen = batchCount
				}
				listInList[index] = make([]interface{}, arrLen)
			}
			listInList[index][i%batchCount] = valueInfo.Index(i).Interface()
		}
	}
	return listInList
}

func SplitStoreName(fullName, separator, defaultPrefix string) (prefix, bareName string) {
	names := strings.Split(fullName, separator)
	if len(names) == 2 {
		prefix = names[0]
		bareName = names[1]
	} else {
		searchResult := storeNamePat.FindStringSubmatch(fullName)
		if searchResult != nil {
			prefix = searchResult[1]
			bareName = strings.Trim(strings.Trim(searchResult[2], defaultPrefix), separator)
		} else {
			prefix = defaultPrefix
			bareName = strings.Trim(strings.Trim(fullName, defaultPrefix), separator)
		}
	}
	return TrimDecorationChar(prefix), TrimDecorationChar(bareName)
}

func ComposeStoreName(bareName string, vendorID int) (fullName string) {
	bareName = TrimDecorationChar(strings.Trim(bareName, "-"))
	storeName := globals.StoreName
	if vendorID == model.VendorIDJD {
		fullName = storeName + "-" + bareName
	} else {
		if globals.IsMainProductEnv() && model.ShopChineseNames[vendorID] != "" {
			storeName = model.ShopChineseNames[vendorID]
		}
		fullName = storeName + "(" + bareName + ")"
	}
	return fullName
}

func StrTime2JxOperationTime(strTime string, defValue int16) int16 {
	if timeValue, err := time.Parse("15:04:05", strTime); err == nil {
		return int16(timeValue.Hour()*100 + timeValue.Minute())
	}
	return defValue
}

func JxOperationTime2StrTime(value int16) string {
	return fmt.Sprintf("%02d:%02d", value/100, value%100)
}

func JxOperationTime2TimeByDate(value int16, tm time.Time) (outTm time.Time) {
	return utils.Str2TimeWithDefault(fmt.Sprintf("%s %02d:%02d:00", utils.Time2DateStr(tm), value/100, value%100), utils.DefaultTimeValue)
}

func GetPolygonFromCircle(lng, lat, distance float64, pointCount int) (points [][2]float64) {
	points = make([][2]float64, pointCount)
	for k := range points {
		angle := float64(k) * 360 / float64(pointCount)
		points[k][0], points[k][1] = ConvertDistanceToLogLat(lng, lat, float64(distance), angle)
	}
	return points
}

func GetPolygonFromCircleStr(lng, lat, distance float64, pointCount int) string {
	return CoordinatePoints2Str(GetPolygonFromCircle(lng, lat, distance, pointCount))
}

func CoordinatePoints2Str(points [][2]float64) string {
	points2 := make([]string, len(points))
	for k, v := range points {
		points2[k] = fmt.Sprintf("%.6f,%.6f", v[0], v[1])
	}
	return strings.Join(points2, ";")
}

func CoordinateStr2Points(pointsStr string) (points [][2]float64) {
	strPoints := strings.Split(pointsStr, ";")
	for _, v := range strPoints {
		strPoint := strings.Split(v, ",")
		if len(strPoint) >= 2 {
			points = append(points, [2]float64{utils.Str2Float64(strPoint[0]), utils.Str2Float64(strPoint[1])})
		}
	}
	return points
}

func IntMap2List(intMap map[int]int) []int {
	retVal := make([]int, len(intMap))
	index := 0
	for k := range intMap {
		retVal[index] = k
		index++
	}
	return retVal
}

func IntList2Map(intList []int) map[int]int {
	retVal := make(map[int]int)
	for _, v := range intList {
		retVal[v] = 1
	}
	return retVal
}

func Int64Map2List(int64Map map[int64]int) []int64 {
	retVal := make([]int64, len(int64Map))
	index := 0
	for k := range int64Map {
		retVal[index] = k
		index++
	}
	return retVal
}

func StringMap2List(stringMap map[string]int) []string {
	retVal := make([]string, len(stringMap))
	index := 0
	for k := range stringMap {
		retVal[index] = k
		index++
	}
	return retVal
}

func StringList2Map(strList []string) map[string]int {
	retVal := make(map[string]int)
	for _, v := range strList {
		retVal[v] = 1
	}
	return retVal
}

func GetSliceLen(list interface{}) int {
	return reflect.ValueOf(list).Len()
}

func RegularizeSkuQuality(specQuality float32, specUnit string) (g int) {
	lowerSpecUnit := strings.ToLower(specUnit)
	if lowerSpecUnit == "kg" || lowerSpecUnit == "l" {
		specQuality *= 1000
	}
	return int(specQuality)
}

//  计算SKU价格，unitPrice为一斤的单价，specQuality为质量，单位为克
func CaculateSkuPrice(unitPrice int, specQuality float32, specUnit string, skuNameUnit string) int {
	if skuNameUnit != model.SpecialUnit {
		return unitPrice
	}
	specQuality2 := RegularizeSkuQuality(specQuality, specUnit)
	floatPrice := float64(unitPrice) * float64(specQuality2) / float64(model.SpecialSpecQuality)
	// if specQuality2 < 250 {
	// 	floatPrice = floatPrice * 110 / 100
	// } else if specQuality2 < 500 {
	// 	floatPrice = floatPrice * 105 / 100
	// }
	if floatPrice <= 1 {
		floatPrice = 1
	}
	return int(math.Round(floatPrice))
}

//  计算SKU标准价格，CaculateSkuPrice的逆过程
func CaculateUnitPrice(skuPrice int, specQuality float32, specUnit string, skuNameUnit string) (unitPrice int) {
	if skuNameUnit != model.SpecialUnit {
		return skuPrice
	}
	specQuality2 := RegularizeSkuQuality(specQuality, specUnit)
	unitPrice = skuPrice * model.SpecialSpecQuality / specQuality2
	// if specQuality2 < 250 {
	// 	unitPrice = unitPrice * 100 / 110
	// } else if specQuality2 < 500 {
	// 	unitPrice = unitPrice * 100 / 105
	// }
	if unitPrice <= 1 {
		unitPrice = 1
	}
	return unitPrice
}

func ConstrainPricePercentage(percentage int) int {
	if percentage < model.MinVendorPricePercentage || percentage > model.MaxVendorPricePercentage {
		percentage = model.DefVendorPricePercentage
	}
	return percentage
}

func CaculateSkuActVendorPrice(price, percentage, priceAdd int) (vendorPrice int) {
	percentage = ConstrainPricePercentage(percentage)
	vendorPrice = int(math.Round(float64(price*percentage)/100)) + priceAdd
	if vendorPrice < 1 {
		vendorPrice = 1
	}
	return vendorPrice
}

func CaculateSkuVendorPrice(price, percentage, priceAdd int) (vendorPrice int) {
	percentage = ConstrainPricePercentage(percentage)
	vendorPrice = int(math.Round(float64(price*percentage)/100)) + priceAdd
	vendorPrice = int(math.Round(float64(vendorPrice)/10) * 10)
	if vendorPrice < 1 {
		vendorPrice = 1
	}
	return vendorPrice
}

func CaculateSkuPriceFromVendor(vendorPrice, percentage, priceAdd int) (price int) {
	percentage = ConstrainPricePercentage(percentage)
	price = int(math.Round(float64(vendorPrice-priceAdd) * 100 / float64(percentage)))
	if price < 1 {
		price = 1
	}
	return price
}

func GetPricePercentage(l model.PricePercentagePack, price int, defPricePercentage int) (pricePercentage, priceAdd int) {
	pricePercentage = defPricePercentage
	itemLen := len(l)
	if itemLen > 0 {
		low := 0
		high := itemLen - 1
		mid := 0
		for low <= high {
			mid = low + (high-low)/2
			if mid < 0 || mid >= itemLen-1 {
				break
			}
			if price >= l[mid].BeginPrice {
				if price < l[mid+1].BeginPrice {
					break
				} else {
					low = mid + 1
				}
			} else {
				high = mid - 1
			}
		}
		if mid >= 0 && mid <= itemLen-1 && low <= high {
			pricePercentage = l[mid].PricePercentage
			priceAdd = l[mid].PriceAdd
		}
	}
	return pricePercentage, priceAdd
}

func GetPricePercentageByVendorPrice(l model.PricePercentagePack, vendorPrice int, defPricePercentage int) (pricePercentage, priceAdd int) {
	pricePercentage = defPricePercentage
	if len(l) > 0 {
		var lastItem *model.PricePercentageItem
		for _, v := range l {
			if CaculateSkuVendorPrice(v.BeginPrice, v.PricePercentage, v.PriceAdd) > vendorPrice {
				break
			}
			lastItem = v
		}
		if lastItem != nil {
			pricePercentage = lastItem.PricePercentage
			priceAdd = lastItem.PriceAdd
		}
	}
	return pricePercentage, priceAdd
}

func CaculatePriceByPricePack(l model.PricePercentagePack, defPricePercentage, price int) (outPrice int) {
	pricePercentage, priceAdd := GetPricePercentage(l, price, defPricePercentage)
	return CaculateSkuVendorPrice(price, pricePercentage, priceAdd)
}

func CaculateJxPriceByPricePack(l model.PricePercentagePack, defPricePercentage, vendorPrice int) (jxPrice int) {
	pricePercentage, priceAdd := GetPricePercentageByVendorPrice(l, vendorPrice, defPricePercentage)
	jxPrice = CaculateSkuPriceFromVendor(vendorPrice, pricePercentage, priceAdd)
	return jxPrice
}

func ConstrainPayPercentage(payPerCentage int) int {
	if payPerCentage <= 50 {
		payPerCentage = 60
	}
	return payPerCentage
}

func IsSkuSpecial(specQuality float32, specUnit string) bool {
	return int(specQuality) == model.SpecialSpecQuality && (specUnit == model.SpecialSpecUnit || specUnit == model.SpecialSpecUnit2)
}

var lastFakeID int64
var lastFakeIDMutex sync.RWMutex

// 生成一个不重复的临时ID
func genFakeID1() int64 {
	for {
		fakeID := time.Now().UnixNano() / 1000
		lastFakeIDMutex.RLock()
		if fakeID == lastFakeID {
			lastFakeIDMutex.RUnlock()
			time.Sleep(1 * time.Microsecond)
		} else {
			lastFakeIDMutex.RUnlock()
			lastFakeIDMutex.Lock()
			defer lastFakeIDMutex.Unlock()
			lastFakeID = fakeID
			return fakeID
		}
	}
}

// 这个用于没有打开远程同步时的假同步，生成ID使用
func GenFakeID() int64 {
	return genFakeID1() * 3
}

func IsFakeID(id int64) bool {
	if IsEmptyID(id) {
		return true
	}
	multiple := id / genFakeID1()
	return multiple >= 2 && multiple <= 4
}

func IsEmptyID(id int64) bool {
	return id == 0
}

func FormalizePageSize(pageSize int) int {
	if pageSize == 0 {
		return model.DefPageSize
	} else if pageSize < 0 {
		return model.UnlimitedPageSize
	}
	return pageSize
}

func FormalizePageOffset(offset int) int {
	if offset < 0 {
		offset = 0
	}
	return offset
}

func FormalizeName(name string) string {
	return utils.TrimBlankChar(strings.Replace(strings.Replace(name, "\t", "", -1), "\"", "", -1))
}

func Int2OneZero(value int) int {
	if value != 0 {
		return 1
	}
	return 0
}

func FormalizeMobile(mobile string) string {
	mobile = TrimDecorationChar(mobile)
	return strings.Replace(strings.Replace(mobile, "-", ",", -1), "_", ",", -1)
}

func IsStringLikeMobile(mobile string) bool {
	return mobilePat.FindStringIndex(mobile) != nil
}

func IsLegalStoreID(id int) bool {
	return id >= 100000 && id < 900000
}

// 将规格转为重量
func FormatSkuWeight(specQuality float32, specUnit string) int {
	return RegularizeSkuQuality(specQuality, specUnit)
}

type SkuList []*model.Sku

func (s SkuList) Len() int {
	return len(s)
}

func (s SkuList) Less(i, j int) bool {
	if s[i].NameID == s[j].NameID {
		if s[i].SpecUnit == s[j].SpecUnit {
			return s[i].SpecQuality < s[j].SpecQuality
		}
		return s[i].SpecUnit < s[j].SpecUnit
	}
	return s[i].NameID < s[j].NameID
}

func (s SkuList) Swap(i, j int) {
	tmp := s[i]
	s[i] = s[j]
	s[j] = tmp
}

func DownloadFileByURL(fileURL string) (bodyData []byte, fileMD5 string, err error) {
	response, err := http.Get(fileURL)
	if err == nil {
		defer response.Body.Close()
		if response.StatusCode == http.StatusOK {
			if bodyData, err = ioutil.ReadAll(response.Body); err == nil {
				fileMD5 = fmt.Sprintf("%X", md5.Sum(bodyData))
			}
		} else {
			err = platformapi.ErrHTTPCodeIsNot200
		}
	}
	return bodyData, fileMD5, err
}

/////
func GenPicFileName(suffix string) string {
	return fmt.Sprintf("image/%x%s", md5.Sum([]byte(utils.GetUUID()+suffix)), suffix)
}

func GuessVendorIDFromVendorStoreID(vendorStoreID int64) (vendorID int) {
	vendorID = model.VendorIDUnknown
	if vendorStoreID > 10040008 && vendorStoreID < 98765432 { // 京东11733065，8位
		vendorID = model.VendorIDJD
	} else if vendorStoreID > 1234567 && vendorStoreID < 9876543 { // 美团外卖 2461713，7位
		vendorID = model.VendorIDMTWM
	} else if vendorStoreID > 1234567890 && vendorStoreID < 998765432109 { // 饿百 2167002607，10位，12位
		vendorID = model.VendorIDEBAI
	} else if false { //vendorStoreID > 123456789 && vendorStoreID < 987654321 { // 微盟微商城 132091048，9位
		// vendorID = model.VendorIDWSC
	} else if vendorStoreID > 123456 && vendorStoreID < 654321 { // 京西门店ID，6位
		vendorID = model.VendorIDJX
	} else if vendorStoreID >= 100000000 { // 饿百出现九未id
		vendorID = model.VendorIDEBAI
	}
	return vendorID
}

func GetVendorName(vendorID int) (vendorName string) {
	return model.VendorChineseNames[vendorID]
}

func CaculateSkuEarningPrice(shopPrice, salePrice int64, storePayPercentage int) (earningPrice int64) {
	//TODO 2021-07-05 16:50菜市和果园一样，取低的
	//TODO 2021-07-05 18:48  还是改回来
	if beego.BConfig.RunMode == model.ServerTypeFruits || beego.BConfig.RunMode == model.ServerTypePet {
		earningPrice = salePrice
		if salePrice != 0 {
			if shopPrice > 0 && shopPrice < earningPrice {
				earningPrice = shopPrice
			}
		}
	} else {
		if shopPrice == 0 {
			earningPrice = salePrice * 70 / 100
		} else {
			earningPrice = shopPrice
		}
	}
	storePayPercentage = ConstrainPayPercentage(storePayPercentage)
	if storePayPercentage <= 0 {
		storePayPercentage = model.DefaultEarningPricePercentage
	}
	earningPrice = int64(math.Round(float64(earningPrice) * float64(storePayPercentage) / 100))
	return earningPrice
}

func GetImgBase64(img image.Image) (imgBase64 string, err error) {
	bufWriter := bytes.NewBuffer(nil)
	png.Encode(bufWriter, img)
	imgBase64 = "data:image/png;base64," + base64.StdEncoding.EncodeToString(bufWriter.Bytes())

	return imgBase64, err
}

func CreateQrOrBarCode(width, height int, codeType, srcData string) (imgBase64 string, err error) {
	if width > MaxCodeWidth {
		width = MaxCodeWidth
	}
	if height > MaxCodeHeight {
		height = MaxCodeHeight
	}

	var code barcode.Barcode
	if codeType == CodeTypeQr {
		code, err = qr.Encode(srcData, qr.M, qr.Auto)
	} else if codeType == CodeTypeBar {
		code, err = code128.Encode(srcData)
	} else {
		err = errors.New(fmt.Sprintf("未知编码类型:%s", codeType))
	}
	if err == nil {
		code, err = barcode.Scale(code, width, height)
		if err == nil {
			imgBase64, err = GetImgBase64(code)
		}
	}

	return imgBase64, err
}

// 高德地图面积计算公式
// https://blog.csdn.net/zdb1314/article/details/80661602
func CalcPolygonAreaAutonavi(points [][2]float64) (area float64) {
	sJ := float64(6378137)
	Hq := float64(0.017453292519943295)
	c := float64(sJ * Hq)
	d := float64(0)

	if 3 > len(points) {
		return 0
	}

	for i := 0; i < len(points)-1; i++ {
		h := points[i]
		k := points[i+1]
		u := h[0] * c * math.Cos(h[1]*Hq)

		hhh := h[1] * c
		v := k[0] * c * math.Cos(k[1]*Hq)
		d = d + (u*k[1]*c - v*hhh)
	}

	g1 := points[len(points)-1]
	point := points[0]
	eee := g1[0] * c * math.Cos(g1[1]*Hq)
	g2 := g1[1] * c

	k := point[0] * c * math.Cos(point[1]*Hq)
	d += eee*point[1]*c - k*g2
	return 0.5 * math.Abs(d) / float64(1000000)
}
