package services

import (
	"encoding/json"
	"eta_gn/eta_api/models"
	"eta_gn/eta_api/models/smart_report"
	"eta_gn/eta_api/services/alarm_msg"
	"eta_gn/eta_api/utils"
	"fmt"
	"html"
	"os"
	"os/exec"
	"path"
	"strconv"
	"time"
)

// SmartReportBuildVideoAndUpdate 生成音频
func SmartReportBuildVideoAndUpdate(item *smart_report.SmartReport) {
	if item == nil {
		return
	}
	var err error
	defer func() {
		if err != nil {
			tips := fmt.Sprintf("智能研报-音频生成, errMsg: %s", err.Error())
			go alarm_msg.SendAlarmMsg(tips, 2)
		}
	}()

	videoUrl, videoName, videoSize, videoPlaySeconds, e := CreateReportVideo(item.Title, item.Content, time.Now().Local().Format(utils.FormatDateTime))
	if e != nil {
		err = fmt.Errorf("create audio err: %s", e.Error())
		return
	}
	item.VideoUrl = videoUrl
	item.VideoName = videoName
	item.VideoSize = videoSize
	item.VideoPlaySeconds = videoPlaySeconds
	item.ModifyTime = time.Now().Local()
	cols := []string{"VideoUrl", "VideoName", "VideoSize", "VideoPlaySeconds", "ModifyTime"}
	if e = item.Update(cols); e != nil {
		err = fmt.Errorf("smart report update err: %s", e.Error())
		return
	}
}

// UpdateSmartReportEditing 更新研报当前更新状态
// status 枚举值 1:编辑中,0:完成编辑, 2:只做查询
func UpdateSmartReportEditing(reportId, status, thisUserId int, thisUserName string, adminIdName map[int]string, lang string) (ret models.MarkReportResp, err error) {
	key := fmt.Sprint(utils.CACHE_SMART_REPORT_EDITING, reportId)
	ret.Status = 0
	ret.Msg = "无人编辑"

	opUserId, e := utils.Rc.RedisInt(key)
	var opUser models.MarkReportItem
	var classifyNameFirst string
	if e != nil {
		opUserInfoStr, tErr := utils.Rc.RedisString(key)
		if tErr == nil {
			tErr = json.Unmarshal([]byte(opUserInfoStr), &opUser)
			if tErr == nil {
				opUserId = opUser.AdminId
			}
		}
	}

	if opUserId > 0 && opUserId != thisUserId {
		editor := opUser.Editor
		if editor == "" {
			editor = adminIdName[opUserId]
		}
		ret.Status = 1
		if lang == utils.EnLangVersion {
			ret.Msg = fmt.Sprintf("%s is currently editing the report", editor)
		} else {
			ret.Msg = fmt.Sprintf("当前%s正在编辑报告", editor)
		}
		ret.Editor = editor
		return
	}

	if status == 1 {
		nowUser := &models.MarkReportItem{AdminId: thisUserId, Editor: thisUserName, ReportClassifyNameFirst: classifyNameFirst}
		bt, e := json.Marshal(nowUser)
		if e != nil {
			err = fmt.Errorf("格式化编辑者信息失败")
			return
		}
		if opUserId > 0 {
			utils.Rc.Do("SETEX", key, int64(180), string(bt)) //3分钟缓存
		} else {
			utils.Rc.SetNX(key, string(bt), time.Second*60*3) //3分钟缓存
		}
	} else if status == 0 {
		//清除编辑缓存
		_ = utils.Rc.Delete(key)
	}
	return
}

// SmartReportElasticUpsert 新增/编辑报告es
func SmartReportElasticUpsert(smartReportId int, state int) (err error) {
	if smartReportId <= 0 {
		return
	}

	reportOB := new(smart_report.SmartReport)
	item, e := reportOB.GetItemById(smartReportId)
	if e != nil {
		if utils.IsErrNoRow(e) {
			// 可能被删了就直接忽略掉
			return
		}
		err = fmt.Errorf("获取报告失败, Err: %s", e.Error())
		return
	}

	esReport := new(smart_report.ElasticSmartReport)
	esReport.SmartReportId = item.SmartReportId
	esReport.Title = item.Title
	esReport.Abstract = item.Abstract
	esReport.BodyContent = utils.TrimHtml(html.UnescapeString(item.Content))
	esReport.PublishTime = item.PublishTime.Format(utils.FormatDateTime)
	esReport.PublishState = state
	esReport.Author = item.Author
	esReport.ClassifyIdFirst = item.ClassifyIdFirst
	esReport.ClassifyNameFirst = item.ClassifyNameFirst
	esReport.ClassifyIdSecond = item.ClassifyIdSecond
	esReport.ClassifyNameSecond = item.ClassifyNameSecond
	esReport.StageStr = strconv.Itoa(item.Stage)
	esReport.Frequency = item.Frequency
	if err = EsAddOrEditSmartReport(utils.SmartReportIndexName, strconv.Itoa(item.SmartReportId), esReport); err != nil {
		return
	}
	return
}

