package api

import (
	"archive/zip"
	"box-cost/db/model"
	"box-cost/db/repo"
	"box-cost/log"
	"bytes"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/xuri/excelize/v2"
	"go.mongodb.org/mongo-driver/bson"
	"go.mongodb.org/mongo-driver/bson/primitive"
)

// 生产计划管理
func ProductPlan(r *GinRouter) {

	// 创建生产计划
	r.POST("/plan/create", CreateProductPlan)

	// 获取生产计划详情
	r.GET("/plan/detail/:id", GetProductPlan)

	// 获取生产计划列表
	r.GET("/plan/list", GetProductPlans)

	// 更新生产计划
	r.POST("/plan/update", UpdateProductPlan)

	// 删除生产计划
	r.POST("/plan/delete/:id", DelProductPlan)

	// 下载部件单据
	// r.GET("/bill/plan/download", DownLoadCompBills)
	r.GET("/bill/plan/download", DownLoadPlanBills)

	r.GET("/bill/plan/downloadPdf", DownLoadPlanBillsPdf)

	// 生产成本表
	r.GET("/plan/cost/download", DownLoadPlanCost)

	// 单据批量分配给供应商
	r.GET("/plan/alloc/batch", PlanAllocBatch)

}

// 把某个计划的订单批量分配给供应商
// id 为计划id
// /plan/alloc/batch?id=xxx
func PlanAllocBatch(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	_planId := c.Query("id")
	planId, err := primitive.ObjectIDFromHex(_planId)
	if err != nil {
		return nil, errors.New("planId错误")
	}
	plan := model.ProductPlan{}
	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
		CollectName: repo.CollectionProductPlan,
		Query:       repo.Map{"_id": planId},
	}, &plan)
	if !found || err != nil {
		return nil, errors.New("数据未找到")
	}
	// 获取所有stages单据id
	billIds := make([]string, 0)
	for _, comp := range plan.Pack.Components {
		if comp.Id == "" || len(comp.Stages) == 0 {
			continue
		}
		for _, stage := range comp.Stages {
			billId, _ := primitive.ObjectIDFromHex(stage.BillId)
			if !billId.IsZero() {
				billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
			}
		}

	}
	// 去重单据号
	typeBillIds := removeDuplicationSort(billIds)
	if len(typeBillIds) < 1 {
		return nil, errors.New("未找到单据信息")
	}

	for _, tId := range typeBillIds {
		tidArr := strings.Split(tId, "_")
		// 采购
		billId, _ := primitive.ObjectIDFromHex(tidArr[1])
		if tidArr[0] == "1" {
			repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionBillPurchase, billId.Hex(), &model.PurchaseBill{IsSend: true, SendTime: time.Now()})
		}
		// 工艺
		if tidArr[0] == "2" {
			repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionBillProduce, billId.Hex(), &model.ProduceBill{IsSend: true, SendTime: time.Now()})
		}
		// 成品采购
		if tidArr[0] == "3" {
			repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionBillProduct, billId.Hex(), &model.ProductBill{IsSend: true, SendTime: time.Now()})
		}

	}

	return true, nil

}

// 更新供应商确定数量与plan中stage项的同步
func updateStageCount(billId primitive.ObjectID, planId primitive.ObjectID, idCounts map[string]int, apictx *ApiSession) (interface{}, error) {
	if len(idCounts) == 0 {
		return true, nil
	}

	plan := model.ProductPlan{}
	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
		CollectName: repo.CollectionProductPlan,
		Query:       repo.Map{"_id": planId},
	}, &plan)
	if !found || err != nil {
		return nil, errors.New("数据未找到")
	}
	for _, comp := range plan.Pack.Components {
		if comp.Id == "" || len(comp.Stages) == 0 {
			continue
		}
		for _, stage := range comp.Stages {
			if v, ok := idCounts[stage.Id]; ok {
				stage.ConfirmCount = v
			}
		}

	}
	plan.UpdateTime = time.Now()
	result, err := repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, planId.Hex(), &plan)
	if err != nil {
		log.Error(err)
		fmt.Println(err)
	}
	return result, err

}

type SupplierPlanCost struct {
	*model.ProductPlan
	SupplierId string
}

