/*
 * @description:层级文件夹数据操作相关
 * @Author: CP
 * @Date: 2020-09-11 14:43:58
 * @FilePath: \construction_management\services\tree_service.go
 */
package services

import (
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"strconv"
	"time"

	"github.com/kataras/iris/v12"
	"go.mod/comm"
	"go.mod/conf"
	"go.mod/dao"
	"go.mod/datasource"
	"go.mod/models"
	"go.mod/web/viewmodels"
)

//定义项目Service接口
type TreeService interface {
	//ValidManager(code string, account string, password string) error
	ValidRule(ctx iris.Context) (viewmodels.Tree, error)
	Create(data viewmodels.Tree) error
	GetAllProject(projectId int) *viewmodels.Tree
	GetAllContract(projectId int) *viewmodels.FolderContract
	Rename(treevm viewmodels.Tree, projectId int) error
	GetFolderAndBid(id int, projectId int) ([]models.CmTree, error)
	DeleteFolderAndBid(id int, projectId int) error
	Move(id int, moveId int, projectId int) error
}

//返回service操作类
type treeService struct {
	dao *dao.TreeDao
}

//创建项目service
func NewTreeService() TreeService {
	return &treeService{
		dao: dao.NewTreeDao(datasource.InstanceDbMaster()),
	}
}

// 文件夹规则验证
func (s *treeService) ValidRule(ctx iris.Context) (viewmodels.Tree, error) {
	folderVaild := viewmodels.Tree{}
	err := ctx.ReadJSON(&folderVaild)
	if err != nil {
		log.Println("folder-ValidRule-ReadForm转换异常, error=", err)
		return folderVaild, err
	}

	err = folderVaild.Validate()
	if err != nil {
		log.Println("文件夹验证, error=", err)
		return folderVaild, err
	}

	return folderVaild, nil
}

// 获得项目下 相关文件夹-整个树结构
func (s *treeService) GetAllProject(projectId int) *viewmodels.Tree {
	datalist := s.dao.GetAllTree(projectId)
	folderlist := make([]*viewmodels.Tree, 0)
	// 生成根
	folder := &viewmodels.Tree{}
	id, _ := comm.AesEncrypt(strconv.Itoa(0), conf.SignSecret)
	parentId, _ := comm.AesEncrypt(strconv.Itoa(-1), conf.SignSecret)
	folder.Id = id
	folder.Name = "root"
	folder.Isfolder = 1
	folder.ParentId = parentId
	folderlist = append(folderlist, folder)
	// 加入数据
	for _, data := range datalist {
		folder := &viewmodels.Tree{}
		//folder.Id = comm.Encrypt([]byte(conf.SignSecret), []byte(strconv.Itoa(data.Id)))
		id, _ := comm.AesEncrypt(strconv.Itoa(data.Id), conf.SignSecret)
		parentId, _ := comm.AesEncrypt(strconv.Itoa(data.ParentId), conf.SignSecret)
		projectId, _ := comm.AesEncrypt(strconv.Itoa(data.ProjectId), conf.SignSecret)
		serial, _ := comm.AesEncrypt(strconv.Itoa(data.Serial), conf.SignSecret)
		bidsectionId, _ := comm.AesEncrypt(strconv.Itoa(data.BidsectionId), conf.SignSecret)
		folder.Id = id
		folder.Name = data.Name
		folder.ParentId = parentId
		folder.ProjectId = projectId
		folder.BidsectionId = bidsectionId
		folder.Depth = data.Depth + 1
		folder.Serial = serial
		folder.Ancounts = data.Ancounts
		folder.Isfolder = data.Isfolder
		folder.IsEnd = false
		//folder.Leaf = true
		folder.HasFolder = false
		folder.IsBid = false
		folder.CreateTime = data.CreateTime.Format(conf.SysTimeform)
		folderlist = append(folderlist, folder)
	}

	//fmt.Println(folderlist)

	//var data []*viewmodels.Tree
	// data := make([]*viewmodels.Tree, 0)
	// for i, _ := range folderlist {
	// 	var a *viewmodels.Tree
	// 	a = &folderlist[i]
	// 	data = append(data, a)
	// }

	node := folderlist[0]      //父节点
	maketree(folderlist, node) //调用生成tree
	//transformjson(node)    //转化为json
	return node
}

