// Copyright 2019  getensh.com. All rights reserved.
// Use of this source code is governed by  getensh.com.
package ctrl_v1

import (
	"gd_management_gateway/rpc_apis/gd_statistics"
	"encoding/json"
	"fmt"
	"reflect"
	"sort"
	"time"
	"github.com/xuri/excelize/v2"
	"github.com/tealeg/xlsx"
)

type LogWriteExcelManagerNew struct {
	file             *excelize.File
	baseSheet        *excelize.StreamWriter
	reqSheet         *excelize.StreamWriter
	resSheet         *excelize.StreamWriter
	isMakeHeader     bool
	reqTitleList []string
	resTitleList []string
	line int
}

func initLogExcelManagerNew(isParseParam bool) *LogWriteExcelManagerNew {
	m := &LogWriteExcelManagerNew{}
	file := excelize.NewFile()
	//设置表名
	m.file = file
	file.SetSheetName("Sheet1", "日志")
	//创建流式写入
	m.baseSheet, _ = file.NewStreamWriter("日志")
	//修改列宽
	m.baseSheet.SetColWidth(1, 15, 12)
	if isParseParam {
		file.NewSheet("请求参数")
		m.reqSheet, _ = file.NewStreamWriter("请求参数")
		m.reqSheet.SetColWidth(1, 15, 12)
		file.NewSheet("响应参数")
		m.resSheet, _ = file.NewStreamWriter("响应参数")
		m.resSheet.SetColWidth(1, 15, 12)
	}
	//writer.SetRow("A1",model.Title)
	return m
}


// initLogExcelManager 初始化日志文件结构， isParseParam 代表是否解析参数
func initLogExcelManager(isParseParam bool) *LogWriteExcelManager {
	file := xlsx.NewFile()
	m := &LogWriteExcelManager{}
	m.file = file
	m.baseSheet, _ = m.file.AddSheet("日志")
	if isParseParam {
		m.reqTitleIndexMap = map[string]int{}
		m.resTitleIndexMap = map[string]int{}
		m.reqSheet, _ = m.file.AddSheet("请求参数")
		m.resSheet, _ = m.file.AddSheet("响应参数")
	}

	return m
}


// setBaseSheetTitle 设置excel日志sheet列名
func setBaseSheetTitleNew(baseSheet *excelize.StreamWriter) {
	title := []interface{}{ "关键字","错误码", "错误消息","实际请求","实际响应","原始请求","原始响应","请求时间", "耗时"}
	baseSheet.SetRow("A1",title)

}


// setBaseSheetTitle 设置excel日志sheet列名
func setBaseSheetTitle(baseSheet *xlsx.Sheet) {
	row := baseSheet.AddRow()
	cell := row.AddCell()
	cell.Value = "关键字"
	cell = row.AddCell()
	cell.Value = "错误码"
	cell = row.AddCell()
	cell.Value = "错误消息"
	cell = row.AddCell()
	cell.Value = "实际请求"
	cell = row.AddCell()
	cell.Value = "实际响应"
	cell = row.AddCell()
	cell.Value = "原始请求"
	cell = row.AddCell()
	cell.Value = "原始响应"
	cell = row.AddCell()
	cell.Value = "请求时间"
	cell = row.AddCell()
	cell.Value = "耗时"
}

// setBaseSheetThirdTitle 设置excel数据源日志sheet列名
func setBaseSheetThirdTitle(baseSheet *xlsx.Sheet) {
	row := baseSheet.AddRow()
	cell := row.AddCell()
	cell.Value = "关键字"
	cell = row.AddCell()
	cell.Value = "code"
	cell = row.AddCell()
	cell.Value = "msg"
	cell = row.AddCell()
	cell.Value = "请求"
	cell = row.AddCell()
	cell.Value = "响应"
	cell = row.AddCell()
	cell.Value = "请求时间"
	cell = row.AddCell()
	cell.Value = "耗时"
}

// convertParamToMap 将参数字符串转为map
func convertParamToMap(param string) map[string]interface{} {
	if param == "" || param[0] != '[' && param[0] != '{' {
		return nil
	}
	pm := map[string]interface{}{}

	// 数据库参数格式为json时，转为普通map
	if param[0] == '{' {
		err := json.Unmarshal([]byte(param), &pm)
		if err != nil {
			return nil
		}
	} else if param[0] == '[' {
		// 数据库参数格式为json数组时，转为普通map
		m := []map[string]interface{}{}
		err := json.Unmarshal([]byte(param), &m)
		if err != nil {
			return nil
		}
		if len(m) == 0 {
			return nil
		}
		pm = m[0]
	}
	return pm
}