func DownLoadPlanCost(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	_planId := c.Query("id")
	_supplierId := c.Query("supplierId")
	supplierId, _ := primitive.ObjectIDFromHex(_supplierId)
	planId, _ := primitive.ObjectIDFromHex(_planId)
	if planId.IsZero() {
		return nil, errors.New("planId错误")
	}

	plan := model.ProductPlan{}
	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
		CollectName: repo.CollectionProductPlan,
		Query:       repo.Map{"_id": planId},
	}, &plan)
	if !found || err != nil {
		return nil, errors.New("数据未找到")
	}
	summaryPlans := []*PlanSummary{}
	summaryPlan := GetPlanStatus(&plan, apictx)
	summaryPlans = append(summaryPlans, summaryPlan)
	if len(summaryPlans) < 1 {
		return nil, errors.New("数据不存在")
	}

	f := excelize.NewFile()
	index := f.NewSheet("Sheet1")
	f.SetActiveSheet(index)
	f.SetDefaultFont("宋体")
	planSummaryExcel := NewPlanCostExcel(f)
	planSummaryExcel.Content = &SupplierPlanSummary{
		Plans:      summaryPlans,
		SupplierId: supplierId,
	}
	// 绘制表格
	planSummaryExcel.Title = fmt.Sprintf("生产成本表(%s)%d盒", plan.Name, plan.Total)
	planSummaryExcel.Draws()

	c.Header("Content-Type", "application/octet-stream")
	c.Header("Content-Disposition", "attachment; filename="+"planCost.xlsx")
	c.Header("Content-Transfer-Encoding", "binary")

	err = f.Write(c.Writer)
	if err != nil {
		return nil, err
	}
	return nil, nil

}

