package controllers

import (
	"archive/zip"
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	service "gitlab.fjmaimaimai.com/allied-creation/performance/pkg/application/evaluation_template"
	templateCommand "gitlab.fjmaimaimai.com/allied-creation/performance/pkg/application/evaluation_template/command"
	"gitlab.fjmaimaimai.com/allied-creation/performance/pkg/application/import/command"
	"gitlab.fjmaimaimai.com/allied-creation/performance/pkg/constant"
	"gitlab.fjmaimaimai.com/allied-creation/performance/pkg/infrastructure/xredis"
	"gitlab.fjmaimaimai.com/allied-creation/performance/pkg/utils"
	"golang.org/x/text/encoding/simplifiedchinese"
	"golang.org/x/text/transform"
	"io"
	"io/ioutil"
	"mime/multipart"
	"os"
	"path"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/linmadan/egglib-go/core/application"
	"github.com/linmadan/egglib-go/utils/tool_funs"
	"github.com/linmadan/egglib-go/web/beego"
	"github.com/xuri/excelize/v2"
	"gitlab.fjmaimaimai.com/allied-creation/performance/pkg/application/factory"
	"gitlab.fjmaimaimai.com/allied-creation/performance/pkg/domain"
	"gitlab.fjmaimaimai.com/allied-creation/performance/pkg/port/beego/middlewares"
)

type ImportController struct {
	beego.BaseController
}

func (controller *ImportController) Import() {
	_, header, err := controller.GetFile("file")
	if err != nil {
		controller.Response(nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, "上传错误:"+err.Error()))
		return
	}
	file, err := header.Open()
	if err != nil {
		controller.Response(nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, "上传错误:"+err.Error()))
		return
	}
	reader, err := excelize.OpenReader(file)
	if err != nil {
		controller.Response(nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, "上传错误:"+err.Error()))
		return
	}
	index := reader.GetActiveSheetIndex()
	rows, err := reader.GetRows(reader.GetSheetName(index))
	if err != nil {
		controller.Response(nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, "读取excel错误:"+err.Error()))
		return
	}
	formType := controller.GetString("type")
	switch formType {
	case "PerformanceDimension":
		dimensions, err := domain.LoadPerformanceDimensions(rows)
		if err != nil {
			controller.Response(nil, application.ThrowError(application.ARG_ERROR, err.Error()))
		}
		if err, list := controller.parseTemplateNodeContent(dimensions); err != nil {
			controller.Response(nil, application.ThrowError(application.ARG_ERROR, err.Error()))
		} else {
			controller.Response(tool_funs.SimpleWrapGridMap(int64(len(list)), list), nil)
		}
	default:
		controller.Response(nil, application.ThrowError(application.ARG_ERROR, "请确认您导入的表单类型"))
	}
}