func makeParamTitleNew(write *LogWriteExcelManagerNew, m map[string]interface{}, headMap map[string]string,isReq bool) {
	paramList := &write.reqTitleList
	if !isReq{
		paramList = &write.resTitleList
	}

	if len(*paramList) > 0 {
		return
	}

	titleList := []interface{}{}
	for k, _ := range m {
		*paramList = append(*paramList,k)
		//titleList = append(titleList,getChineseHeader(k, headMap))
	}

	sort.Strings(*paramList)
	for _,v := range *paramList{
		titleList = append(titleList,getChineseHeader(v, headMap))
	}

	if len(titleList)>0{
		if isReq{
			write.reqSheet.SetRow("A1",titleList)
		}else{
			write.resSheet.SetRow("A1",titleList)
		}

	}
}

// makeParamTitle 根据参数map填充列名栏，并返回列名和列名所在列的关系map
func makeParamTitle(row *xlsx.Row, m map[string]interface{}, titleIndexMap map[string]int, headMap map[string]string) {
	nextIndex := len(titleIndexMap)
	for k, _ := range m {
		if _, ok := titleIndexMap[k]; ok == false {
			titleIndexMap[k] = nextIndex
			nextIndex++
			cell := row.AddCell()
			cell.Value = getChineseHeader(k, headMap)
		}
	}
}

// getParamValue 将data转换为字符串
func getParamValue(data interface{}) string {
	if data == nil {
		return ""
	}
	t := reflect.TypeOf(data).Kind()
	if t == reflect.Struct || t == reflect.Map || t == reflect.Slice {
		bytes, _ := json.Marshal(data)
		return string(bytes)
	}

	/*if t == reflect.Slice {
		array, _ := data.([]interface{})
		if len(array) == 0 {
			return "[]"
		}
		if _, ok := data.(map[string]interface{}); ok {
			bytes, _ := json.Marshal(data)
			return string(bytes)
		}
	}*/
	return fmt.Sprintf("%v", data)
}

// writeParamRow 将参数m根据列名-索引titleMap写入行
func writeParamRowNew(write *LogWriteExcelManagerNew, m map[string]interface{},isReq bool) {
	paramList := write.reqTitleList
	if !isReq{
		paramList = write.resTitleList
	}

	rowList := []interface{}{}
	for _,v := range paramList{
		if value, ok := m[v]; ok {
			rowList = append(rowList,value)
		}else{
			rowList = append(rowList,"")
		}
	}

	//if len(rowList)>0{
		cell, _ := excelize.CoordinatesToCellName(1, write.line)
		if isReq{
			write.reqSheet.SetRow(cell,rowList)
		}else{
			write.resSheet.SetRow(cell,rowList)
		}
	//}
}

// writeParamRow 将参数m根据列名-索引titleMap写入行
func writeParamRow(row *xlsx.Row, m map[string]interface{}, titleIndex map[string]int) {
	for i := 0; i < len(titleIndex); i++ {
		row.AddCell()
	}
	for k, i := range titleIndex {
		if value, ok := m[k]; ok {
			row.Cells[i].Value = getParamValue(value)
		}
	}
}


// writeParamSheet 将参数解析后写入对应sheet
func writeParamSheetNew(write *LogWriteExcelManagerNew, param string, headMap map[string]string,isReq bool) {

	if isReq{
		if write.reqSheet == nil {
			return
		}
	} else{
		if write.resSheet == nil {
			return
		}
	}

	// 处理参数
	m := convertParamToMap(param)
	if m == nil {
		return
	}

	// 处理列名
	makeParamTitleNew(write, m, headMap,isReq)

	// 根据列名及其索引添加对应值
	writeParamRowNew(write, m, isReq)
}


// writeParamSheet 将参数解析后写入对应sheet
func writeParamSheet(sheet *xlsx.Sheet, param string, titleIndexMap map[string]int, headMap map[string]string) {
	if sheet == nil {
		return
	}

	// 列名栏
	if len(sheet.Rows) == 0 {
		sheet.AddRow()
	}
	row := sheet.AddRow()

	// 处理参数
	m := convertParamToMap(param)
	if m == nil {
		row.AddCell()
		return
	}

	// 处理列名
	makeParamTitle(sheet.Rows[0], m, titleIndexMap, headMap)

	// 根据列名及其索引添加对应值
	writeParamRow(row, m, titleIndexMap)
}

type LogWriteExcelManager struct {
	file             *xlsx.File
	baseSheet        *xlsx.Sheet
	reqSheet         *xlsx.Sheet
	resSheet         *xlsx.Sheet
	reqTitleIndexMap map[string]int
	resTitleIndexMap map[string]int
}