func DownLoadPlanBills(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	_planId := c.Query("id")
	planId, err := primitive.ObjectIDFromHex(_planId)
	if err != nil {
		return nil, errors.New("planId错误")
	}
	plan := model.ProductPlan{}
	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
		CollectName: repo.CollectionProductPlan,
		Query:       repo.Map{"_id": planId},
	}, &plan)
	if !found || err != nil {
		return nil, errors.New("数据未找到")
	}
	// 获取所有stages单据id
	billIds := make([]string, 0)
	for _, comp := range plan.Pack.Components {
		if comp.Id == "" || len(comp.Stages) == 0 {
			continue
		}
		for _, stage := range comp.Stages {
			billId, _ := primitive.ObjectIDFromHex(stage.BillId)
			if !billId.IsZero() {
				billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
			}
		}

	}
	// 去重单据号
	typeBillIds := removeDuplicationSort(billIds)
	if len(typeBillIds) < 1 {
		return nil, errors.New("未找到单据信息")
	}
	f := excelize.NewFile()
	f.SetDefaultFont("宋体")
	flagIndex := -1
	companyName := getCompanyName(apictx)

	// 采购 加工 加工-印刷 加工-覆膜 成品采购
	typeRows := []int{0, 0, 0, 0, 0}
	for _, tId := range typeBillIds {
		tidArr := strings.Split(tId, "_")
		var billExcel IExcel
		// 采购
		billId, _ := primitive.ObjectIDFromHex(tidArr[1])
		if tidArr[0] == "1" {
			purchase := model.PurchaseBill{}
			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
				CollectName: repo.CollectionBillPurchase,
				Query:       repo.Map{"_id": billId},
			}, &purchase)
			if !found {
				continue
			}
			sheetName := "采购单"
			index := f.NewSheet(sheetName)
			if flagIndex < 0 {
				flagIndex = index
			}
			// index := f.NewSheet("采购单")
			// f.SetActiveSheet(index)
			billExcel = NewPurchaseBill(f)
			billExcel.SetSheetName(sheetName)
			if purchase.Reviewed == 1 {
				if len(purchase.SignUsers) > 0 {
					signs := []*model.Signature{}
					repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
						CollectName: repo.CollectionSignature,
						Query:       repo.Map{"_id": bson.M{"$in": purchase.SignUsers}},
						Sort:        bson.M{"sort": 1},
					}, &signs)
					billExcel.SetSignatures(signs)
				}

			}
			billExcel.SetContent(&purchase)
			billExcel.SetTitle(fmt.Sprintf("%s原材料采购单", companyName))

			billExcel.SetRow(typeRows[0])
			billExcel.Draws()
			typeRows[0] = billExcel.GetRow() + 5

		}
		// 工艺
		if tidArr[0] == "2" {
			produce := model.ProduceBill{}
			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
				CollectName: repo.CollectionBillProduce,
				Query:       repo.Map{"_id": billId},
			}, &produce)
			if !found {
				continue
			}
			sheetName := "加工单"
			if produce.IsPrint {
				sheetName = "加工单-印刷"
			} else if produce.IsLam {
				sheetName = "加工单-覆膜"
			}
			index := f.NewSheet(sheetName)
			if flagIndex < 0 {
				flagIndex = index
			}
			billExcel = NewProduceBill(f)
			billExcel.SetSheetName(sheetName)
			if produce.Reviewed == 1 {
				if len(produce.SignUsers) > 0 {
					signs := []*model.Signature{}
					repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
						CollectName: repo.CollectionSignature,
						Query:       repo.Map{"_id": bson.M{"$in": produce.SignUsers}},
						Sort:        bson.M{"sort": 1},
					}, &signs)
					billExcel.SetSignatures(signs)
				}

			}
			billExcel.SetContent(&produce)
			billExcel.SetTitle(fmt.Sprintf("%s加工单", companyName))

			if produce.IsPrint {
				billExcel.SetRow(typeRows[2])
				billExcel.Draws()
				typeRows[2] = billExcel.GetRow() + 5
			} else if produce.IsLam {
				billExcel.SetRow(typeRows[3])
				billExcel.Draws()
				typeRows[3] = billExcel.GetRow() + 5
			} else {
				billExcel.SetRow(typeRows[1])
				billExcel.Draws()
				typeRows[1] = billExcel.GetRow() + 5

			}

		}
		// 成品采购
		if tidArr[0] == "3" {
			product := model.ProductBill{}
			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
				CollectName: repo.CollectionBillProduct,
				Query:       repo.Map{"_id": billId},
			}, &product)
			if !found {
				continue
			}
			sheetName := "成品采购单"
			index := f.NewSheet(sheetName)
			if flagIndex < 0 {
				flagIndex = index
			}
			billExcel = NewProductBill(f)
			billExcel.SetSheetName(sheetName)
			if product.Reviewed == 1 {
				if len(product.SignUsers) > 0 {
					signs := []*model.Signature{}
					repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
						CollectName: repo.CollectionSignature,
						Query:       repo.Map{"_id": bson.M{"$in": product.SignUsers}},
						Sort:        bson.M{"sort": 1},
					}, &signs)
					billExcel.SetSignatures(signs)
				}

			}
			billExcel.SetContent(&product)
			billExcel.SetTitle(companyName)

			billExcel.SetRow(typeRows[4])
			billExcel.Draws()
			typeRows[4] = billExcel.GetRow() + 5
		}

	}
	// 设置活跃sheet
	f.SetActiveSheet(flagIndex)
	// 删除默认Sheet1
	f.DeleteSheet("Sheet1")

	c.Header("Content-Type", "application/octet-stream")
	c.Header("Content-Disposition", "attachment; filename="+"bill.xlsx")
	c.Header("Content-Transfer-Encoding", "binary")

	err = f.Write(c.Writer)
	if err != nil {
		return nil, err
	}

	return nil, nil
}

