package services

import (
	"bytes"
	"encoding/base64"
	"encoding/binary"
	"encoding/json"
	"errors"
	"eta/eta_mobile/models"
	"eta/eta_mobile/services/alarm_msg"
	"eta/eta_mobile/utils"
	"fmt"
	"github.com/PuerkitoBio/goquery"
	"github.com/kgiannakakis/mp3duration/src/mp3duration"
	"html"
	"io"
	"io/ioutil"
	"os"
	"strings"
	"time"
	"unicode"
)

func CreateVideo(report *models.Report) (err error) {
	defer func() {
		if err != nil {
			utils.FileLog.Error("CreateVideo Err:%s", err.Error())
			go alarm_msg.SendAlarmMsg("CreateVideo, Err:"+err.Error(), 3)
			//go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "Err:"+err.Error(), utils.EmailSendToUsers)
		}
	}()

	// 获取基础配置, 若未配置则直接返回
	conf, e := models.GetBusinessConf()
	if e != nil {
		err = fmt.Errorf("获取基础配置失败, Err: " + e.Error())
		return
	}
	if conf[models.BusinessConfUseXf] != "true" {
		return
	}
	if conf[models.BusinessConfXfAppid] == "" || conf[models.BusinessConfXfApiKey] == "" || conf[models.BusinessConfXfApiSecret] == "" || conf[models.BusinessConfXfVcn] == "" {
		return
	}
	var xfReq XfParams
	xfReq.XfAPPID = conf[models.BusinessConfXfAppid]
	xfReq.XfAPIKey = conf[models.BusinessConfXfApiKey]
	xfReq.XfAPISecret = conf[models.BusinessConfXfApiSecret]

	ct, err := time.Parse(utils.FormatDateTime, report.CreateTime)
	createTime := ct.Format("0102")
	videoName := report.Title + "(" + createTime + ")"
	content := html.UnescapeString(report.Content)
	content = strings.Replace(content, "Powered", "", -1)
	content = strings.Replace(content, "by", "", -1)
	content = strings.Replace(content, "Froala", "", -1)
	content = strings.Replace(content, "Editor", "", -1)
	doc, err := goquery.NewDocumentFromReader(strings.NewReader(content))
	if err != nil {
		return
	}

	param := new(models.XfSendParam)
	param.Common.AppId = conf[models.BusinessConfXfAppid]
	param.Business.Aue = "lame"
	param.Business.Sfl = 1
	param.Business.Auf = "audio/L16;rate=16000"
	param.Business.Vcn = conf[models.BusinessConfXfVcn]
	param.Business.Speed = 50
	param.Business.Volume = 100
	param.Business.Pitch = 50
	param.Business.Bgs = 0
	param.Business.Tte = "UTF8"
	param.Business.Reg = "2"
	param.Business.Rdn = "0"
	param.Data.Status = 2
	videoContent := doc.Text()

	saveName := utils.GetRandStringNoSpecialChar(16) + ".mp3"
	savePath := "./" + saveName
	defer func() {
		if utils.FileIsExist(savePath) {
			os.Remove(savePath)
		}
	}()

	// 如果没有文本内容，那么就不生成了
	videoContent = strings.TrimSpace(videoContent)
	if videoContent == `` {
		return
	}

	contentArr := GetChineseCount(videoContent)
	for _, v := range contentArr {
		newText := v
		param.Data.Text = base64.StdEncoding.EncodeToString([]byte(newText))
		result, err := json.Marshal(param)
		if err != nil {
			return err
		}
		err = GetXfVideo(result, savePath, xfReq)
		if err != nil {
			err = errors.New("GetXfVideo Err:" + err.Error())
			utils.FileLog.Error("GetXfVideo err", err.Error())
			return err
		}
		time.Sleep(5 * time.Second)
	}

	uploadUrl := ``
	ossClient := NewOssClient()
	if ossClient == nil {
		err = fmt.Errorf("初始化OSS服务失败")
		return
	}
	uploadUrl, err = ossClient.UploadFile(saveName, savePath, "")
	if err != nil {
		err = fmt.Errorf("文件上传失败, Err: %s", err.Error())
		return
	}

	fileBody, err := ioutil.ReadFile(savePath)
	videoSize := len(fileBody)
	sizeFloat := (float64(videoSize) / float64(1024)) / float64(1024)
	sizeStr := utils.SubFloatToFloatStr(sizeFloat, 2)

	playSeconds, err := mp3duration.Calculate(savePath)
	if playSeconds <= 0 {
		playSeconds, err = utils.GetVideoPlaySeconds(savePath)
		if err != nil {
			err = errors.New("GetVideoPlaySeconds Err:" + err.Error())
			return
		}
	}

	// 修改报告的音频信息
	err = models.ModifyReportVideo(report.Id, uploadUrl, videoName, sizeStr, playSeconds)

	return
}