func (controller *ImportController) parseTemplateNodeContent(data []*domain.PerformanceDimension) (error, []*domain.NodeContent) {
	nodeContents := make([]*domain.NodeContent, 0)

	transactionContext, err := factory.StartTransaction()
	if err != nil {
		return err, nodeContents
	}
	defer func() {
		transactionContext.RollbackTransaction()
	}()
	// 获取当前公司下的默认规则
	ua := middlewares.GetUser(controller.Ctx)

	// 系统默认规则确认
	ruleRepository := factory.CreateEvaluationRuleRepository(map[string]interface{}{"transactionContext": transactionContext})
	_, rules, err := ruleRepository.Find(map[string]interface{}{"companyId": ua.CompanyId, "sysType": domain.EvaluationSysTypeSystem, "limit": 1})
	if err != nil {
		return err, nodeContents
	}
	var ruleId = int64(0)
	if len(rules) == 0 {
		newRule := domain.GenerateSysRule(ua.CompanyId) // 生成一个系统默认规则
		if rule, err := ruleRepository.Insert(newRule); err != nil {
			return err, nodeContents
		} else {
			ruleId = rule.Id
		}
		if err := transactionContext.CommitTransaction(); err != nil {
			return err, nodeContents
		}
	} else {
		ruleId = rules[0].Id
	}

	// 评估内容主导人名称
	evaluatorNames := make([]string, 0)
	evaluatorMap := map[string]string{}

	weightTotal := 0.0
	for i := range data {
		dimension := data[i]
		for i2 := range dimension.PerformanceModule {
			nc := &domain.NodeContent{}
			nc.Category = dimension.Name // 类别

			module := dimension.PerformanceModule[i2]
			nc.Name = module.ModuleName                 // 名称
			nc.RuleId = ruleId                          // 规则ID
			sIndex := strings.Index(module.Weight, "%") // 权重
			if sIndex != -1 {
				iWeight, _ := strconv.ParseFloat(module.Weight[:sIndex], 64)
				nc.Weight = iWeight
			} else {
				nc.Weight = 0
			}
			weightTotal += nc.Weight                     // 总权重
			nc.PromptTitle = ""                          // 提示项标题
			nc.PromptText = module.Standard              // 提示项内容
			nc.EntryItems = make([]*domain.EntryItem, 0) // 输入项
			for i3 := range module.Target {
				target := module.Target[i3]
				nc.EntryItems = append(nc.EntryItems, &domain.EntryItem{
					Title:      target.Task,       // 输入型标题
					HintText:   "",                // 输入项提示文本
					Definition: target.Definition, //定义
				})
			}
			// 没有任何输入项时,默认1个
			if len(nc.EntryItems) == 0 {
				nc.EntryItems = append(nc.EntryItems, &domain.EntryItem{
					Title:    "填写反馈",
					HintText: "",
				})
			}

			// 必填项
			nc.Required = module.Required
			// 项目评估人
			if module.Evaluator == "HRBP" {
				nc.EvaluatorId = -1
			} else {
				if len(module.Evaluator) > 0 {
					evaluatorNames = append(evaluatorNames, module.Evaluator) // 项目评估人名称数组
					evaluatorMap[nc.Category+nc.Name] = module.Evaluator      // k,v = (类别+名称, 项目评估人名称)
				}
			}
			// 指标类型(0指标、1任务)
			nc.IndicatorType = module.IndicatorType

			nodeContents = append(nodeContents, nc)
		}
	}

	// 权重总数不等于100%,提示报错
	if weightTotal != 100 {
		sprintf := fmt.Sprintf("当前导入的总权重值为:%s%%(必须等于100%%)", utils.FormatFloatDecimal(weightTotal, 2))
		return application.ThrowError(application.INTERNAL_SERVER_ERROR, sprintf), nodeContents
	}

	if len(evaluatorNames) > 0 {
		userRepository := factory.CreateUserRepository(map[string]interface{}{"transactionContext": transactionContext})
		_, users, err := userRepository.Find(map[string]interface{}{"companyId": ua.CompanyId, "names": evaluatorNames})
		if err != nil {
			return err, nodeContents
		}
		nameIdMap := map[string]int64{}
		for i := range users {
			nameIdMap[users[i].Name] = users[i].Id
		}

		// 名称 -> ID
		var nc *domain.NodeContent
		for i := range nodeContents {
			nc = nodeContents[i]
			if nc.EvaluatorId == -1 { // HRBP
				continue
			}
			if evaluatorName, ok := evaluatorMap[nc.Category+nc.Name]; ok {
				if userId, ok := nameIdMap[evaluatorName]; ok {
					nc.EvaluatorId = userId
				}
			}
		}
	}

	return nil, nodeContents
}

func (controller *ImportController) ZipVerify() {
	in := &command.VerifyKeyCommand{}
	if err := controller.Unmarshal(in); err != nil {
		controller.Response(nil, application.ThrowError(application.ARG_ERROR, err.Error()))
	} else {
		bytes, err := xredis.GetBytes(in.VerifyKey)
		if err == nil {
			out := &command.OutResult{}
			err = json.Unmarshal(bytes, out)
			if err == nil {
				controller.Response(out, nil)
				return
			}
		}

		// 有异常直接返回完成标记
		if err != nil {
			out := &command.OutResult{}
			out.Status = "completed"
			controller.Response(out, nil)
		}
	}
}

func (controller *ImportController) ZipImport() {
	_, header, err := controller.GetFile("file")
	if err != nil {
		controller.Response(nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, "上传错误:"+err.Error()))
		return
	}
	file, err := header.Open()
	if err != nil {
		controller.Response(nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, "文件错误:"+err.Error()))
		return
	}

	// 写入本地
	zipPath, day, id, err := controller.writeLocal(file)
	if err != nil {
		controller.Response(nil, application.ThrowError(application.INTERNAL_SERVER_ERROR, err.Error()))
		return
	}

	out := command.OutResult{
		Status:  "uncompleted",
		Success: make([]command.ErrorI, 0),
		Failure: make([]command.ErrorI, 0),
	}
	key := xredis.CreateUploadZipKey(id)
	_ = xredis.Set(key, out, 2*time.Hour)
	controller.Response(map[string]string{"key": key}, nil)
	go func() {
		// 解压目标文件夹
		dstDir := constant.UPLOAD_ZIP_PATH + "/" + day + "-" + id
		fileNames, err := controller.Unzip(zipPath, dstDir)
		if err != nil {
			_ = xredis.Remove(key)
			_ = os.RemoveAll(dstDir)
		} else {
			success := make([]command.ErrorI, 0)
			failure := make([]command.ErrorI, 0)
			for i := range fileNames {
				fn := fileNames[i]
				f, err := os.Open(path.Join(dstDir, fn))
				if err != nil {
					failure = append(failure, command.ErrorI{
						FileName: fn,
						Message:  err.Error(),
					})
					continue
				}
				if err = controller.parserAndInsert(f, fn); err != nil {
					failure = append(failure, command.ErrorI{
						FileName: fn,
						Message:  err.Error(),
					})
				} else {
					success = append(success, command.ErrorI{
						FileName: fn,
						Message:  "",
					})
				}
			}

			// 解析完成
			out = command.OutResult{
				Status:  "completed",
				Success: success,
				Failure: failure,
			}

			// 30分钟后失效
			_ = xredis.Set(key, out, 30*time.Minute)

			// 删除临时文件夹
			_ = os.RemoveAll(dstDir)
		}

	}()

}