// 获得合同管理的目录
func (s *treeService) GetAllContract(projectId int) *viewmodels.FolderContract {
	datalist := s.dao.GetAllTree(projectId)
	folderlist := make([]viewmodels.FolderContract, 0)
	// 生成根
	folder := viewmodels.FolderContract{}
	id, _ := comm.AesEncrypt(strconv.Itoa(0), conf.SignSecret)
	parentId, _ := comm.AesEncrypt(strconv.Itoa(-1), conf.SignSecret)
	folder.Id = id
	folder.Name = "root"
	folder.ParentId = parentId
	folder.Isfolder = 1
	folderlist = append(folderlist, folder)
	// 加入数据
	for _, data := range datalist {
		folder := viewmodels.FolderContract{}
		id, _ := comm.AesEncrypt(strconv.Itoa(data.Id), conf.SignSecret)
		parentId, _ := comm.AesEncrypt(strconv.Itoa(data.ParentId), conf.SignSecret)
		projectId, _ := comm.AesEncrypt(strconv.Itoa(data.ProjectId), conf.SignSecret)
		bidsectionId, _ := comm.AesEncrypt(strconv.Itoa(data.BidsectionId), conf.SignSecret)
		folder.Id = id
		folder.Name = data.Name
		folder.ParentId = parentId
		folder.ProjectId = projectId
		folder.BidsectionId = bidsectionId
		// 合同数据
		folder.Contracts = data.Contracts
		folder.ContractsIncome = data.ContractsIncome
		folder.ContractsReturned = data.ContractsReturned
		// 汇款进度
		ContractsIncome, err := strconv.ParseFloat(data.ContractsIncome, 64)
		if err != nil {
			ContractsIncome = 0
		}
		ContractsReturned, err := strconv.ParseFloat(data.ContractsReturned, 64)
		if err != nil || ContractsReturned == 0 {
			ContractsReturned = 0
			folder.ContractsIncomeProgress = "0%"
		} else {
			folder.ContractsIncomeProgress = fmt.Sprintf("%.0f", (ContractsReturned/ContractsIncome)*100) + "%"
		}

		folder.ContractsPay = data.ContractsPay
		folder.ContractsPaid = data.ContractsPaid
		// 支付进度
		ContractsPay, err := strconv.ParseFloat(data.ContractsPay, 64)
		if err != nil {
			ContractsPay = 0
		}
		ContractsPaid, err := strconv.ParseFloat(data.ContractsPaid, 64)
		if err != nil || ContractsPaid == 0 {
			ContractsPaid = 0
			folder.ContractsPayProgress = "0%"
		} else {
			folder.ContractsPayProgress = fmt.Sprintf("%.0f", (ContractsPaid/ContractsPay)*100) + "%"
		}

		folder.Isfolder = data.Isfolder
		folder.IsEnd = false

		folder.HasFolder = false
		folder.IsBid = false
		folderlist = append(folderlist, folder)
	}

	//fmt.Println(folderlist)

	var data []*viewmodels.FolderContract
	data = make([]*viewmodels.FolderContract, 0)
	for i, _ := range folderlist {
		var a *viewmodels.FolderContract
		a = &folderlist[i]
		data = append(data, a)
	}

	node := &folderlist[0] //父节点
	comm.MakeFolderContract(data, node)
	return node
}