// todo old 功能确定后删除
func DownLoadPlanBillsBack(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	_planId := c.Query("id")
	planId, err := primitive.ObjectIDFromHex(_planId)
	if err != nil {
		return nil, errors.New("planId错误")
	}
	plan := model.ProductPlan{}
	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
		CollectName: repo.CollectionProductPlan,
		Query:       repo.Map{"_id": planId},
	}, &plan)
	if !found || err != nil {
		return nil, errors.New("数据未找到")
	}
	// 获取所有stages单据id
	billIds := make([]string, 0)
	for _, comp := range plan.Pack.Components {
		if comp.Id == "" || len(comp.Stages) == 0 {
			continue
		}
		for _, stage := range comp.Stages {
			billId, _ := primitive.ObjectIDFromHex(stage.BillId)
			if !billId.IsZero() {
				billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
			}
		}

	}
	// 去重单据号
	typeBillIds := removeDuplicationSort(billIds)
	if len(typeBillIds) < 1 {
		return nil, errors.New("未找到单据信息")
	}
	f := excelize.NewFile()
	index := f.NewSheet("Sheet1")
	f.SetActiveSheet(index)
	f.SetDefaultFont("宋体")
	companyName := getCompanyName(apictx)

	row := 0
	for _, tId := range typeBillIds {
		tidArr := strings.Split(tId, "_")

		var billExcel IExcel
		// 采购
		billId, _ := primitive.ObjectIDFromHex(tidArr[1])
		if tidArr[0] == "1" {
			purchase := model.PurchaseBill{}
			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
				CollectName: repo.CollectionBillPurchase,
				Query:       repo.Map{"_id": billId},
			}, &purchase)
			if found {
				billExcel = NewPurchaseBill(f)
				if purchase.Reviewed == 1 {
					if len(purchase.SignUsers) > 0 {
						signs := []*model.Signature{}
						repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
							CollectName: repo.CollectionSignature,
							Query:       repo.Map{"_id": bson.M{"$in": purchase.SignUsers}},
							Sort:        bson.M{"sort": 1},
						}, &signs)
						billExcel.SetSignatures(signs)
					}

				}
				billExcel.SetContent(&purchase)
				billExcel.SetTitle(fmt.Sprintf("%s原材料采购单", companyName))
			}

		}
		// 工艺
		if tidArr[0] == "2" {
			produce := model.ProduceBill{}
			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
				CollectName: repo.CollectionBillProduce,
				Query:       repo.Map{"_id": billId},
			}, &produce)
			if found {
				billExcel = NewProduceBill(f)
				if produce.Reviewed == 1 {
					if len(produce.SignUsers) > 0 {
						signs := []*model.Signature{}
						repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
							CollectName: repo.CollectionSignature,
							Query:       repo.Map{"_id": bson.M{"$in": produce.SignUsers}},
							Sort:        bson.M{"sort": 1},
						}, &signs)
						billExcel.SetSignatures(signs)
					}

				}
				billExcel.SetContent(&produce)
				billExcel.SetTitle(fmt.Sprintf("%s加工单", companyName))
			}

		}
		// 成品采购
		if tidArr[0] == "3" {
			product := model.ProductBill{}
			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
				CollectName: repo.CollectionBillProduct,
				Query:       repo.Map{"_id": billId},
			}, &product)
			if found {
				billExcel = NewProductBill(f)
				if product.Reviewed == 1 {
					if len(product.SignUsers) > 0 {
						signs := []*model.Signature{}
						repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
							CollectName: repo.CollectionSignature,
							Query:       repo.Map{"_id": bson.M{"$in": product.SignUsers}},
							Sort:        bson.M{"sort": 1},
						}, &signs)
						billExcel.SetSignatures(signs)
					}

				}
				billExcel.SetContent(&product)
				billExcel.SetTitle(companyName)
			}
		}
		if billExcel == nil {
			continue
		}
		billExcel.SetRow(row)
		billExcel.Draws()
		row = billExcel.GetRow() + 5
	}

	c.Header("Content-Type", "application/octet-stream")
	c.Header("Content-Disposition", "attachment; filename="+"bill.xlsx")
	c.Header("Content-Transfer-Encoding", "binary")

	err = f.Write(c.Writer)
	if err != nil {
		return nil, err
	}

	return nil, nil
}