func (controller *ImportController) writeLocal(file multipart.File) (string, string, string, error) {
	var err error
	id, err := utils.NewSnowflakeId()
	if err != nil {
		return "", "", "", err
	}
	id2 := fmt.Sprintf("%v", id)
	day := time.Now().Format("2006-01-02")
	zipPath := constant.UPLOAD_ZIP_PATH + "/" + day + "-" + id2 + "/" + "temp"

	var fd *os.File

	dir := filepath.Dir(zipPath)
	err = os.MkdirAll(dir, os.ModePerm)
	if err != nil {
		return "", "", "", err
	}

	fd, err = os.OpenFile(zipPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
	if err != nil {
		return "", "", "", err
	}
	defer func(fd *os.File) {
		if err = fd.Close(); err != nil {

		}
	}(fd)

	wt := bufio.NewWriter(fd)
	_, err = file.Seek(0, io.SeekStart)
	if err != nil {
		return "", "", "", err
	}
	_, err = io.Copy(wt, file)
	if err != nil {
		return "", "", "", err
	}

	err = wt.Flush()
	if err != nil {
		return "", "", "", err
	}

	return zipPath, day, id2, err
}

func (controller *ImportController) Unzip(zipPath, dstDir string) ([]string, error) {
	// 文件名称
	fileNames := make([]string, 0)
	// open zip file
	reader, err := zip.OpenReader(zipPath)
	if err != nil {
		return fileNames, err
	}
	defer reader.Close()

	var decodeName string
	for _, f := range reader.File {
		if f.Flags == 0 { // 如果标致位是0,则是默认的本地编码(默认为gbk),如果标志为是 1 << 11也就是 2048(则是utf-8编码)
			r := bytes.NewReader([]byte(f.Name))
			decoder := transform.NewReader(r, simplifiedchinese.GB18030.NewDecoder())
			content, _ := ioutil.ReadAll(decoder)
			decodeName = string(content)
		} else {
			decodeName = f.Name
		}

		if err := controller.unzipFile(f, decodeName, dstDir); err != nil {
			return fileNames, err
		} else {
			if !(f.FileInfo().IsDir()) {
				fileNames = append(fileNames, decodeName)
			}
		}
	}
	return fileNames, nil
}

// 解析文件并插入数据
func (controller *ImportController) parserAndInsert(file *os.File, fileName string) error {
	defer func() {
		err := file.Close()
		if err != nil {
			return
		}
	}()

	reader, err := excelize.OpenReader(file)
	if err != nil {
		return application.ThrowError(application.INTERNAL_SERVER_ERROR, "上传错误:"+err.Error())
	}
	index := reader.GetActiveSheetIndex()
	rows, err := reader.GetRows(reader.GetSheetName(index))
	if err != nil {
		return application.ThrowError(application.INTERNAL_SERVER_ERROR, "读取excel错误:"+err.Error())
	}
	dimensions, err := domain.LoadPerformanceDimensions(rows)
	if err != nil {
		return application.ThrowError(application.ARG_ERROR, err.Error())
	}
	if err, list := controller.parseTemplateNodeContent(dimensions); err != nil {
		return application.ThrowError(application.ARG_ERROR, err.Error())
	} else {
		if len(list) == 0 {
			return application.ThrowError(application.ARG_ERROR, "没有数据内容")
		}

		var nameWithSuffix = path.Base(fileName)
		var suffix = path.Ext(nameWithSuffix)
		var nameOnly = strings.TrimSuffix(nameWithSuffix, suffix)

		ruService := service.NewEvaluationTemplateService()
		in := &templateCommand.CreateTemplateCommand{}

		ua := middlewares.GetUser(controller.Ctx)
		in.CompanyId = ua.CompanyId
		in.CreatorId = ua.UserId
		in.Name = nameOnly
		in.Describe = ""
		in.NodeContents = list
		in.State = domain.TemplateStateWaitActive

		if _, err := ruService.Create(in); err != nil {
			return application.ThrowError(application.ARG_ERROR, err.Error())
		}
	}
	return nil
}

func (controller *ImportController) unzipFile(f *zip.File, fName string, dstDir string) error {
	// create the directory of file
	filePath := path.Join(dstDir, fName)
	if f.FileInfo().IsDir() {
		if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
			return err
		}
		return nil
	}
	if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
		return err
	}

	// open the file
	rc, err := f.Open()
	if err != nil {
		return err
	}
	defer rc.Close()

	// create the file
	w, err := os.Create(filePath)
	if err != nil {
		return err
	}
	defer w.Close()

	// save the decompressed file content
	_, err = io.Copy(w, rc)
	return err
}