// 新增一个文件夹
func (s *treeService) Create(data viewmodels.Tree) error {
	// 类型校验
	folder := models.CmTree{}
	folder.Name = data.Name
	// 项目ID
	ProjectId, err := strconv.Atoi(data.ProjectId)
	if err != nil {
		return err
	}

	// 获得该目录ID
	Id, err := comm.AesDecrypt(data.Id, conf.SignSecret)
	if err != nil {
		return err
	}
	IdInt, err1 := strconv.Atoi(Id)
	if err1 != nil {
		return err1
	}

	// 该目录中是否有标段
	bidlist := s.dao.GetBidsection(IdInt)
	if len(bidlist) > 0 {
		return errors.New("该文件夹已存在标段，不能在新增目录")
	}

	// 获得该深度的文件夹最大序号
	serial := 0
	depth := data.Depth + 1
	if data.Depth == -1 {
		// 1-创建顶级目录--获得最大序号
		datalist := s.dao.GetAllDepth(depth, ProjectId)
		maxIndex := len(datalist)
		if maxIndex != 0 {
			serial = datalist[maxIndex-1].Serial + 1
		}
	} else {
		// 1-创建次目录-获得次级目录最大序号
		treeNode := s.dao.Get(IdInt, ProjectId)
		// 去除前端depth作用-在这里获得下级目录的深度
		depth = treeNode.Depth + 1
		if treeNode.Id == 0 {
			return errors.New("上级目录不正确")
		}
		attribution := fmt.Sprintf("%s%d-", treeNode.Attribution, treeNode.Serial)
		datalist := s.dao.GetALLDepthByAttribution(depth, ProjectId, attribution)
		maxIndex := len(datalist)
		if maxIndex != 0 {
			serial = datalist[maxIndex-1].Serial + 1
		}

		// 设置归属
		folder.Attribution = attribution //treeNode.Attribution + strconv.Itoa(treeNode.Serial) + "-"
		folder.ParentId = IdInt
	}

	folder.ProjectId = ProjectId
	folder.Serial = serial
	folder.Depth = depth
	folder.Isfolder = 1
	folder.CreateTime = time.Now()
	folder.UpdateTime = time.Now()
	folder.ContractsIncome = "0"
	folder.ContractsPaid = "0"
	folder.ContractsPay = "0"
	folder.ContractsReturned = "0"
	err = s.dao.Create(&folder)
	if err != nil {
		log.Println("添加目录出错, error=", err)
		return errors.New("添加目录出错")
	}
	return nil
}

// 重命名
func (s *treeService) Rename(data viewmodels.Tree, projectId int) error {
	// 获得该目录ID
	Id, err := comm.AesDecrypt(data.Id, conf.SignSecret)
	if err != nil {
		return err
	}
	IdInt, err1 := strconv.Atoi(Id)
	if err1 != nil {
		return err1
	}

	// 获得树信息
	treeData := s.dao.Get(IdInt, projectId)
	if treeData.Id == 0 {
		return errors.New("修改的目录不合法")
	}
	treeNode := models.CmTree{}
	treeNode.Id = IdInt
	treeNode.Name = data.Name
	if treeData.Isfolder == 1 {
		// 目录
		err = s.dao.Update(&treeNode, []string{"Name"})
	} else {
		// 标段
		treeNode.BidsectionId = treeData.BidsectionId
		err = s.dao.Rename(&treeNode, []string{"Name"})
	}

	if err != nil {
		return err
	}
	return nil
}

// 获得该目录下所有的目录和标段
func (s *treeService) GetFolderAndBid(id int, projectId int) ([]models.CmTree, error) {
	// 获得该目录ID
	// id, err := comm.AesDecrypt(id, conf.SignSecret)
	// if err != nil {
	// 	return nil, errors.New("目录解析错误")
	// }
	// IdInt, err := strconv.Atoi(id)
	// if err != nil {
	// 	return nil, errors.New("目录解析错误")
	// }
	// 获得该目录下所有数据
	folder := s.dao.Get(id, projectId)
	if folder.Id == 0 {
		return nil, errors.New("目录不合法")
	}
	attribution := fmt.Sprintf("%s%d-", folder.Attribution, folder.Serial)
	// 获得目录归属
	dataList := s.dao.GetFolderAndBid(folder.ProjectId, attribution)
	return dataList, nil
}

// 删除该目录和所属的目录和标段
func (s *treeService) DeleteFolderAndBid(id int, projectId int) error {
	// 获得该目录ID
	// id, err := comm.AesDecrypt(id, conf.SignSecret)
	// if err != nil {
	// 	return errors.New("目录解析错误")
	// }
	// IdInt, err := strconv.Atoi(id)
	// if err != nil {
	// 	return errors.New("目录解析错误")
	// }
	// 获得该目录下 归属关系
	folder := s.dao.Get(id, projectId)
	if folder.Id == 0 {
		return errors.New("目录不合法")
	}
	attribution := fmt.Sprintf("%s%d-", folder.Attribution, folder.Serial)
	// --逻辑删除 目录和数据
	err := s.dao.DeleteFolderAndBid(folder.Id, folder.ProjectId, attribution)
	if err != nil {
		return err
	}
	// TODO -加入堆栈 物理删除数据

	return nil
}