func DownLoadPlanBillsPdf(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	_planId := c.Query("id")
	planId, err := primitive.ObjectIDFromHex(_planId)
	if err != nil {
		return nil, errors.New("planId错误")
	}
	plan := model.ProductPlan{}
	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
		CollectName: repo.CollectionProductPlan,
		Query:       repo.Map{"_id": planId},
	}, &plan)
	if !found || err != nil {
		return nil, errors.New("数据未找到")
	}
	// 获取所有stages单据id
	billIds := make([]string, 0)
	for _, comp := range plan.Pack.Components {
		if comp.Id == "" || len(comp.Stages) == 0 {
			continue
		}
		for _, stage := range comp.Stages {
			billId, _ := primitive.ObjectIDFromHex(stage.BillId)
			if !billId.IsZero() {
				billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
			}
		}

	}
	// 去重单据号
	typeBillIds := removeDuplicationSort(billIds)
	if len(typeBillIds) < 1 {
		return nil, errors.New("未找到单据信息")
	}

	companyName := getCompanyName(apictx)
	_planName := plan.Name
	r := regexp.MustCompile(`/`)
	planName := r.ReplaceAllString(_planName, `&`)
	// fmt.Println(planName)
	// 打包pdf的缓存目录
	saveTmpDir := fmt.Sprintf("tmp1/%s", planName)

	if isExistDir(saveTmpDir) {
		os.RemoveAll(saveTmpDir)
	}
	// 记录文件数量
	var wg sync.WaitGroup
	c1 := make(chan int)
	for _, tId := range typeBillIds {
		productName := ""
		supplierName := ""
		serialNumber := ""
		f := excelize.NewFile()
		index := f.NewSheet("Sheet1")
		f.SetActiveSheet(index)
		f.SetDefaultFont("宋体")

		tidArr := strings.Split(tId, "_")
		var billExcel IExcel
		// 采购
		billId, _ := primitive.ObjectIDFromHex(tidArr[1])
		if tidArr[0] == "1" {
			purchase := model.PurchaseBill{}
			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
				CollectName: repo.CollectionBillPurchase,
				Query:       repo.Map{"_id": billId},
			}, &purchase)
			if !found {
				continue
			}
			billExcel = NewPurchaseBill(f)
			if purchase.Reviewed == 1 {
				if len(purchase.SignUsers) > 0 {
					signs := []*model.Signature{}
					repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
						CollectName: repo.CollectionSignature,
						Query:       repo.Map{"_id": bson.M{"$in": purchase.SignUsers}},
						Sort:        bson.M{"sort": 1},
					}, &signs)
					billExcel.SetSignatures(signs)

				}

			}
			productName = purchase.ProductName
			supplierName = purchase.Supplier
			serialNumber = purchase.SerialNumber
			billExcel.SetContent(&purchase)
			billExcel.SetTitle(fmt.Sprintf("%s原材料采购单", companyName))

		}
		// 工艺
		if tidArr[0] == "2" {
			produce := model.ProduceBill{}
			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
				CollectName: repo.CollectionBillProduce,
				Query:       repo.Map{"_id": billId},
			}, &produce)
			if !found {
				continue
			}
			billExcel = NewProduceBill(f)
			if produce.Reviewed == 1 {
				if len(produce.SignUsers) > 0 {
					signs := []*model.Signature{}
					repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
						CollectName: repo.CollectionSignature,
						Query:       repo.Map{"_id": bson.M{"$in": produce.SignUsers}},
						Sort:        bson.M{"sort": 1},
					}, &signs)
					billExcel.SetSignatures(signs)
				}

			}
			productName = produce.ProductName
			supplierName = produce.Supplier
			serialNumber = produce.SerialNumber
			billExcel.SetContent(&produce)
			billExcel.SetTitle(fmt.Sprintf("%s加工单", companyName))

		}
		// 成品采购
		if tidArr[0] == "3" {
			product := model.ProductBill{}
			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
				CollectName: repo.CollectionBillProduct,
				Query:       repo.Map{"_id": billId},
			}, &product)
			if !found {
				continue
			}
			billExcel = NewProductBill(f)
			if product.Reviewed == 1 {
				if len(product.SignUsers) > 0 {
					signs := []*model.Signature{}
					repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
						CollectName: repo.CollectionSignature,
						Query:       repo.Map{"_id": bson.M{"$in": product.SignUsers}},
						Sort:        bson.M{"sort": 1},
					}, &signs)
					billExcel.SetSignatures(signs)
				}

			}
			productName = product.ProductName
			supplierName = product.Supplier
			serialNumber = product.SerialNumber
			billExcel.SetContent(&product)
			billExcel.SetTitle(companyName)
		}
		billExcel.SetIsPdf("true")
		billExcel.Draws()
		buf, _ := f.WriteToBuffer()

		// r := regexp.MustCompile(`/`)
		_productName := r.ReplaceAllString(productName, `&`)
		_supplierName := r.ReplaceAllString(supplierName, `&`)
		targePdfName := fmt.Sprintf("%s-%s-%s.pdf", _supplierName, _productName, serialNumber)
		// fmt.Println(targePdfName)

		wg.Add(1)
		go toPdfAndSaveTask(buf, apictx.Svc.Conf.PdfApiAddr, saveTmpDir, targePdfName, c1, &wg)

	}
	go func() {
		// 等待所有goroutine
		wg.Wait()
		// 关闭channel
		close(c1)
	}()
	// channel关闭后结束后停止遍历接收channel中的值
	for n := range c1 {
		if n == -1 {
			return nil, errors.New("下载失败,请重试")
		}
	}

	c.Header("Content-Type", "application/octet-stream")
	c.Header("Content-Disposition", "attachment; filename="+planName+".zip")
	c.Header("Content-Transfer-Encoding", "binary")

	archive := zip.NewWriter(c.Writer)
	defer archive.Close()
	// 遍历路径信息
	filepath.Walk(saveTmpDir, func(path string, info os.FileInfo, _ error) error {

		// 如果是源路径,提前进行下一个遍历
		if path == saveTmpDir {
			return nil
		}

		// 获取:文件头信息
		header, _ := zip.FileInfoHeader(info)
		header.Name = strings.TrimPrefix(path, saveTmpDir+`/`)

		// 判断:文件是不是文件夹
		if info.IsDir() {
			header.Name += `/`
		} else {
			// 设置:zip的文件压缩算法
			header.Method = zip.Deflate
		}

		// 创建:压缩包头部信息
		writer, _ := archive.CreateHeader(header)
		if !info.IsDir() {
			file, _ := os.Open(path)
			defer file.Close()
			io.Copy(writer, file)
		}
		return nil
	})

	// 删除缓存目录
	os.RemoveAll(saveTmpDir)

	return nil, nil
}

