package upload

import (
	"bytes"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"fonchain-fiee/pkg/config"
	"fonchain-fiee/pkg/e"
	"fonchain-fiee/pkg/logic"
	"fonchain-fiee/pkg/model"
	"fonchain-fiee/pkg/service"
	"fonchain-fiee/pkg/utils"
	"github.com/disintegration/imaging"
	"github.com/fonchain_enterprise/utils/objstorage"
	"github.com/gin-gonic/gin"
	uuid "github.com/satori/go.uuid"
	"io"

	ffmpeg "github.com/u2takey/ffmpeg-go"
	"go.uber.org/zap"
	"io/ioutil"
	"mime/multipart"
	"net/url"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
)

const (
	MediaPath            = "./runtime/"
	RouteType            = "static/"
	VideoType            = "video"
	ImageType            = "image"
	PngType              = "png"
	PdfType              = "pdf"
	ArtworkFilePath      = "artwork"
	ArtworkChunkBasePath = "./runtime/tmp/artworks"
)

func UploadImg(c *gin.Context) {
	var err error
	var filename string
	var fileFullName string
	source := c.PostForm("source")
	mask := c.PostForm("mask")
	action := c.PostForm("action")
	defineFileName := c.PostForm("defineFileName")
	urlParam := c.PostForm("urlParam")
	if mask == "" {
		mask = "default"
	}
	mediaType := c.PostForm("type")
	zap.L().Info("UploadImg 1", zap.Any("mask", mask))
	var BasePath string
	if mediaType == "" || mediaType == ImageType {
		mediaType = ImageType
	}
	BasePath = fmt.Sprintf("%s%s", MediaPath, mediaType)
	//BaseRoute = fmt.Sprintf("%s%s", RouteType, mediaType)
	var isCompress int
	if cStr, ok := c.GetPostForm("is_compress"); ok {
		var errS error
		isCompress, errS = strconv.Atoi(cStr)
		if errS != nil {
			service.Error(c, err)
			return
		}
	}
	zap.L().Info("UploadImg 2 ", zap.Any("mask", mask))
	// 检验参数
	if mask == "" || source == "" {
		service.Error(c, err)
		return
	}
	file, err := c.FormFile("file")
	// 检验文件
	if err != nil {
		zap.L().Error("Upload FormFile err", zap.Error(err))
		service.Error(c, err)
		return
	}
	//logger.Errorf("UploadImg 3 %+v", mask)
	// 判断是不是视频或者需要压缩
	var oriUrl string
	if isCompress != 1 && mediaType != "video" && action == "" {
		oriUrl, err = quickBos(file, mediaType, mask, source, defineFileName)
		if err != nil {
			service.Error(c, err)
			return
		}
		if urlParam != "" {
			oriUrl = fmt.Sprintf("%s?%s", oriUrl, urlParam)
		}
		service.Success(c, oriUrl)
		return
	}
	//创建文件名
	fileExt := strings.ToLower(path.Ext(file.Filename))
	if defineFileName != "" {
		fileFullName = defineFileName
	} else {
		newUu, _err := uuid.NewV4()
		if _err != nil {
			service.Error(c, err)
			return
		}
		filename = newUu.String()
		fileFullName = fmt.Sprintf("%s%s", filename, fileExt)
	}
	//检测文件夹 不存在就创建
	imgPath := fmt.Sprintf("%s/%s/%s", BasePath, source, mask)
	_, err = utils.CheckDirPath(imgPath, true)
	if err != nil {
		service.Error(c, err)
		return
	}
	dst := fmt.Sprintf("%s/%s", imgPath, fileFullName)
	// 保存文件至指定路径
	err = c.SaveUploadedFile(file, dst)
	if err != nil {
		service.Error(c, err)
		return
	}
	if action == model.ImgActionRotate {
		fileFullName = fmt.Sprintf("%s%s", filename, fileExt)
		newDst := fmt.Sprintf("%s/%s_rotate%v", imgPath, filename, fileExt)
		if err = logic.MakeThumbnail(dst, newDst); err != nil {
			//ResponseQuickMsg(c, e.Failed, e.GetMsg(e.ERROR_ROTATE_IMG), nil)
			//return
		} else {
			_ = os.Remove(dst)
			dst = newDst
		}
	} else if action == model.ImgActionRotate270 {
		fileFullName = fmt.Sprintf("%s%s", filename, fileExt)
		newDst := fmt.Sprintf("%s/%s_rotate%v", imgPath, filename, fileExt)
		if err = logic.MakeThumbnailDefault270(dst, newDst); err != nil {
			fmt.Printf("MakeThumbnailDefault90 err %+v\n", err)
		} else {
			_ = os.Remove(dst)
			dst = newDst
		}
	}
	//localUrl := fmt.Sprintf("%s/%s/%s/%s/%s", config.ServerDM, BaseRoute, source, mask, fileFullName)
	var data map[string]string = make(map[string]string, 2)
	//data["ori_url"] = localUrl

	if int32(isCompress) == 1 {
		//压缩图片并存储在原图路径,命名格式xx.jpg_small.jpg
		fileFullName = fmt.Sprintf("%s_small%s", filename, fileExt)
		newDst := fmt.Sprintf("%s/%s", imgPath, fileFullName)
		//compressUrl := fmt.Sprintf("%s/%s/%s/%s/%s", config.ServerDM, BaseRoute, source, mask, fileFullName)
		err = utils.CompressJPG(dst, newDst)
		compressUrl, err := PutBos(newDst, mediaType, true)
		if err != nil {
			service.Error(c, err)
			return
		}
		data["compress_url"] = compressUrl
	}
	// 如果是视频需要截图图片做封面
	if mediaType == VideoType {
		videoCover := fmt.Sprintf("%s/%s", imgPath, filename)
		_, err = GetSnapshot(dst, videoCover, 1)
		if err != nil {
			zap.L().Error("GetSnapshot err", zap.Error(err))
			service.Error(c, err)
			return
		}
		zap.L().Info("UploadImg 8.1 videoCover", zap.Any("videoCover", videoCover))
		//data["cover_url"] = fmt.Sprintf("%s/%s/%s/%s/%s", config.ServerDM, BaseRoute, source, mask, fmt.Sprintf("%s.%s", filename, PngType))
		coverUrl, err := PutBos(videoCover+"."+PngType, mediaType, true)
		if urlParam != "" {
			coverUrl = fmt.Sprintf("%s?%s", coverUrl, urlParam)
		}
		data["cover_url"] = coverUrl
		if err != nil {
			zap.L().Error("Upload GetSnapshot err", zap.Error(err))
			service.Error(c, err)
			return
		}
		//ResponseQuickMsg(c, e.Ok, e.GetMsg(e.SUCCESS), data)
		//return
	}
	ossUrl, err := PutBos(dst, mediaType, true)
	if err != nil {
		service.Error(c, err)
		return
	}
	if urlParam != "" {
		ossUrl = fmt.Sprintf("%s?%s", ossUrl, urlParam)
	}
	data["ori_url"] = ossUrl
	service.Success(c, data)
	return
}
func quickBos(file *multipart.FileHeader, mediaType string, mask string, source string, defineFileName string) (url string, err error) {
	newFile, _ := file.Open()
	var filename string
	defer newFile.Close()
	if defineFileName != "" {
		filename = defineFileName
	} else {
		uuids, _ := uuid.NewV4()
		filename = uuids.String()
		filename = fmt.Sprintf("%s%s", filename, filepath.Ext(file.Filename))
	}
	filePath := fmt.Sprintf("%s/%s/%s/%s", mediaType, mask, source, filename)
	fileBytes, _ := ioutil.ReadAll(newFile)
	if mediaType == "image" {
		if err = BaiduCheckImage(fileBytes); err != nil {
			return
		}
	}
	var objectName string = fmt.Sprintf("%s/%s/%s", config.ConfigData.Oss.BaseDir, config.Env, filePath)
	BOSClient, _ := objstorage.NewOSS(config.ConfigData.Oss.AccessKeyId, config.ConfigData.Oss.AccessKeySecret, config.ConfigData.Oss.Endpoint)
	_, err = BOSClient.PutObjectFromBytes(config.ConfigData.Oss.BucketName, objectName, fileBytes)
	if err != nil {
		//logger.Errorf("quickOss err", err)
		return
	}
	//url = fmt.Sprintf("%s%s%s/%s", config.BosHttp, config.BosBucketName, config.BosUrl, objectName)
	url = fmt.Sprintf("%s/%s", config.ConfigData.Oss.CdnHost, objectName)
	return
}