// 移动目录
func (s *treeService) Move(id int, moveId int, projectId int) error {
	// 获得项目目录
	treeNode := s.dao.Get(id, projectId)

	if treeNode.Id == 0 {
		return errors.New("目录或标段解析错误")
	}

	targetFolder := &models.CmTree{}
	// id为0是跟目录
	if moveId == 0 {
		// id
		targetFolder.Id = 0
		// 1.获得最大序号
		// seriallist := s.dao.GetALLDepthByAttribution(0, projectId, "")
		// maxIndex := len(seriallist)
		// serial := 0
		// if maxIndex != 0 {
		// 	serial = seriallist[maxIndex-1].Serial + 1
		// }
		// targetFolder.Serial = serial
		targetFolder.Serial = 0
		// 2.归属
		targetFolder.Attribution = ""
		// 3.深度depth-根目录为-1
		targetFolder.Depth = -1
		// 4.为目录
		targetFolder.Isfolder = 1
		// 5.项目ID
		targetFolder.ProjectId = projectId

		// fmt.Println(targetFolder)
		// if targetFolder.Id == 0 {
		// 	return errors.New("目录解析错误-测试")
		// }
	} else {
		targetFolder = s.dao.Get(moveId, projectId)
		if targetFolder.Id == 0 {
			return errors.New("目录解析错误")
		}
	}

	// 目录必须是目录
	if targetFolder.Isfolder == 0 {
		return errors.New("目标不是文件夹")
	}

	// 目录的目标不能是自己
	if id == moveId {
		return errors.New("目标目录是原目录-不能移动")
	}

	// 移动的是目录
	if treeNode.Isfolder == 1 {
		// 1.目录下不能有标段
		// 被移动的目录存在标段
		moveBidList := s.dao.GetBidsection(moveId)
		if len(moveBidList) > 0 {
			return errors.New("该目录中存在标段，不能移动")
		}
	} else {
		// 标段
		// 1.移动的目录是否为叶子节点
		moveBidList := s.dao.GetChildFolder(moveId)
		if len(moveBidList) > 0 {
			return errors.New("该目录中存在其他目录，不能移动")
		}
	}

	err := s.dao.Move(treeNode, targetFolder)
	if err != nil {
		return err
	}
	return nil
}

// 创建一颗树...interface{}
func maketree(Data []*viewmodels.Tree, node *viewmodels.Tree) { //参数为父节点，添加父节点的子节点指针切片
	childs, _ := havechild(Data, node) //判断节点是否有子节点并返回
	if childs != nil {
		// fmt.Printf("\n")
		// fmt.Println(*node)
		// fmt.Println("子节点：")
		// for _, v := range childs {
		// 	fmt.Println(*v)
		// } //打印

		// 子节点总数
		Total := len(childs)
		node.ChildsTotal = Total
		// 标记最后一个元素
		end := Total - 1
		childs[end].IsEnd = true

		// 往父节点添加子节点
		node.Children = append(node.Children, childs[0:]...) //添加子节点
		for _, v := range childs {                           //查询子节点的子节点，并添加到子节点
			_, has := havechild(Data, v)
			if has {
				// 递归添加节点
				maketree(Data, v)
			}
			// else {
			// 	// 叶子节点
			// 	node.Leaf = false
			// }
			// 目录下是否包含标段
			if v.Isfolder == 0 {
				node.IsBid = true
			} else {
				node.HasFolder = true
			}
		}
	}
}

// 是否有子树
func havechild(Data []*viewmodels.Tree, node *viewmodels.Tree) (child []*viewmodels.Tree, yes bool) {
	for _, v := range Data {
		if v.ParentId == node.Id {
			child = append(child, v)
		}
	}
	if child != nil {
		yes = true
	}
	return
}

func transformjson(Data *viewmodels.Tree) { //转为json

	Jsondata, _ := json.Marshal(Data)

	fmt.Println(string(Jsondata))
}

//now := comm.NowUnix()
//comm.FormatFromUnixTime(now)
// ParentIdInt := 0
// if data.ParentId != "" {
// 	ParentId, err := comm.AesDecrypt(data.ParentId, conf.SignSecret)
// 	if err != nil {
// 		return err
// 	}
// 	ParentIdInt, err = strconv.Atoi(ParentId)
// 	if err != nil {
// 		ParentIdInt = 0
// 	}
// }