type ToPdfResult struct {
	IsSucc bool
	Err    error
}

func toPdfAndSaveTask(buf *bytes.Buffer, toPdfAddr, saveTmpDir, targetPdfName string, toPdfResult chan<- int, wg *sync.WaitGroup) {
	if buf.Len() < 1<<10 {
		fmt.Println("execl内容为空")
		log.Error("execl内容为空")
		toPdfResult <- -1
		wg.Done()
		return

	}
	res, err := excelToPdf(buf, toPdfAddr)

	if err != nil {
		fmt.Println(err)
		log.Error(err)
		// pdfRes := ToPdfResult{
		// 	IsSucc: false,
		// 	Err:    err,
		// }
		// toPdfResult <- pdfRes
		toPdfResult <- -1
		wg.Done()
		return

	}

	byteData, err := io.ReadAll(res.Body)
	if err != nil {
		fmt.Println(err)
		// pdfRes := ToPdfResult{
		// 	IsSucc: false,
		// 	Err:    err,
		// }
		// toPdfResult <- pdfRes
		toPdfResult <- -1
		wg.Done()
		return
	}
	if len(byteData) < 1 {
		fmt.Println("pdf内容为空")
		log.Error("pdf内容为空")
		toPdfResult <- -1
		wg.Done()
		return
	}

	defer res.Body.Close()

	err = savePdfToTmp(saveTmpDir, targetPdfName, byteData)
	if err != nil {
		// pdfRes := ToPdfResult{
		// 	IsSucc: false,
		// 	Err:    err,
		// }
		// toPdfResult <- pdfRes
		toPdfResult <- -1
		wg.Done()
		return
	}
	// pdfRes := ToPdfResult{
	// 	IsSucc: true,
	// 	Err:    err,
	// }
	// toPdfResult <- pdfRes
	toPdfResult <- 1
	wg.Done()
}