// accessLogWriteToExcel 将日志写入excel， baseSheet是日志sheet，reqSheet是入参解析数据sheet，resSheel是反参解析数据sheet
func accessLogWriteToExcelNew(mg *LogWriteExcelManagerNew, datas []gd_statistics.AccessLog, headMap map[string]string) {
	if !mg.isMakeHeader {
		setBaseSheetTitleNew(mg.baseSheet)
		mg.isMakeHeader = true
		mg.line = 1
	}

	for _, data := range datas {
		cellData  := []interface{}{}
		cellData = append(cellData,data.Search,data.Code,data.Msg,data.RawRequestParams,
			data.ResponseParams,data.RequestParams,data.RawResponseParams,
			fmt.Sprintf("%s", time.Unix(data.Timestamp, 0).Format("2006-01-02 15:04:05")),
			fmt.Sprintf("%.2f", data.Elapsed))
		mg.line++
		cell, _ := excelize.CoordinatesToCellName(1, mg.line)
		mg.baseSheet.SetRow(cell, cellData)
		if mg.reqSheet != nil {
			// 请求参数解析后单独放一个sheet
			writeParamSheetNew(mg, data.RequestParams, headMap,true)
		}

		if mg.resSheet != nil {
			// 响应参数解析后单独放一个sheet
			writeParamSheetNew(mg, data.ResponseParams, headMap,false)
		}

	}
}


// accessLogWriteToExcel 将日志写入excel， baseSheet是日志sheet，reqSheet是入参解析数据sheet，resSheel是反参解析数据sheet
func accessLogWriteToExcel(mg *LogWriteExcelManager, datas []gd_statistics.AccessLog, headMap map[string]string) {
	if len(mg.baseSheet.Rows) == 0 {
		setBaseSheetTitle(mg.baseSheet)
	}

	for _, data := range datas {
		row := mg.baseSheet.AddRow()
		// 关键字
		cell := row.AddCell()
		cell.Value = data.Search

		// code
		cell = row.AddCell()
		cell.Value = fmt.Sprintf("%d", data.Code)

		// msg
		cell = row.AddCell()
		cell.Value = data.Msg

		// 实际请求
		cell = row.AddCell()
		cell.Value = data.RawRequestParams

		// 实际响应
		cell = row.AddCell()
		cell.Value = data.ResponseParams

		// 原始请求
		cell = row.AddCell()
		cell.Value = data.RequestParams
		if mg.reqSheet != nil {
			// 请求参数解析后单独放一个sheet
			writeParamSheet(mg.reqSheet, data.RequestParams, mg.reqTitleIndexMap, headMap)
		}

		// 原始响应
		cell = row.AddCell()
		cell.Value = data.RawResponseParams
		if mg.resSheet != nil {
			// 响应参数解析后单独放一个sheet
			writeParamSheet(mg.resSheet, data.ResponseParams, mg.resTitleIndexMap, headMap)
		}

		// 请求时间
		cell = row.AddCell()
		cell.Value = fmt.Sprintf("%s", time.Unix(data.Timestamp, 0).Format("2006-01-02 15:04:05"))

		// 耗时
		cell = row.AddCell()
		cell.Value = fmt.Sprintf("%.2f", data.Elapsed)
	}
}

// thirdLogWriteToExcel 将日志写入excel， baseSheet是日志sheet，reqSheet是入参解析数据sheet，resSheel是反参解析数据sheet
func thirdLogWriteToExcel(mg *LogWriteExcelManager, datas []gd_statistics.ThirdpartLog) {
	if len(mg.baseSheet.Rows) == 0 {
		setBaseSheetThirdTitle(mg.baseSheet)
	}

	for _, data := range datas {
		row := mg.baseSheet.AddRow()
		// 关键字
		cell := row.AddCell()
		cell.Value = data.Search

		// code
		cell = row.AddCell()
		cell.Value = fmt.Sprintf("%d", data.Code)

		// msg
		cell = row.AddCell()
		cell.Value = data.Msg

		// 请求
		cell = row.AddCell()
		cell.Value = data.RequestParams
		if mg.reqSheet != nil {
			// 请求参数解析后单独放一个sheet
			writeParamSheet(mg.reqSheet, data.RequestParams, mg.reqTitleIndexMap, nil)
		}

		// 响应
		cell = row.AddCell()
		cell.Value = data.ResponseParams
		if mg.resSheet != nil {
			// 响应参数解析后单独放一个sheet
			writeParamSheet(mg.resSheet, data.ResponseParams, mg.resTitleIndexMap, nil)
		}

		// 请求时间
		cell = row.AddCell()
		cell.Value = fmt.Sprintf("%s", time.Unix(data.Timestamp, 0).Format("2006-01-02 15:04:05"))

		// 耗时
		cell = row.AddCell()
		cell.Value = fmt.Sprintf("%.2f", data.Elapsed)
	}
}