func ReportToPdf(width int, reportUrl, filePath string) (err error) {
	pyCode := `
import asyncio
from pyppeteer import launch

@asyncio.coroutine
async def main():
    # 异步代码
    browser = await launch({
        'executablePath': '%s',
        'headless': True,
        'args': ['--disable-infobars', '--no-sandbox']
    })
    page = await browser.newPage()
    await page.setViewport({
        'width': %d,
        'height': 1080,
    })
    await page.goto('%s', {
        'waitUntil': 'networkidle0',
        'timeout': 3000000  # 设置超时时间为 100 秒
    })

    # 在生成PDF之前等待2秒
    await asyncio.sleep(10)

    await page.pdf({
        'path': "%s",
        'printBackground': True,
        'format': "A2",
        'margin': {
            'top': '10mm',
            'bottom': '10mm',
            'left': '10mm',
            'right': '10mm'
        }
    })
    await browser.close()

# 创建事件循环
loop = asyncio.get_event_loop()

# 使用事件循环运行main函数
try:
    loop.run_until_complete(main())
finally:
    # 关闭事件循环
    loop.close()
`

	pyCode = fmt.Sprintf(pyCode, utils.ChromePath, width, reportUrl, filePath)
	utils.FileLog.Info("pdf pyCode: \n" + pyCode)
	cmd := exec.Command("python3", "-c", pyCode)
	output, e := cmd.CombinedOutput()
	if e != nil {
		err = e
		utils.FileLog.Info("ReportToPdf failed: , error: \n" + err.Error())
		utils.FileLog.Info("Output: %s\n", string(output))
		go alarm_msg.SendAlarmMsg("ReportToPdf failed:"+err.Error(), 3)
		go alarm_msg.SendAlarmMsg("Output :"+string(output), 3)
	}
	defer func() {
		cmd.Process.Kill()
	}()
	return
}

func ReportToJpeg(width int, reportUrl, filePath string) (err error) {
	pyCode := `
import asyncio
from pyppeteer import launch, errors

async def main():
    try:
        # 启动浏览器
        browser = await launch({
            'executablePath': '%s',
            'headless': True,
            'args': ['--disable-infobars', '--no-sandbox']
        })
        
        # 新建页面
        page = await browser.newPage()
        
        # 设置视口大小
        await page.setViewport({
            'width': %d,
            'height': 1080
        })
        
        # 导航到页面
        await page.goto('%s', {
            'waitUntil': 'networkidle0',
            'timeout': 3000000  # 设置超时时间为 100 秒
        })
        # Customizing footer for page numbers starting from page 2

        # 在这里添加两秒的等待
        await asyncio.sleep(5)

        await page.screenshot({
            'path': "%s",
            'fullPage': True,
			'quality':80
        })
        
    except errors.BrowserError as e:
        print('Browser closed unexpectedly:', e)
    except Exception as e:
        print('An error occurred:', e)
    finally:
        # 确保浏览器关闭
        if browser is not None:
            await browser.close()

# 获取当前事件循环
loop = asyncio.get_event_loop()

# 运行事件循环直到main协程完成
try:
    loop.run_until_complete(main())
except Exception as e:
    print('Error during event loop execution:', e)
finally:
    # 关闭事件循环
    loop.close()
`

	pyCode = fmt.Sprintf(pyCode, utils.ChromePath, width, reportUrl, filePath)
	utils.FileLog.Info("jpeg pyCode: \n" + pyCode)
	cmd := exec.Command("python3", "-c", pyCode)

	output, e := cmd.CombinedOutput()
	if e != nil {
		err = e
		utils.FileLog.Info("ReportToJpeg failed: , error: \n" + err.Error())
		utils.FileLog.Info("Output: %s\n", string(output))
		go alarm_msg.SendAlarmMsg("ReportToJpeg failed:"+err.Error(), 3)
		go alarm_msg.SendAlarmMsg("Output :"+string(output), 3)
	}
	defer func() {
		cmd.Process.Kill()
	}()
	return
}

