package datares

import (
	"bytes"
	"fmt"
	"image"
	"image/gif"
	"image/jpeg"
	"image/png"
	"net/http"

	"git.rosy.net.cn/jx-callback/business/jxutils"
	"git.rosy.net.cn/jx-callback/business/jxutils/jxcontext"
	"git.rosy.net.cn/jx-callback/business/jxutils/tasksch"
	"git.rosy.net.cn/jx-callback/business/model"
	"git.rosy.net.cn/jx-callback/business/model/dao"
	"git.rosy.net.cn/jx-callback/business/partner"
	"git.rosy.net.cn/jx-callback/globals"
	"git.rosy.net.cn/jx-callback/globals/api"
	"github.com/qiniu/api.v7/storage"
)

const (
	qiniuTokenExpires = 300 // 七牛TOKEN有效时间，5分钟

	MainImgWidth   = 800
	MainImgHeight  = 800
	MainImgWidth2  = 500
	MainImgHeight2 = 500
)

type UploadResTokenInfo struct {
	Token    string `json:"token,omitempty"`
	Expires  uint64 `json:"expires,omitempty"`
	FileName string `json:"fileName,omitempty"`
	Hit      bool   `json:"hit,omitempty"`
	Img      string `json:"img,omitempty"`
}

func Binary2Image(binaryData []byte, mimeType string) (img image.Image, outMimeType string, err error) {
	if mimeType == "" {
		mimeType = http.DetectContentType(binaryData)
	}
	switch mimeType {
	case model.MimeTypeJpeg:
		img, err = jpeg.Decode(bytes.NewReader(binaryData))
	case model.MimeTypePng:
		img, err = png.Decode(bytes.NewReader(binaryData))
	case model.MimeTypeGif:
		img, err = gif.Decode(bytes.NewReader(binaryData))
	}
	return img, mimeType, err
}

func GetQiniuUploadToken(ctx *jxcontext.Context, suffix, hashCode string) (upTokenInfo *UploadResTokenInfo, err error) {
	imgURL := ""
	if hashCode != "" {
		imgURL, _ = GetDataResource(ctx, hashCode)
	}

	putPolicy := storage.PutPolicy{
		Scope:   globals.QiniuBucket,
		Expires: qiniuTokenExpires,
	}
	upTokenInfo = &UploadResTokenInfo{
		Token:    putPolicy.UploadToken(api.QiniuAPI),
		Expires:  putPolicy.Expires,
		FileName: jxutils.GenPicFileName(suffix),
		Hit:      imgURL != "",
		Img:      imgURL,
	}
	return upTokenInfo, err
}

// 此函数要求resBinary不能空，mimeType与hashCode必须是正确的
func RegisterDataResource(ctx *jxcontext.Context, name, resourceURL, mimeType, hashCode string, resBinary []byte, imgType int, isAsyncUpload2Vendor bool) (dataRes *model.DataResource, err error) {
	if model.ValidMimeTypes[mimeType] == 0 {
		return nil, fmt.Errorf("MIME type:%s非法，当前只支持:%s", mimeType, model.GetValidMimeTypeDesc())
	}
	img, _, err := Binary2Image(resBinary, mimeType)
	if err != nil {
		return nil, err
	}
	if imgType == model.ImgTypeMain {
		if (img.Bounds().Dx() != MainImgWidth || img.Bounds().Dy() != MainImgHeight) && (img.Bounds().Dx() != MainImgWidth2 || img.Bounds().Dy() != MainImgHeight2) {
			return nil, fmt.Errorf("图片大小:%dx%d非法，要求必须:%dx%d", img.Bounds().Dx(), img.Bounds().Dy(), MainImgWidth, MainImgHeight)
		}
	}
	dataRes = &model.DataResource{
		Name:         name,
		HashCode:     hashCode,
		ResourceType: mimeType,
		MainURL:      resourceURL,
		UseType:      int8(imgType),
	}
	vendorID := jxutils.GuessDataResourceVendor(resourceURL)
	switch vendorID {
	case model.VendorIDQiNiuCloud:
		dataRes.QiniuURL = resourceURL
	case model.VendorIDEBAI:
		dataRes.EbaiURL = resourceURL
	}
	dao.WrapAddIDCULEntity(dataRes, ctx.GetUserName())
	if err = dao.CreateEntity(dao.GetDB(), dataRes); err != nil {
		if dao.IsDuplicateError(err) {
			dataRes, err = dao.GetDataResource(dao.GetDB(), dataRes.HashCode, dataRes.MainURL)
		} else {
			dataRes = nil
		}
		return dataRes, err
	}
	if imgType > 0 {
		if globals.EnableStoreWrite {
			// 忽略上传错误
			UploadImage2Vendors(ctx, nil, dataRes, resBinary, isAsyncUpload2Vendor)
		}
	}
	return dataRes, err
}

func TryRegisterDataResource(ctx *jxcontext.Context, name, resourceURL string, imgType int, isAsyncUpload2Vendor bool) (dataRes *model.DataResource, err error) {
	dataRes = &model.DataResource{
		MainURL: resourceURL,
	}
	err = dao.GetEntity(dao.GetDB(), dataRes, "MainURL")
	if err != nil {
		if !dao.IsNoRowsError(err) {
			return nil, err
		}
	} else {
		return dataRes, nil
	}
	resBinary, hashCode, err := jxutils.DownloadFileByURL(resourceURL)
	if err != nil {
		return nil, err
	}
	return RegisterDataResource(ctx, name, resourceURL, http.DetectContentType(resBinary), hashCode, resBinary, imgType, isAsyncUpload2Vendor)
}