func GetChineseCount(str1 string) []string {
	fontArr := make([]string, 0)
	str := ""
	count := 0
	for _, char := range str1 {
		str += string(char)
		if unicode.Is(unicode.Han, char) {
			count++
			if count >= 1700 {
				fontArr = append(fontArr, str)
				str = ""
				count = 0
			}
		}
	}
	fontArr = append(fontArr, str)
	return fontArr
}

// BoxHeader 信息头
type BoxHeader struct {
	Size       uint32
	FourccType [4]byte
	Size64     uint64
}

// GetMP4Duration 获取视频时长，以秒计
func GetMP4Duration(reader io.ReaderAt) (lengthOfTime uint32, err error) {
	var info = make([]byte, 0x10)
	var boxHeader BoxHeader
	var offset int64 = 0
	// 获取moov结构偏移
	for {
		_, err = reader.ReadAt(info, offset)
		if err != nil {
			return
		}
		boxHeader = getHeaderBoxInfo(info)
		fourccType := getFourccType(boxHeader)
		if fourccType == "moov" {
			break
		}
		// 有一部分mp4 mdat尺寸过大需要特殊处理
		if fourccType == "mdat" {
			if boxHeader.Size == 1 {
				offset += int64(boxHeader.Size64)
				continue
			}
		}
		offset += int64(boxHeader.Size)
	}
	// 获取moov结构开头一部分
	moovStartBytes := make([]byte, 0x100)
	_, err = reader.ReadAt(moovStartBytes, offset)
	if err != nil {
		return
	}
	// 定义timeScale与Duration偏移
	timeScaleOffset := 0x1C
	durationOffest := 0x20
	timeScale := binary.BigEndian.Uint32(moovStartBytes[timeScaleOffset : timeScaleOffset+4])
	Duration := binary.BigEndian.Uint32(moovStartBytes[durationOffest : durationOffest+4])
	lengthOfTime = Duration / timeScale
	return
}

// getHeaderBoxInfo 获取头信息
func getHeaderBoxInfo(data []byte) (boxHeader BoxHeader) {
	buf := bytes.NewBuffer(data)
	binary.Read(buf, binary.BigEndian, &boxHeader)
	return
}

// getFourccType 获取信息头类型
func getFourccType(boxHeader BoxHeader) (fourccType string) {
	fourccType = string(boxHeader.FourccType[:])
	return
}