// Report2pdfAndJpeg
// @Description: 报告转pdf和图片
// @author: Roc
// @datetime 2024-07-19 14:11:38
// @param reportUrl string
// @param reportId int
// @param reportType int
func Report2pdfAndJpeg(reportUrl string, reportId, reportType int) {
	var err error

	defer func() {
		if err != nil {
			go alarm_msg.SendAlarmMsg("Report2pdfAndJpeg failed:"+err.Error(), 3)
			utils.FileLog.Info("Report2pdfAndJpeg failed: , error: \n" + err.Error())
		}
	}()

	if reportUrl == `` {
		return
	}

	// 先清空字段
	if reportType == 1 {
		err = models.UpdatePdfUrlReportById(reportId)
		if err != nil {
			utils.FileLog.Info("清空pdf长图字段失败, Err: \n" + err.Error())
			return
		}
	} else if reportType == 2 {
		err = models.UpdatePdfUrlEnglishReportById(reportId)
		if err != nil {
			utils.FileLog.Info("清空pdf长图字段失败, Err: \n" + err.Error())
			return
		}
	} else if reportType == 3 {
		err = smart_report.UpdatePdfUrlSmartReportById(reportId)
		if err != nil {
			utils.FileLog.Info("清空pdf长图字段失败, Err: \n" + err.Error())
			return
		}
	}

	reportCode := utils.MD5(strconv.Itoa(reportId))

	pdfPath := `./static/` + reportCode + ".pdf"
	jpegPath := `./static/` + reportCode + ".jpg"

	width := 1560
	if reportType == 3 {
		width = 800
	}
	err = ReportToPdf(width, reportUrl, pdfPath)
	if err != nil {
		utils.FileLog.Info("ReportToPdf failed: , error: \n" + err.Error())
		go alarm_msg.SendAlarmMsg("ReportToPdf failed:"+err.Error(), 3)
	}

	file, err := os.Open(pdfPath)
	if err != nil {
		utils.FileLog.Info("Open failed: , error: \n" + err.Error())
		go alarm_msg.SendAlarmMsg("Open failed:"+err.Error(), 3)
		return
	}

	ext := path.Ext(file.Name())

	randStr := utils.GetRandStringNoSpecialChar(28)
	fileName := randStr + ext
	defer file.Close() //关闭上传文件

	resourceUrl := ``
	ossClient := NewOssClient()
	if ossClient == nil {
		utils.FileLog.Info("初始化OSS服务失败")
		return
	}
	resourceUrl, err = ossClient.UploadFile(fileName, pdfPath, "")
	if err != nil {
		utils.FileLog.Info("文件上传失败, Err: \n" + err.Error())
		go alarm_msg.SendAlarmMsg("文件上传失败:"+err.Error(), 3)
		return
	}
	defer func() {
		_ = os.Remove(pdfPath)
	}()

	if reportType == 3 {
		// 更新pdf url
		ob := new(smart_report.SmartReport)
		ob.SmartReportId = reportId
		ob.DetailPdfUrl = resourceUrl
		if err = ob.Update([]string{"DetailPdfUrl"}); err != nil {
			utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
			return
		}
	} else if reportType == 2 {
		err = models.ModifyEnglishReportPdfUrl(reportId, resourceUrl)
		if err != nil {
			utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
			return
		}
	} else if reportType == 1 {
		err = models.ModifyReportPdfUrl(reportId, resourceUrl)
		if err != nil {
			utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
			return
		}
	}

	time.Sleep(1 * time.Minute)

	err = ReportToJpeg(width, reportUrl, jpegPath)
	if err != nil {
		utils.FileLog.Info("ReportToJpeg failed: , error: \n" + err.Error())
	}
	file, err = os.Open(jpegPath)
	if err != nil {
		utils.FileLog.Info("open file failed: , error: \n" + err.Error())
		return
	}

	ext = path.Ext(file.Name())

	randStr = utils.GetRandStringNoSpecialChar(28)
	fileName = randStr + ext
	defer file.Close() //关闭上传文件

	resourceUrl = ``
	ossClient = NewOssClient()
	if ossClient == nil {
		utils.FileLog.Info("初始化OSS服务失败")
		return
	}
	resourceUrl, err = ossClient.UploadFile(fileName, jpegPath, "")
	if err != nil {
		utils.FileLog.Info("文件上传失败, Err: \n" + err.Error())
		return
	}
	defer func() {
		_ = os.Remove(jpegPath)
	}()

	if reportType == 3 {
		// 更新jpeg url
		ob := new(smart_report.SmartReport)
		ob.SmartReportId = reportId
		ob.DetailImgUrl = resourceUrl
		if err = ob.Update([]string{"DetailImgUrl"}); err != nil {
			utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
			return
		}
	} else if reportType == 2 {
		err = models.ModifyEnglishReportImgUrl(reportId, resourceUrl)
		if err != nil {
			utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
			return
		}
	} else if reportType == 1 {
		err = models.ModifyReportImgUrl(reportId, resourceUrl)
		if err != nil {
			utils.FileLog.Info("更新研报失败, Err: \n" + err.Error())
			return
		}
	}
}