func GetDataResource(ctx *jxcontext.Context, hashCode string) (resourceURL string, err error) {
	db := dao.GetDB()
	dataRes, err := dao.GetDataResource(db, hashCode, "")
	if err != nil {
		if dao.IsNoRowsError(err) {
			err = nil
		}
	} else {
		resourceURL = dataRes.MainURL
	}
	return resourceURL, err
}

// 这个函数，可能部分平台成功，部分失败
func UploadImage2Vendors(ctx *jxcontext.Context, parentTask tasksch.ITask, dataRes *model.DataResource, imgData []byte, isAsync bool) (hint string, err error) {
	var vendorIDs []int
	if dataRes.EbaiURL == "" {
		vendorIDs = append(vendorIDs, model.VendorIDEBAI)
	}
	if dataRes.MtwmURL == "" {
		vendorIDs = append(vendorIDs, model.VendorIDMTWM)
	}
	// if dataRes.JdsURL == "" {
	// 	vendorIDs = append(vendorIDs, model.VendorIDJDShop)
	// }
	if len(vendorIDs) > 0 {
		imgName := jxutils.GetShortNameFromURL(dataRes.MainURL)
		task := tasksch.NewSeqTask(fmt.Sprintf("上传图片至平台1:%s,%s", dataRes.Name, dataRes.MainURL), ctx,
			func(task *tasksch.SeqTask, step int, params ...interface{}) (result interface{}, err error) {
				switch step {
				case 0:
					if imgData == nil {
						if imgData, _, err = jxutils.DownloadFileByURL(dataRes.MainURL); err != nil {
							return nil, err
						}
					}
				case 1:
					uploadTask := tasksch.NewParallelTask(fmt.Sprintf("上传图片至平台2:%s,%s", dataRes.Name, imgName),
						tasksch.NewParallelConfig().SetIsContinueWhenError(true), ctx,
						func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
							vendorID := batchItemList[0].(int)
							if handler := partner.GetPurchasePlatformFromVendorID(vendorID); handler != nil {
								// TODO vendorOrgCode
								imgHint, err2 := handler.UploadImg(ctx, "", dataRes.MainURL, imgData, imgName, int(dataRes.UseType))
								if err = err2; err == nil {
									retVal = [][]interface{}{
										[]interface{}{
											vendorID,
											imgHint,
										},
									}
								}
							}
							return retVal, err
						}, vendorIDs)
					tasksch.HandleTask(uploadTask, task, false).Run()
					resultList, err2 := uploadTask.GetResult(0)
					err = err2
					if len(resultList) > 0 {
						db := dao.GetDB()
						for _, v := range resultList {
							result := v.([]interface{})
							vendorID := result[0].(int)
							imgHint := result[1].(string)
							updateField := ""
							if vendorID == model.VendorIDEBAI {
								dataRes.EbaiURL = imgHint
								updateField = "EbaiURL"
							} else if vendorID == model.VendorIDMTWM {
								dataRes.MtwmURL = imgHint
								updateField = "MtwmURL"
							}
							// else if vendorID == model.VendorIDJDShop {
							// 	dataRes.JdsURL = imgHint
							// 	updateField = "JdsURL"
							// }
							dao.UpdateEntity(db, dataRes, updateField)
						}
					}
				}
				return result, err
			}, 2)
		tasksch.HandleTask(task, parentTask, false).Run()
		if !isAsync {
			if _, err = task.GetResult(0); err == nil {
				hint = "1"
			}
		} else {
			hint = task.GetID()
		}
	}
	return hint, err
}

// func UploadImg2Platforms(ctx *jxcontext.Context, parentTask tasksch.ITask, imgURL string, imgData []byte, imgName string) (imgHintMap map[int]string, err error) {
// 	task := tasksch.NewParallelTask("UploadImg2Platforms", nil, ctx,
// 		func(task *tasksch.ParallelTask, batchItemList []interface{}, params ...interface{}) (retVal interface{}, err error) {
// 			vendorID := batchItemList[0].(int)
// 			if handler := partner.GetPurchasePlatformFromVendorID(vendorID); handler != nil {
// 				imgHint, err2 := handler.UploadImg(ctx, imgURL, imgData, imgName)
// 				if err = err2; err == nil {
// 					return [][]interface{}{
// 						[]interface{}{
// 							vendorID,
// 							imgHint,
// 						},
// 					}, nil
// 				}
// 			}
// 			return nil, err
// 		}, []int{model.VendorIDEBAI})
// 	tasksch.HandleTask(task, parentTask, false).Run()
// 	resultList, err := task.GetResult(0)
// 	if err == nil {
// 		imgHintMap = make(map[int]string)
// 		for _, v := range resultList {
// 			vList := v.([]interface{})
// 			imgHintMap[vList[0].(int)] = vList[1].(string)
// 		}
// 	}
// 	return imgHintMap, err
// }