// CreateReportVideo 生成报告video
func CreateReportVideo(reportTitle, reportContent, reportTime string) (uploadUrl, videoName, sizeStr string, playSeconds float64, err error) {
	defer func() {
		if err != nil {
			utils.FileLog.Error("CreateReportVideo Err:%s", err.Error())
			go alarm_msg.SendAlarmMsg("CreateReportVideo, reportTitle:"+reportTitle+", Err:"+err.Error(), 3)
			//go utils.SendEmail(utils.APPNAME+"【"+utils.RunMode+"】"+"失败提醒", "CreateReportVideo, reportTitle:" + reportTitle +", Err:"+err.Error(), utils.EmailSendToUsers)
		}
	}()

	// 如果没有文本内容，那么就不生成了
	reportContent = strings.TrimSpace(reportContent)
	if reportContent == `` {
		return
	}

	// 获取基础配置, 若未配置则直接返回
	conf, e := models.GetBusinessConf()
	if e != nil {
		err = fmt.Errorf("获取基础配置失败, Err: " + e.Error())
		return
	}
	if conf[models.BusinessConfUseXf] != "true" {
		return
	}
	if conf[models.BusinessConfXfAppid] == "" || conf[models.BusinessConfXfApiKey] == "" || conf[models.BusinessConfXfApiSecret] == "" || conf[models.BusinessConfXfVcn] == "" {
		return
	}
	var xfReq XfParams
	xfReq.XfAPPID = conf[models.BusinessConfXfAppid]
	xfReq.XfAPIKey = conf[models.BusinessConfXfApiKey]
	xfReq.XfAPISecret = conf[models.BusinessConfXfApiSecret]

	ct, err := time.Parse(utils.FormatDateTime, reportTime)
	if err != nil {
		return
	}
	createTime := ct.Format("0102")
	videoName = reportTitle + "(" + createTime + ")"
	content := html.UnescapeString(reportContent)
	content = strings.Replace(content, "Powered", "", -1)
	content = strings.Replace(content, "by", "", -1)
	content = strings.Replace(content, "Froala", "", -1)
	content = strings.Replace(content, "Editor", "", -1)
	doc, err := goquery.NewDocumentFromReader(strings.NewReader(content))
	if err != nil {
		return
	}

	param := new(models.XfSendParam)
	param.Common.AppId = conf[models.BusinessConfXfAppid]
	param.Business.Aue = "lame"
	param.Business.Sfl = 1
	param.Business.Auf = "audio/L16;rate=16000"
	param.Business.Vcn = conf[models.BusinessConfXfVcn]
	param.Business.Speed = 50
	param.Business.Volume = 100
	param.Business.Pitch = 50
	param.Business.Bgs = 0
	param.Business.Tte = "UTF8"
	param.Business.Reg = "2"
	param.Business.Rdn = "0"
	param.Data.Status = 2
	videoContent := doc.Text()

	// 如果没有文本内容，那么就不生成了
	videoContent = strings.TrimSpace(videoContent)
	if videoContent == `` {
		return
	}

	saveName := utils.GetRandStringNoSpecialChar(16) + ".mp3"
	savePath := "./" + saveName
	defer func() {
		if utils.FileIsExist(savePath) {
			os.Remove(savePath)
		}
	}()
	contentArr := GetChineseCount(videoContent)
	for _, v := range contentArr {
		newText := v
		param.Data.Text = base64.StdEncoding.EncodeToString([]byte(newText))
		result, tmpErr := json.Marshal(param)
		if tmpErr != nil {
			return
		}
		err = GetXfVideo(result, savePath, xfReq)
		if err != nil {
			err = errors.New("GetXfVideo Err:" + err.Error())
			utils.FileLog.Error("GetXfVideo err", err.Error())
			return
		}
		time.Sleep(5 * time.Second)
	}

	//上传到阿里云 和 minio
	//if utils.ObjectStorageClient == "minio" {
	//	uploadUrl, err = UploadAudioToMinIo(saveName, savePath)
	//	if err != nil {
	//		err = errors.New("UploadAudioAliyun Err:" + err.Error())
	//		return
	//	}
	//} else {
	//	uploadUrl, err = UploadAudioAliyun(saveName, savePath)
	//	if err != nil {
	//		err = errors.New("UploadAudioAliyun Err:" + err.Error())
	//		return
	//	}
	//}
	ossClient := NewOssClient()
	if ossClient == nil {
		err = fmt.Errorf("初始化OSS服务失败")
		return
	}
	uploadUrl, err = ossClient.UploadFile(saveName, savePath, "")
	if err != nil {
		err = fmt.Errorf("文件上传失败, Err: %s", err.Error())
		return
	}

	fileBody, err := os.ReadFile(savePath)
	videoSize := len(fileBody)
	sizeFloat := (float64(videoSize) / float64(1024)) / float64(1024)
	sizeStr = utils.SubFloatToFloatStr(sizeFloat, 2)

	playSeconds, err = mp3duration.Calculate(savePath)
	if playSeconds <= 0 {
		playSeconds, err = utils.GetVideoPlaySeconds(savePath)
		if err != nil {
			err = errors.New("GetVideoPlaySeconds Err:" + err.Error())
			return
		}
	}

	return
}