// BaiduCheckImage 图片鉴黄
func BaiduCheckImage(imageByte []byte) (err error) {
	return
	var (
		accesstoken string
		response    string
	)
	sourcestring := base64.StdEncoding.EncodeToString(imageByte)
	if accesstoken, err = logic.GetImageAccessToken(); err != nil {
		return err
	}
	host := "https://aip.baidubce.com/rest/2.0/solution/v1/img_censor/v2/user_defined?access_token=[" + accesstoken + "]"
	if response, err = utils.PostForm(host, url.Values{"image": {sourcestring}}); err != nil {
		//logger.Error("user_defined PostForm err", err)
		return err
	}
	var res struct {
		ErrorCode      int64  `json:"error_code"`
		ErrorMsg       string `json:"error_msg"`
		Conclusion     string `json:"conclusion"`
		Log_id         uint64 `json:"log_id"`
		IsHitMd5       bool   `json:"isHitMd5"`
		ConclusionType int64  `json:"conclusionType"`
	}
	if err = json.Unmarshal([]byte(response), &res); err != nil {
		//err = errors.New(e.GetMsg(e.JsonUnmarshal))
		return
	}
	//logger.Error("user_defined res", res)
	if res.ErrorCode != 0 || res.ErrorMsg != "" {
		return errors.New(e.GetMsg(e.ERROR_BAIDU_FAIL))
	}
	if res.Conclusion != "合规" && res.Conclusion != "疑似" {
		return errors.New(e.GetMsg(e.ERROR_BAIDU_IMAGE))
	}
	return nil
}
func PutBos(filePath string, mediaType string, needRemove bool) (url string, err error) {
	BOSClient, err := objstorage.NewOSS(config.ConfigData.Oss.AccessKeyId, config.ConfigData.Oss.AccessKeySecret, config.ConfigData.Oss.Endpoint)
	if err != nil {
		//logger.Errorf("PutBos NewOss err ", err)
		err = errors.New(e.GetMsg(e.ErrorUploadBos))
		return
	}
	f, err := os.Open(filePath)
	if err != nil {
		//logger.Errorf("PutBos Open err %+v", err.Error())
		return
	}
	fileBytes, _ := io.ReadAll(f)
	f.Close()
	//删除本地文件
	if needRemove {
		os.Remove(filePath)
	}
	if mediaType == "image" {
		if err = BaiduCheckImage(fileBytes); err != nil {
			return
		}
	}
	filePath = strings.Replace(filePath, model.MediaPath, "", 1)
	var objectName string = fmt.Sprintf("%s/%s%s", config.ConfigData.Oss.BaseDir, config.Env, filePath)
	_, err = BOSClient.PutObjectFromBytes(config.ConfigData.Oss.BucketName, objectName, fileBytes)
	if err != nil {
		//logger.Errorf("PutBos PutObject err %+v", err.Error())
		err = errors.New(e.GetMsg(e.ErrorUploadBos))
		return
	}
	//url = fmt.Sprintf("%s%s%s/%s", config.BosHttp, config.BosBucketName, config.BosUrl, objectName)
	url = fmt.Sprintf("%s/%s", config.ConfigData.Oss.CdnHost, objectName)
	return
}
func GetSnapshot(videoPath, snapshotPath string, frameNum int) (snapshotName string, err error) {
	buf := bytes.NewBuffer(nil)
	zap.L().Info("GetSnapshot", zap.Any("videoPath", videoPath))
	err = ffmpeg.Input(videoPath).
		Filter("select", ffmpeg.Args{fmt.Sprintf("gte(n,%d)", frameNum)}).
		Output("pipe:", ffmpeg.KwArgs{"vframes": 1, "format": "image2", "vcodec": "mjpeg"}).
		WithOutput(buf, os.Stdout).
		Run()
	if err != nil {
		zap.L().Error("GetSnapshot Input err", zap.Error(err))
		return "", err
	}

	img, err := imaging.Decode(buf)
	if err != nil {
		zap.L().Error("GetSnapshot Decode err", zap.Error(err))
		return "", err
	}

	err = imaging.Save(img, snapshotPath+"."+PngType)
	if err != nil {
		zap.L().Error("GetSnapshot Save err", zap.Error(err))
		return "", err
	}

	names := strings.Split(snapshotPath, "\\")
	snapshotName = names[len(names)-1] + "." + PngType
	return
}