// todo 已弃用 功能稳定后删除
func DownLoadCompBills(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	_planId := c.Query("id")
	compId := c.Query("compId")
	planId, err := primitive.ObjectIDFromHex(_planId)
	if err != nil {
		return nil, errors.New("planId错误")
	}
	plan := model.ProductPlan{}
	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
		CollectName: repo.CollectionProductPlan,
		Query:       repo.Map{"_id": planId},
	}, &plan)
	if !found || err != nil {
		return nil, errors.New("数据未找到")
	}
	// 获取部件单据
	curComp := &model.PackComponent{}
	for _, comp := range plan.Pack.Components {
		if comp.Id == compId {
			curComp = comp
		}
	}
	if curComp.Id == "" {
		return nil, errors.New("该组件不存在")
	}

	// 获取bill
	if len(curComp.Stages) == 0 {
		return nil, errors.New("该组件数据不存在")
	}

	// 获取不同类型的单据id
	billIds := make([]string, 0)
	for _, stage := range curComp.Stages {
		billId, _ := primitive.ObjectIDFromHex(stage.BillId)
		if !billId.IsZero() {
			billIds = append(billIds, fmt.Sprintf("%d_%s", stage.BillType, stage.BillId))
		}
	}

	// 去重单据号
	typeBillIds := removeDuplicationSort(billIds)
	if len(typeBillIds) < 1 {
		return nil, errors.New("未找到单据信息")
	}
	f := excelize.NewFile()
	index := f.NewSheet("Sheet1")
	f.SetActiveSheet(index)
	f.SetDefaultFont("宋体")
	companyName := getCompanyName(apictx)

	row := 0
	for _, tId := range typeBillIds {
		tidArr := strings.Split(tId, "_")

		var billExcel IExcel
		// 采购
		billId, _ := primitive.ObjectIDFromHex(tidArr[1])
		if tidArr[0] == "1" {
			purchase := model.PurchaseBill{}
			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
				CollectName: repo.CollectionBillPurchase,
				Query:       repo.Map{"_id": billId},
			}, &purchase)
			if found {
				billExcel = NewPurchaseBill(f)
				if purchase.Reviewed == 1 {
					if len(purchase.SignUsers) > 0 {
						signs := []*model.Signature{}
						repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
							CollectName: repo.CollectionSignature,
							Query:       repo.Map{"_id": bson.M{"$in": purchase.SignUsers}},
							Sort:        bson.M{"sort": 1},
						}, &signs)
						billExcel.SetSignatures(signs)
					}

				}
				billExcel.SetContent(&purchase)
				billExcel.SetTitle(fmt.Sprintf("%s原材料采购单", companyName))
			}

		}
		// 工艺
		if tidArr[0] == "2" {
			produce := model.ProduceBill{}
			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
				CollectName: repo.CollectionBillProduce,
				Query:       repo.Map{"_id": billId},
			}, &produce)
			if found {
				billExcel = NewProduceBill(f)
				if produce.Reviewed == 1 {
					if len(produce.SignUsers) > 0 {
						signs := []*model.Signature{}
						repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
							CollectName: repo.CollectionSignature,
							Query:       repo.Map{"_id": bson.M{"$in": produce.SignUsers}},
							Sort:        bson.M{"sort": 1},
						}, &signs)
						billExcel.SetSignatures(signs)
					}

				}
				billExcel.SetContent(&produce)
				billExcel.SetTitle(fmt.Sprintf("%s加工单", companyName))
			}

		}
		// 成品采购
		if tidArr[0] == "3" {
			product := model.ProductBill{}
			found, _ := repo.RepoSeachDoc(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
				CollectName: repo.CollectionBillProduct,
				Query:       repo.Map{"_id": billId},
			}, &product)
			if found {
				billExcel = NewProductBill(f)
				if product.Reviewed == 1 {
					if len(product.SignUsers) > 0 {
						signs := []*model.Signature{}
						repo.RepoDocsSearch(apictx.CreateRepoCtx(), &repo.PageSearchOptions{
							CollectName: repo.CollectionSignature,
							Query:       repo.Map{"_id": bson.M{"$in": product.SignUsers}},
							Sort:        bson.M{"sort": 1},
						}, &signs)
						billExcel.SetSignatures(signs)
					}

				}
				billExcel.SetContent(&product)
				billExcel.SetTitle(companyName)
			}
		}
		if billExcel == nil {
			continue
		}
		billExcel.SetRow(row)
		billExcel.Draws()
		row = billExcel.GetRow() + 5
	}

	c.Header("Content-Type", "application/octet-stream")
	c.Header("Content-Disposition", "attachment; filename="+"bill.xlsx")
	c.Header("Content-Transfer-Encoding", "binary")

	err = f.Write(c.Writer)
	if err != nil {
		return nil, err
	}

	return nil, nil
}

// 创建生产计划
func CreateProductPlan(c *gin.Context, apictx *ApiSession) (interface{}, error) {

	var plan model.ProductPlan

	err := c.ShouldBindJSON(&plan)
	if err != nil {
		fmt.Println(err)
		return nil, errors.New("参数错误!")
	}

	if plan.Name == "" {
		return nil, errors.New("生产计划名为空")
	}
	if plan.Total == 0 {
		return nil, errors.New("生产计划数应不为0")
	}

	plan.Status = "process" // 进行中
	plan.CreateTime = time.Now()
	plan.UpdateTime = time.Now()

	result, err := repo.RepoAddDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, &plan)
	return result, err
}

// 获取生产计划信息
func GetProductPlan(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	planId := c.Param("id")
	id, err := primitive.ObjectIDFromHex(planId)
	if err != nil {
		return nil, errors.New("非法id")
	}
	var plan model.ProductPlan
	option := &repo.DocSearchOptions{
		CollectName: repo.CollectionProductPlan,
		Query:       repo.Map{"_id": id},
	}

	found, err := repo.RepoSeachDoc(apictx.CreateRepoCtx(), option, &plan)
	if !found || err != nil {
		log.Info(err)
		return nil, errors.New("数据未找到")
	}

	billStates := map[string]string{}
	billIsSend := map[string]bool{}
	billReviewed := map[string]int32{}
	billIsAck := map[string]bool{}
	if plan.Pack != nil && plan.Pack.Components != nil {
		for _, comp := range plan.Pack.Components {
			if comp.Stages != nil {
				for _, stage := range comp.Stages {

					if len(stage.BillId) > 0 {
						collectName := ""
						// 材料
						if stage.BillType == 1 {
							collectName = repo.CollectionBillPurchase
						}
						// 工艺
						if stage.BillType == 2 {
							collectName = repo.CollectionBillProduce
						}
						// 成品
						if stage.BillType == 3 {
							collectName = repo.CollectionBillProduct
						}

						ok, state := repo.RepoSeachDocMap(apictx.CreateRepoCtx(), &repo.DocSearchOptions{
							CollectName: collectName,
							Query:       repo.Map{"_id": stage.BillId},
							Project:     []string{"status", "isSend", "reviewed", "isAck", "serialNumber", "remark"}})
						if ok {
							billStates[stage.BillId] = state["status"].(string)
							if v, ok := state["isSend"]; ok {
								billIsSend[stage.BillId] = v.(bool)

							} else {
								billIsSend[stage.BillId] = false
							}
							if v, ok := state["reviewed"]; ok {
								billReviewed[stage.BillId] = v.(int32)
							} else {
								billReviewed[stage.BillId] = -1
							}
							if v, ok := state["isAck"]; ok {
								billIsAck[stage.BillId] = v.(bool)

							} else {
								billIsAck[stage.BillId] = false
							}

						}
					}

				}
			}
		}
	}

	return map[string]interface{}{
		"plan":         plan,
		"billStates":   billStates,
		"billIsSend":   billIsSend,
		"billReviewed": billReviewed,
		"billIsAck":    billIsAck,
	}, nil
}

// 获取生产计划列表
func GetProductPlans(c *gin.Context, apictx *ApiSession) (interface{}, error) {

	page, size, query := UtilQueryPageSize(c)
	if _packId, ok := query["packId"]; ok {
		packId, _ := primitive.ObjectIDFromHex(_packId.(string))
		query["pack._id"] = packId
		delete(query, "packId")
	}

	if _name, ok := query["name"]; ok {
		delete(query, "name")
		query["name"] = bson.M{"$regex": _name.(string)}
	}

	option := &repo.PageSearchOptions{
		CollectName: repo.CollectionProductPlan,
		Query:       query,
		Page:        page,
		Size:        size,
		Sort:        bson.M{"createTime": -1},
		Project:     []string{"_id", "thumbnail", "name", "updateTime", "createTime", "createUser", "total", "totalPrice", "status"},
	}
	return repo.RepoPageSearch(apictx.CreateRepoCtx(), option)
}

// 更新生产计划
func UpdateProductPlan(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	var plan model.ProductPlan
	err := c.ShouldBindJSON(&plan)
	if err != nil {
		return nil, errors.New("参数错误")
	}
	if plan.Id.Hex() == "" {
		return nil, errors.New("id的为空")
	}
	plan.UpdateTime = time.Now()
	return repo.RepoUpdateSetDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, plan.Id.Hex(), &plan)
}

// 删除生产计划
func DelProductPlan(c *gin.Context, apictx *ApiSession) (interface{}, error) {
	planId := c.Param("id")
	if planId == "" {
		return nil, errors.New("id为空")
	}

	return repo.RepoDeleteDoc(apictx.CreateRepoCtx(), repo.CollectionProductPlan, planId)
}