package routers

import (
	"context"
	"encoding/json"
	parsefmt "fmt"
	"ops/agents"
	"ops/models"
	"slices"
	"time"

	"github.com/gin-gonic/gin"
	"gorm.io/gorm"
)

var (
	workOrderUserGroup TabUserGroups
	workOrderAdmins    []uint
)

// updateWorkOrderAdminsCash 刷新工单管理员缓存
func WorkOrderUpdateAdminsCash() {
	workOrderAdmins = nil
	workOrderAdmins = append(workOrderAdmins, 1) // id=1 超级管理员
	var binds []TabUserGroupBinds
	models.DB.Where("group_id = ?", workOrderUserGroup.ID).Find(&binds)
	for _, item := range binds {
		if !slices.Contains(workOrderAdmins, item.UserID) {
			workOrderAdmins = append(workOrderAdmins, item.UserID)
		}
	}
}

// canModifyWorkOrder 判断是否有权限修改/删除工单（创建者或管理员）
func canModifyWorkOrder(userID, creatorUserID uint) bool {
	if slices.Contains(workOrderAdmins, userID) {
		return true
	}
	return userID == creatorUserID
}

// ---------- 数据表结构 ----------

type TabWorkOrder struct {
	ID            uint           `gorm:"primarykey"`
	UserID        uint           `gorm:"not null;comment:创建人ID"`
	Title         string         `gorm:"size:200;not null;comment:工单标题"`
	Description   string         `gorm:"type:text;comment:问题描述"`
	CurrentStatus string         `gorm:"size:50;default:pending;comment:当前状态: pending-待处理 checked-已检查 parts_ordered-已下单零件 repaired-已维修 returned-已送还 unrepairable-无法维修"`
	CreatedAt     *time.Time     `gorm:"type:datetime;autoCreateTime"`
	UpdatedAt     *time.Time     `gorm:"type:datetime;autoUpdateTime"`
	DeletedAt     gorm.DeletedAt `gorm:"index"`
}

type TabWorkOrderCommit struct {
	ID          uint       `gorm:"primarykey"`
	WorkOrderID uint       `gorm:"not null;index;comment:关联工单ID"`
	UserID      uint       `gorm:"not null;comment:操作人ID"`
	Action      string     `gorm:"size:50;not null;comment:操作类型: create-创建 create_status-状态变更"`
	Status      string     `gorm:"size:50;comment:变更后的状态"`
	OldStatus   string     `gorm:"size:50;comment:变更前的状态"`
	Comment     string     `gorm:"type:text;comment:备注"`
	IP          string     `gorm:"size:50;comment:操作IP"`
	CreatedAt   *time.Time `gorm:"type:datetime;autoCreateTime"`
}

type TabWorkOrderLog struct {
	ID          uint       `gorm:"primarykey"`
	WorkOrderID uint       `gorm:"not null;index;comment:关联工单ID"`
	UserID      uint       `gorm:"not null;comment:操作人ID"`
	ActionType  string     `gorm:"size:50;not null;comment:操作类型: create update delete query"`
	OldContent  string     `gorm:"type:text;comment:修改前内容(JSON)"`
	NewContent  string     `gorm:"type:text;comment:修改后内容(JSON)"`
	IP          string     `gorm:"size:50;comment:操作IP"`
	Remark      string     `gorm:"size:500;comment:备注"`
	CreatedAt   *time.Time `gorm:"type:datetime;autoCreateTime"`
}

// PurchaseOrderInfo 采购订单简要信息
type PurchaseOrderInfo struct {
	ID     uint   `json:"id"`
	Title  string `json:"title"`
	Status string `json:"status"`
}

// ---------- AI 工单查询 Provider ----------

type workOrderProvider struct{}

func (workOrderProvider) QueryWorkOrders(ctx context.Context, query agents.WorkOrderQuery) (*agents.WorkOrderQueryResult, error) {
	select {
	case <-ctx.Done():
		return nil, ctx.Err()
	default:
	}

	result := &agents.WorkOrderQueryResult{
		Ok:       true,
		Action:   query.Action,
		LoggedIn: query.UserID > 0,
	}
	if !result.LoggedIn {
		result.Message = "需要登录才能查询工单模块信息。"
		return result, nil
	}

	switch query.Action {
	case "count":
		counts, err := queryWorkOrderCounts(query)
		if err != nil {
			return nil, err
		}
		result.Counts = counts
		return result, nil
	case "get":
		order, err := queryWorkOrderDetail(query.OrderID, query.UserID)
		if err != nil {
			return nil, err
		}
		result.Order = order
		if order != nil {
			result.Count = 1
		}
		return result, nil
	default:
		orders, total, err := queryWorkOrderList(query)
		if err != nil {
			return nil, err
		}
		result.Orders = orders
		result.Count = len(orders)
		result.Total = total
		result.Page = query.Page
		result.Limit = query.Limit
		result.Filters = map[string]interface{}{
			"search":     query.Search,
			"status":     query.Status,
			"start_date": query.StartDate,
			"end_date":   query.EndDate,
		}
		return result, nil
	}
}

func applyWorkOrderQueryFilters(db *gorm.DB, query agents.WorkOrderQuery) (*gorm.DB, error) {
	if query.Search != "" {
		var id uint
		if _, err := parsefmt.Sscanf(query.Search, "%d", &id); err == nil && id > 0 {
			db = db.Where("id = ?", id)
		} else {
			db = db.Where("title LIKE ? OR description LIKE ?", "%"+query.Search+"%", "%"+query.Search+"%")
		}
	}
	if query.Status != "" {
		db = db.Where("current_status = ?", query.Status)
	}
	if query.StartDate != "" {
		startDate, err := time.Parse("2006-01-02", query.StartDate)
		if err != nil {
			return nil, err
		}
		db = db.Where("created_at >= ?", startDate)
	}
	if query.EndDate != "" {
		endDate, err := time.Parse("2006-01-02", query.EndDate)
		if err != nil {
			return nil, err
		}
		db = db.Where("created_at < ?", endDate.AddDate(0, 0, 1))
	}
	return db, nil
}

func queryWorkOrderList(query agents.WorkOrderQuery) ([]agents.WorkOrder, int64, error) {
	db, err := applyWorkOrderQueryFilters(models.DB.Model(&TabWorkOrder{}), query)
	if err != nil {
		return nil, 0, err
	}
	var total int64
	if err := db.Count(&total).Error; err != nil {
		return nil, 0, err
	}

	var rows []TabWorkOrder
	if err := db.Order("id DESC").Offset(query.Limit * (query.Page - 1)).Limit(query.Limit).Find(&rows).Error; err != nil {
		return nil, 0, err
	}

	orders := make([]agents.WorkOrder, 0, len(rows))
	for _, row := range rows {
		order := buildWorkOrder(row, query.UserID)
		order.Customers = loadWorkOrderCustomers(row.ID)
		order.Items = loadWorkOrderItems(row.ID)
		orders = append(orders, order)
	}
	return orders, total, nil
}

func queryWorkOrderDetail(orderID uint, userID uint) (*agents.WorkOrder, error) {
	var row TabWorkOrder
	if err := models.DB.Where("id = ?", orderID).First(&row).Error; err != nil {
		if err == gorm.ErrRecordNotFound {
			return nil, nil
		}
		return nil, err
	}
	order := buildWorkOrder(row, userID)
	order.Customers = loadWorkOrderCustomers(orderID)
	order.Items = loadWorkOrderItems(orderID)

	var commits []TabWorkOrderCommit
	if err := models.DB.Where("work_order_id = ?", orderID).Order("created_at DESC").Find(&commits).Error; err != nil {
		return nil, err
	}
	order.Commits = make([]agents.WorkOrderCommit, 0, len(commits))
	for _, commit := range commits {
		order.Commits = append(order.Commits, agents.WorkOrderCommit{
			ID:          commit.ID,
			WorkOrderID: commit.WorkOrderID,
			UserID:      commit.UserID,
			Action:      commit.Action,
			Status:      commit.Status,
			StatusName:  workOrderStatusName(commit.Status),
			OldStatus:   commit.OldStatus,
			Comment:     commit.Comment,
			CreatedAt:   formatTimePtr(commit.CreatedAt),
		})
	}
	return &order, nil
}

func queryWorkOrderCounts(query agents.WorkOrderQuery) (map[string]int64, error) {
	counts := map[string]int64{}
	statuses := []string{"pending", "checked", "parts_ordered", "repaired", "returned", "unrepairable"}
	var total int64
	base, err := applyWorkOrderQueryFilters(models.DB.Model(&TabWorkOrder{}), agents.WorkOrderQuery{Search: query.Search, StartDate: query.StartDate, EndDate: query.EndDate})
	if err != nil {
		return nil, err
	}
	if err := base.Count(&total).Error; err != nil {
		return nil, err
	}
	counts["total"] = total
	for _, status := range statuses {
		statusQuery := agents.WorkOrderQuery{Search: query.Search, Status: status, StartDate: query.StartDate, EndDate: query.EndDate}
		db, err := applyWorkOrderQueryFilters(models.DB.Model(&TabWorkOrder{}), statusQuery)
		if err != nil {
			return nil, err
		}
		var count int64
		if err := db.Count(&count).Error; err != nil {
			return nil, err
		}
		counts[status] = count
	}
	return counts, nil
}

func buildWorkOrder(row TabWorkOrder, currentUserID uint) agents.WorkOrder {
	return agents.WorkOrder{
		ID:                row.ID,
		UserID:            row.UserID,
		Title:             row.Title,
		Description:       row.Description,
		DetailURL:         workOrderDetailURL(row.ID),
		CurrentStatus:     row.CurrentStatus,
		CurrentStatusName: workOrderStatusName(row.CurrentStatus),
		CreatedAt:         formatTimePtr(row.CreatedAt),
		UpdatedAt:         formatTimePtr(row.UpdatedAt),
		CanModify:         canModifyWorkOrder(currentUserID, row.UserID),
	}
}

func loadWorkOrderCustomers(workOrderID uint) []agents.WorkOrderCustomer {
	var customerBinds []TabWorkOrderCustomerBind
	models.DB.Where("work_order_id = ?", workOrderID).Find(&customerBinds)
	if len(customerBinds) == 0 {
		return nil
	}
	customerIDs := make([]uint, 0, len(customerBinds))
	for _, b := range customerBinds {
		customerIDs = append(customerIDs, b.CustomerID)
	}
	var customers []TabCustomer
	if err := models.DB.Where("id IN ?", customerIDs).Find(&customers).Error; err != nil {
		return nil
	}
	result := make([]agents.WorkOrderCustomer, 0, len(customers))
	for _, c := range customers {
		item := agents.WorkOrderCustomer{
			ID:        c.ID,
			FirstName: c.FirstName,
			LastName:  c.LastName,
		}
		var phone TabCustomerPhone
		if err := models.DB.Where("customer_id = ? AND is_primary = ?", c.ID, true).First(&phone).Error; err == nil {
			item.PrimaryPhone = phone.Phone
		} else if err := models.DB.Where("customer_id = ?", c.ID).First(&phone).Error; err == nil {
			item.PrimaryPhone = phone.Phone
		}
		result = append(result, item)
	}
	return result
}

func loadWorkOrderItems(workOrderID uint) []agents.WorkOrderItem {
	var itemBinds []TabWarehouseItemWorkOrderBind
	models.DB.Where("work_order_id = ?", workOrderID).Find(&itemBinds)
	if len(itemBinds) == 0 {
		return nil
	}
	itemIDs := make([]uint, 0, len(itemBinds))
	for _, b := range itemBinds {
		itemIDs = append(itemIDs, b.ItemID)
	}
	var items []TabWarehouseItem
	if err := models.DB.Where("id IN ?", itemIDs).Find(&items).Error; err != nil {
		return nil
	}
	result := make([]agents.WorkOrderItem, 0, len(items))
	for _, it := range items {
		result = append(result, agents.WorkOrderItem{
			ID:           it.ID,
			Name:         it.Name,
			SerialNumber: it.SerialNumber,
		})
	}
	return result
}

func workOrderDetailURL(orderID uint) string {
	return parsefmt.Sprintf("/workorder/showorder/%d", orderID)
}

func workOrderStatusName(status string) string {
	switch status {
	case "pending":
		return "待处理"
	case "checked":
		return "已检查"
	case "parts_ordered":
		return "已下单零件"
	case "repaired":
		return "已维修"
	case "returned":
		return "已送还"
	case "unrepairable":
		return "无法维修"
	default:
		return status
	}
}

// ---------- 初始化 ----------

func ApiWorkOrderInit() {
	models.DB.AutoMigrate(&TabWorkOrder{})

	models.DB.AutoMigrate(&TabWorkOrderCommit{})
	models.DB.AutoMigrate(&TabWorkOrderLog{})

	workOrderUserGroup.Name = "work_order_admin"
	if models.DB.Where(&workOrderUserGroup).First(&workOrderUserGroup).Error == nil {
		WorkOrderUpdateAdminsCash()
	} else {
		workOrderUserGroup.Type = "usergroup"
		models.DB.Create(&workOrderUserGroup)
	}

	agents.RegisterWorkOrderProvider(workOrderProvider{})
}

// ---------- 路由注册 ----------

func ApiWorkOrder(r *gin.RouterGroup) {

	// 新增工单
	r.POST("/add", func(ctx *gin.Context) {
		isAuth, user, data := AuthenticationAuthority(ctx)
		if !isAuth {
			ReturnJson(ctx, "userCookieError", nil)
			return
		}

		type FromAdd struct {
			Title       string   `json:"title"`
			Description string   `json:"description"`
			Photos      []string `json:"photos"`
			ItemIDs     []uint   `json:"item_ids"`
			CustomerIDs []uint   `json:"customer_ids"`
		}
		var from FromAdd
		if err := decodeJSON(data, &from); err != nil || from.Title == "" {
			ReturnJson(ctx, "jsonErr", nil)
			return
		}

		// 校验图片哈希
		for _, hash := range from.Photos {
			if models.IsContainsSpecialChar(hash) {
				ReturnJson(ctx, "photo_hash_invalid", nil)
				return
			}
		}

		order := TabWorkOrder{
			UserID:        user.ID,
			Title:         from.Title,
			Description:   from.Description,
			CurrentStatus: "pending",
		}
		models.DB.Create(&order)

		// 绑定图片
		for _, hash := range from.Photos {
			findFile := TabFileInfo{Sha256: hash, Type: "image"}
			if models.DB.Where(&findFile).First(&findFile).Error == nil {
				models.DB.Create(&TabWorkOrderFileBind{
					WorkOrderID: order.ID,
					FileID:      findFile.ID,
				})
			}
		}

		// 绑定物品（支持多个）
		for _, itemID := range from.ItemIDs {
			if itemID > 0 {
				models.DB.Create(&TabWarehouseItemWorkOrderBind{
					ItemID:      itemID,
					WorkOrderID: order.ID,
					CreatorID:   user.ID,
				})
			}
		}

		// 绑定客户（支持多个）
		for _, customerID := range from.CustomerIDs {
			if customerID > 0 {
				models.DB.Create(&TabWorkOrderCustomerBind{
					WorkOrderID: order.ID,
					CustomerID:  customerID,
					CreatorID:   user.ID,
				})
			}
		}

		// 写创建 commit
		models.DB.Create(&TabWorkOrderCommit{
			WorkOrderID: order.ID,
			UserID:      user.ID,
			Action:      "create",
			Status:      "pending",
			OldStatus:   "",
			Comment:     "工单创建",
			IP:          ctx.ClientIP(),
		})

		// 写操作日志
		newContent, _ := json.Marshal(from)
		models.DB.Create(&TabWorkOrderLog{
			WorkOrderID: order.ID,
			UserID:      user.ID,
			ActionType:  "create",
			NewContent:  string(newContent),
			IP:          ctx.ClientIP(),
		})

		ReturnJson(ctx, "apiOK", gin.H{"id": order.ID})
	})

	// 编辑工单
	r.POST("/update", func(ctx *gin.Context) {
		isAuth, user, data := AuthenticationAuthority(ctx)
		if !isAuth {
			ReturnJson(ctx, "userCookieError", nil)
			return
		}

	type FromUpdate struct {
		ID           uint     `json:"id"`
		Title        string   `json:"title"`
		Description  string   `json:"description"`
		Photos       []string `json:"photos"`
		ItemIDs      []uint   `json:"item_ids"`
		CustomerIDs  []uint   `json:"customer_ids"`
	}
	var from FromUpdate
	if err := decodeJSON(data, &from); err != nil || from.ID == 0 || from.Title == "" {
		ReturnJson(ctx, "jsonErr", nil)
		return
	}

		// 校验图片哈希
		for _, hash := range from.Photos {
			if models.IsContainsSpecialChar(hash) {
				ReturnJson(ctx, "photo_hash_invalid", nil)
				return
			}
		}

		var order TabWorkOrder
		if err := models.DB.Where("id = ?", from.ID).First(&order).Error; err != nil {
			ReturnJson(ctx, "order_not_found", nil)
			return
		}

		if !canModifyWorkOrder(user.ID, order.UserID) {
			ReturnJson(ctx, "no_permission", nil)
			return
		}

		oldContent, _ := json.Marshal(order)

		models.DB.Model(&order).Updates(map[string]interface{}{
			"title":       from.Title,
			"description": from.Description,
		})

		// 重建图片绑定
		models.DB.Where("work_order_id = ?", from.ID).Delete(&TabWorkOrderFileBind{})
		for _, hash := range from.Photos {
			findFile := TabFileInfo{Sha256: hash, Type: "image"}
			if models.DB.Where(&findFile).First(&findFile).Error == nil {
				models.DB.Create(&TabWorkOrderFileBind{
					WorkOrderID: from.ID,
					FileID:      findFile.ID,
				})
			}
		}

		// 重建物品关联绑定
		models.DB.Where("work_order_id = ?", from.ID).Delete(&TabWarehouseItemWorkOrderBind{})
		for _, itemID := range from.ItemIDs {
			models.DB.Create(&TabWarehouseItemWorkOrderBind{
				WorkOrderID: from.ID,
				ItemID:      itemID,
			})
		}

		// 重建客户关联绑定
		models.DB.Where("work_order_id = ?", from.ID).Delete(&TabWorkOrderCustomerBind{})
		for _, customerID := range from.CustomerIDs {
			models.DB.Create(&TabWorkOrderCustomerBind{
				WorkOrderID: from.ID,
				CustomerID:  customerID,
				CreatorID:   user.ID,
			})
		}

		newContent, _ := json.Marshal(from)
		models.DB.Create(&TabWorkOrderLog{
			WorkOrderID: from.ID,
			UserID:      user.ID,
			ActionType:  "update",
			OldContent:  string(oldContent),
			NewContent:  string(newContent),
			IP:          ctx.ClientIP(),
		})

		ReturnJson(ctx, "apiOK", nil)
	})

	// 获取工单列表
	r.POST("/list", func(ctx *gin.Context) {
		isAuth, _, data := AuthenticationAuthority(ctx)
		if !isAuth {
			ReturnJson(ctx, "userCookieError", nil)
			return
		}

		type FromList struct {
			Search  string `json:"search"`
			Status  string `json:"status"`
			Entries int    `json:"entries"`
			Page    int    `json:"page"`
		}
		var from FromList
		if err := decodeJSON(data, &from); err != nil {
			ReturnJson(ctx, "jsonErr", nil)
			return
		}
		if from.Entries <= 0 || from.Entries > 300 {
			from.Entries = 10
		}
		if from.Page <= 0 {
			from.Page = 1
		}

		var count int64
		query := models.DB.Model(&TabWorkOrder{})
		if from.Search != "" {
			query = query.Where("title LIKE ?", "%"+from.Search+"%")
		}
		if from.Status != "" {
			query = query.Where("current_status = ?", from.Status)
		}
		query.Count(&count)

		var orders []TabWorkOrder
		query.Order("id DESC").
			Offset(from.Entries * (from.Page - 1)).
			Limit(from.Entries).
			Find(&orders)

		// 定义返回结构
		type CustomerInfo struct {
			ID          uint   `json:"id"`
			FirstName   string `json:"first_name"`
			LastName    string `json:"last_name"`
			PrimaryPhone string `json:"primary_phone"`
		}
		type OrderWithCustomers struct {
			TabWorkOrder
			Customers []CustomerInfo `json:"customers"`
		}

		var ordersWithCustomers []OrderWithCustomers
		for _, order := range orders {
			orderItem := OrderWithCustomers{
				TabWorkOrder: order,
				Customers:   []CustomerInfo{},
			}

			// 查询关联客户
			var customerBinds []TabWorkOrderCustomerBind
			models.DB.Where("work_order_id = ?", order.ID).Find(&customerBinds)
			if len(customerBinds) > 0 {
				var customerIDs []uint
				for _, b := range customerBinds {
					customerIDs = append(customerIDs, b.CustomerID)
				}
				var customers []TabCustomer
				models.DB.Where("id IN ?", customerIDs).Find(&customers)
				for _, c := range customers {
					customerInfo := CustomerInfo{
						ID:        c.ID,
						FirstName:  c.FirstName,
						LastName:   c.LastName,
						PrimaryPhone: "",
					}
					// 获取主电话
					var phone TabCustomerPhone
					if err := models.DB.Where("customer_id = ? AND is_primary = ?", c.ID, true).First(&phone).Error; err == nil {
						customerInfo.PrimaryPhone = phone.Phone
					} else if err := models.DB.Where("customer_id = ?", c.ID).First(&phone).Error; err == nil {
						customerInfo.PrimaryPhone = phone.Phone
					}
					orderItem.Customers = append(orderItem.Customers, customerInfo)
				}
			}
			ordersWithCustomers = append(ordersWithCustomers, orderItem)
		}

		ReturnJson(ctx, "apiOK", gin.H{
			"all_count":  count,
			"all_orders": ordersWithCustomers,
		})
	})

	// 获取工单详情
	r.POST("/get", func(ctx *gin.Context) {
		isAuth, user, data := AuthenticationAuthority(ctx)
		if !isAuth {
			ReturnJson(ctx, "userCookieError", nil)
			return
		}

		type FromGet struct {
			ID uint `json:"id"`
		}
		var from FromGet
		if err := decodeJSON(data, &from); err != nil || from.ID == 0 {
			ReturnJson(ctx, "jsonErr", nil)
			return
		}

		var order TabWorkOrder
		if err := models.DB.Where("id = ?", from.ID).First(&order).Error; err != nil {
			ReturnJson(ctx, "order_not_found", nil)
			return
		}

		// 关联图片
		var binds []TabWorkOrderFileBind
		models.DB.Where("work_order_id = ?", from.ID).Find(&binds)
		var fileIDs []uint
		for _, b := range binds {
			fileIDs = append(fileIDs, b.FileID)
		}
		var files []TabFileInfo
		if len(fileIDs) > 0 {
			models.DB.Where("id IN ?", fileIDs).Find(&files)
		}

		// commits
		var commits []TabWorkOrderCommit
		models.DB.Where("work_order_id = ?", from.ID).Order("created_at DESC").Find(&commits)

		// 为每条 commit 附加图片和采购订单
		type CommitWithPhotos struct {
			TabWorkOrderCommit
			Photos         []TabFileInfo       `json:"photos"`
			PurchaseOrders []PurchaseOrderInfo `json:"purchaseOrders"`
		}
		var commitsWithPhotos []CommitWithPhotos
		for _, c := range commits {
			item := CommitWithPhotos{TabWorkOrderCommit: c, Photos: []TabFileInfo{}, PurchaseOrders: []PurchaseOrderInfo{}}

			// 附加图片
			var fileBinds []TabWorkOrderCommitFileBind
			models.DB.Where("commit_id = ?", c.ID).Find(&fileBinds)
			if len(fileBinds) > 0 {
				var fileIDs []uint
				for _, fb := range fileBinds {
					fileIDs = append(fileIDs, fb.FileID)
				}
				models.DB.Where("id IN ?", fileIDs).Find(&item.Photos)
			}

			// 附加采购订单
			var poBinds []TabWorkOrderPurchaseOrderBind
			models.DB.Where("commit_id = ?", c.ID).Find(&poBinds)
			if len(poBinds) > 0 {
				var poIDs []uint
				for _, pb := range poBinds {
					poIDs = append(poIDs, pb.PurchaseOrderID)
				}
				var pos []TabPurchaseOrder
				models.DB.Where("id IN ?", poIDs).Find(&pos)
				for _, po := range pos {
					item.PurchaseOrders = append(item.PurchaseOrders, PurchaseOrderInfo{
						ID:     po.ID,
						Title:  po.Title,
						Status: po.OrderStatus,
					})
				}
			}

			commitsWithPhotos = append(commitsWithPhotos, item)
		}

		canModify := canModifyWorkOrder(user.ID, order.UserID)
		// 所有登录用户都可以提交进度
		canCommit := true

		// 关联物品
		type LinkedItem struct {
			ID           uint   `json:"ID"`
			Name         string `json:"Name"`
			SerialNumber string `json:"SerialNumber"`
			ContainerID  *uint  `json:"ContainerID"`
		}
		var linkedItems []LinkedItem
		var itemBinds []TabWarehouseItemWorkOrderBind
		models.DB.Where("work_order_id = ?", from.ID).Find(&itemBinds)
		if len(itemBinds) > 0 {
			var itemIDs []uint
			for _, b := range itemBinds {
				itemIDs = append(itemIDs, b.ItemID)
			}
			var items []TabWarehouseItem
			models.DB.Where("id IN ?", itemIDs).Find(&items)
			for _, it := range items {
				linkedItems = append(linkedItems, LinkedItem{
					ID:           it.ID,
					Name:         it.Name,
					SerialNumber: it.SerialNumber,
					ContainerID:  it.ContainerID,
				})
			}
		}

		// 关联客户
		type LinkedCustomer struct {
			ID         uint   `json:"id"`
			FirstName  string `json:"first_name"`
			LastName   string `json:"last_name"`
			PrimaryPhone string `json:"primary_phone"`
		}
		var linkedCustomers []LinkedCustomer
		var customerBinds []TabWorkOrderCustomerBind
		models.DB.Where("work_order_id = ?", from.ID).Find(&customerBinds)
		if len(customerBinds) > 0 {
			var customerIDs []uint
			for _, b := range customerBinds {
				customerIDs = append(customerIDs, b.CustomerID)
			}
			var customers []TabCustomer
			models.DB.Where("id IN ?", customerIDs).Find(&customers)
			for _, c := range customers {
				item := LinkedCustomer{
					ID:        c.ID,
					FirstName:  c.FirstName,
					LastName:   c.LastName,
					PrimaryPhone: "",
				}
				// 获取主电话
				var phone TabCustomerPhone
				if err := models.DB.Where("customer_id = ? AND is_primary = ?", c.ID, true).First(&phone).Error; err == nil {
					item.PrimaryPhone = phone.Phone
				} else if err := models.DB.Where("customer_id = ?", c.ID).First(&phone).Error; err == nil {
					item.PrimaryPhone = phone.Phone
				}
				linkedCustomers = append(linkedCustomers, item)
			}
		}

		ReturnJson(ctx, "apiOK", gin.H{
			"order":           order,
			"canModify":       canModify,
			"canCommit":       canCommit,
			"photos":          files,
			"commits":         commitsWithPhotos,
			"linkedItems":     linkedItems,
			"linkedCustomers": linkedCustomers,
		})
	})

	// 关联客户到工单
	r.POST("/link_customer", func(ctx *gin.Context) {
		isAuth, user, data := AuthenticationAuthority(ctx)
		if !isAuth {
			ReturnJson(ctx, "userCookieError", nil)
			return
		}

		type FromLinkCustomer struct {
			WorkOrderID uint `json:"work_order_id"`
			CustomerID  uint `json:"customer_id"`
		}
		var from FromLinkCustomer
		if err := decodeJSON(data, &from); err != nil || from.WorkOrderID == 0 || from.CustomerID == 0 {
			ReturnJson(ctx, "parameErr", nil)
			return
		}

		// 检查工单是否存在
		var order TabWorkOrder
		if err := models.DB.Where("id = ?", from.WorkOrderID).First(&order).Error; err != nil {
			ReturnJson(ctx, "order_not_found", nil)
			return
		}

		// 检查客户是否存在
		var customer TabCustomer
		if err := models.DB.Where("id = ?", from.CustomerID).First(&customer).Error; err != nil {
			ReturnJson(ctx, "customer_not_found", nil)
			return
		}

		// 检查是否已关联
		var existingBind TabWorkOrderCustomerBind
		if err := models.DB.Where("work_order_id = ? AND customer_id = ?", from.WorkOrderID, from.CustomerID).First(&existingBind).Error; err == nil {
			ReturnJson(ctx, "already_linked", nil)
			return
		}

		// 创建关联
		if err := models.DB.Create(&TabWorkOrderCustomerBind{
			WorkOrderID: from.WorkOrderID,
			CustomerID:  from.CustomerID,
			CreatorID:   user.ID,
		}).Error; err != nil {
			ReturnJson(ctx, "dbErr", nil)
			return
		}

		// 写操作日志
		models.DB.Create(&TabWorkOrderLog{
			WorkOrderID: from.WorkOrderID,
			UserID:      user.ID,
			ActionType:  "link_customer",
			NewContent:  parsefmt.Sprintf("关联客户 ID: %d", from.CustomerID),
			IP:          ctx.ClientIP(),
		})

		ReturnJson(ctx, "apiOK", nil)
	})

	// 解除工单与客户关联
	r.POST("/unlink_customer", func(ctx *gin.Context) {
		isAuth, user, data := AuthenticationAuthority(ctx)
		if !isAuth {
			ReturnJson(ctx, "userCookieError", nil)
			return
		}

		type FromUnlinkCustomer struct {
			WorkOrderID uint `json:"work_order_id"`
			CustomerID  uint `json:"customer_id"`
		}
		var from FromUnlinkCustomer
		if err := decodeJSON(data, &from); err != nil || from.WorkOrderID == 0 || from.CustomerID == 0 {
			ReturnJson(ctx, "parameErr", nil)
			return
		}

		// 检查工单是否存在
		var order TabWorkOrder
		if err := models.DB.Where("id = ?", from.WorkOrderID).First(&order).Error; err != nil {
			ReturnJson(ctx, "order_not_found", nil)
			return
		}

		// 删除关联
		if err := models.DB.Where("work_order_id = ? AND customer_id = ?", from.WorkOrderID, from.CustomerID).Delete(&TabWorkOrderCustomerBind{}).Error; err != nil {
			ReturnJson(ctx, "dbErr", nil)
			return
		}

		// 写操作日志
		models.DB.Create(&TabWorkOrderLog{
			WorkOrderID: from.WorkOrderID,
			UserID:      user.ID,
			ActionType:  "unlink_customer",
			NewContent:  parsefmt.Sprintf("解除关联客户 ID: %d", from.CustomerID),
			IP:          ctx.ClientIP(),
		})

		ReturnJson(ctx, "apiOK", nil)
	})

	// 新增进度 commit（状态推进）
	r.POST("/commit", func(ctx *gin.Context) {
		isAuth, user, data := AuthenticationAuthority(ctx)
		if !isAuth {
			ReturnJson(ctx, "userCookieError", nil)
			return
		}

		type FromCommit struct {
			ID               uint     `json:"id"`
			Status           string   `json:"status"`
			Comment          string   `json:"comment"`
			Photos           []string `json:"photos"`
			PurchaseOrderIDs []uint   `json:"purchaseOrderIds"` // 关联的采购订单ID列表
		}
		var from FromCommit
		if err := decodeJSON(data, &from); err != nil || from.ID == 0 {
			ReturnJson(ctx, "jsonErr", nil)
			return
		}

		validStatuses := map[string]bool{
			"pending":       true,
			"checked":       true,
			"parts_ordered": true,
			"repaired":      true,
			"returned":      true,
			"unrepairable":  true,
		}
		if !validStatuses[from.Status] {
			ReturnJson(ctx, "invalid_status", nil)
			return
		}

		// 校验图片哈希
		for _, hash := range from.Photos {
			if models.IsContainsSpecialChar(hash) {
				ReturnJson(ctx, "photo_hash_invalid", nil)
				return
			}
		}

		var order TabWorkOrder
		if err := models.DB.Where("id = ?", from.ID).First(&order).Error; err != nil {
			ReturnJson(ctx, "order_not_found", nil)
			return
		}

		oldStatus := order.CurrentStatus
		models.DB.Model(&order).Update("current_status", from.Status)

		// 如果状态变更为"已送还"，移除关联物品的容器
		if from.Status == "returned" {
			var itemBinds []TabWarehouseItemWorkOrderBind
			models.DB.Where("work_order_id = ?", from.ID).Find(&itemBinds)
			for _, bind := range itemBinds {
				var item TabWarehouseItem
				if models.DB.Where("id = ?", bind.ItemID).First(&item).Error == nil {
					oldContainer := item.ContainerID
					// 移除容器
					item.ContainerID = nil
					models.DB.Save(&item)
					// 记录移动 commit
					models.DB.Create(&TabWarehouseItemCommit{
						ItemID:       item.ID,
						UserID:       user.ID,
						OldContainer: oldContainer,
						NewContainer: nil,
						Remark:       "工单送还: " + from.Comment,
						IP:           ctx.ClientIP(),
					})
					// 旧容器 ItemCount -1
					if oldContainer != nil {
						models.DB.Model(&TabWarehouseContainer{}).Where("id = ?", *oldContainer).Update("item_count", models.DB.Raw("item_count - 1"))
					}
				}
			}
		}

		comment := from.Comment
		if comment == "" {
			comment = "状态变更为: " + from.Status
		}

		commit := TabWorkOrderCommit{
			WorkOrderID: order.ID,
			UserID:      user.ID,
			Action:      "create_status",
			Status:      from.Status,
			OldStatus:   oldStatus,
			Comment:     comment,
			IP:          ctx.ClientIP(),
		}
		models.DB.Create(&commit)

		// 绑定进度图片
		for _, hash := range from.Photos {
			findFile := TabFileInfo{Sha256: hash, Type: "image"}
			if models.DB.Where(&findFile).First(&findFile).Error == nil {
				models.DB.Create(&TabWorkOrderCommitFileBind{
					CommitID:    commit.ID,
					WorkOrderID: order.ID,
					FileID:      findFile.ID,
				})
			}
		}

		// 绑定采购订单（去重）
		if len(from.PurchaseOrderIDs) > 0 {
			seen := make(map[uint]bool)
			for _, pid := range from.PurchaseOrderIDs {
				if !seen[pid] {
					seen[pid] = true
					models.DB.Create(&TabWorkOrderPurchaseOrderBind{
						WorkOrderID:     order.ID,
						CommitID:        commit.ID,
						PurchaseOrderID: pid,
					})
				}
			}
		}

		newContent, _ := json.Marshal(map[string]string{
			"status":  from.Status,
			"comment": comment,
		})
		oldContent, _ := json.Marshal(map[string]string{
			"status": oldStatus,
		})
		models.DB.Create(&TabWorkOrderLog{
			WorkOrderID: from.ID,
			UserID:      user.ID,
			ActionType:  "update_status",
			OldContent:  string(oldContent),
			NewContent:  string(newContent),
			IP:          ctx.ClientIP(),
			Remark:      comment,
		})

		ReturnJson(ctx, "apiOK", nil)
	})

	// 删除工单
	r.POST("/delete", func(ctx *gin.Context) {
		isAuth, user, data := AuthenticationAuthority(ctx)
		if !isAuth {
			ReturnJson(ctx, "userCookieError", nil)
			return
		}

		type FromDelete struct {
			ID uint `json:"id"`
		}
		var from FromDelete
		if err := decodeJSON(data, &from); err != nil || from.ID == 0 {
			ReturnJson(ctx, "jsonErr", nil)
			return
		}

		var order TabWorkOrder
		if err := models.DB.Where("id = ?", from.ID).First(&order).Error; err != nil {
			ReturnJson(ctx, "order_not_found", nil)
			return
		}

		if !canModifyWorkOrder(user.ID, order.UserID) {
			ReturnJson(ctx, "no_permission", nil)
			return
		}

		models.DB.Where("work_order_id = ?", from.ID).Delete(&TabWorkOrderFileBind{})
		models.DB.Where("work_order_id = ?", from.ID).Delete(&TabWorkOrderCommitFileBind{})
		models.DB.Where("work_order_id = ?", from.ID).Delete(&TabWorkOrderPurchaseOrderBind{})
		models.DB.Where("work_order_id = ?", from.ID).Delete(&TabWorkOrderCommit{})
		models.DB.Where("work_order_id = ?", from.ID).Delete(&TabWorkOrderLog{})
		models.DB.Delete(&order)

		ReturnJson(ctx, "apiOK", nil)
	})

	// 删除进度
	r.POST("/delete_commit", func(ctx *gin.Context) {
		isAuth, user, data := AuthenticationAuthority(ctx)
		if !isAuth {
			ReturnJson(ctx, "userCookieError", nil)
			return
		}

		type FromDeleteCommit struct {
			WorkOrderID uint `json:"workOrderId"`
			CommitID    uint `json:"commitId"`
		}
		var from FromDeleteCommit
		if err := decodeJSON(data, &from); err != nil || from.WorkOrderID == 0 || from.CommitID == 0 {
			ReturnJson(ctx, "jsonErr", nil)
			return
		}

		// 获取工单信息
		var order TabWorkOrder
		if err := models.DB.Where("id = ?", from.WorkOrderID).First(&order).Error; err != nil {
			ReturnJson(ctx, "order_not_found", nil)
			return
		}

		// 获取进度信息
		var commit TabWorkOrderCommit
		if err := models.DB.Where("id = ? AND work_order_id = ?", from.CommitID, from.WorkOrderID).First(&commit).Error; err != nil {
			ReturnJson(ctx, "commit_not_found", nil)
			return
		}

		// 权限判断：工单创建者 或 进度创建者 或 管理员
		isOrderCreator := user.ID == order.UserID
		isCommitCreator := user.ID == commit.UserID
		isAdmin := slices.Contains(workOrderAdmins, user.ID)

		if !isOrderCreator && !isCommitCreator && !isAdmin {
			ReturnJson(ctx, "no_permission", nil)
			return
		}

		// 删除关联的采购订单绑定
		models.DB.Where("commit_id = ?", from.CommitID).Delete(&TabWorkOrderPurchaseOrderBind{})
		// 删除关联的图片
		models.DB.Where("commit_id = ?", from.CommitID).Delete(&TabWorkOrderCommitFileBind{})
		// 删除进度记录
		models.DB.Where("id = ?", from.CommitID).Delete(&commit)

		ReturnJson(ctx, "apiOK", nil)
	})

	// 获取工单数量统计
	r.POST("/count", func(ctx *gin.Context) {
		isAuth, _, _ := AuthenticationAuthority(ctx)
		if !isAuth {
			ReturnJson(ctx, "userCookieError", nil)
			return
		}

		type WOCount struct {
			Pending      int64 `json:"pending"`
			Checked      int64 `json:"checked"`
			PartsOrdered int64 `json:"parts_ordered"`
			Repaired     int64 `json:"repaired"`
			Returned     int64 `json:"returned"`
			Unrepairable int64 `json:"unrepairable"`
			Total        int64 `json:"total"`
		}
		var count WOCount
		models.DB.Model(&TabWorkOrder{}).Count(&count.Total)
		models.DB.Model(&TabWorkOrder{}).Where("current_status = ?", "pending").Count(&count.Pending)
		models.DB.Model(&TabWorkOrder{}).Where("current_status = ?", "checked").Count(&count.Checked)
		models.DB.Model(&TabWorkOrder{}).Where("current_status = ?", "parts_ordered").Count(&count.PartsOrdered)
		models.DB.Model(&TabWorkOrder{}).Where("current_status = ?", "repaired").Count(&count.Repaired)
		models.DB.Model(&TabWorkOrder{}).Where("current_status = ?", "returned").Count(&count.Returned)
		models.DB.Model(&TabWorkOrder{}).Where("current_status = ?", "unrepairable").Count(&count.Unrepairable)

		ReturnJson(ctx, "apiOK", gin.H{
			"pending":       count.Pending,
			"checked":       count.Checked,
			"parts_ordered": count.PartsOrdered,
			"repaired":      count.Repaired,
			"returned":      count.Returned,
			"unrepairable":  count.Unrepairable,
			"total":         count.Total,
		})
	})

	// 搜索采购订单（用于工单关联）
	r.POST("/search_purchase_orders", func(ctx *gin.Context) {
		isAuth, _, data := AuthenticationAuthority(ctx)
		if !isAuth {
			ReturnJson(ctx, "userCookieError", nil)
			return
		}

		type FromSearch struct {
			Search string `json:"search"`
			Limit  int    `json:"limit"`
		}
		var from FromSearch
		if err := decodeJSON(data, &from); err != nil {
			ReturnJson(ctx, "jsonErr", nil)
			return
		}

		if from.Limit <= 0 || from.Limit > 20 {
			from.Limit = 5
		}

		query := models.DB.Model(&TabPurchaseOrder{})

		// 如果搜索词为空，返回最新的 N 条
		if from.Search != "" {
			// 尝试精确匹配 ID
			var id uint
			if _, err := parsefmt.Sscanf(from.Search, "%d", &id); err == nil && id > 0 {
				query = query.Where("id = ?", id)
			} else {
				// 模糊匹配标题或备注
				query = query.Where("title LIKE ? OR remark LIKE ?",
					"%"+from.Search+"%", "%"+from.Search+"%")
			}
		}

		var orders []TabPurchaseOrder
		query.Order("id DESC").Limit(from.Limit).Find(&orders)

		type OrderInfo struct {
			ID     uint   `json:"id"`
			Title  string `json:"title"`
			Status string `json:"status"`
		}
		var result []OrderInfo
		for _, o := range orders {
			result = append(result, OrderInfo{
				ID:     o.ID,
				Title:  o.Title,
				Status: o.OrderStatus,
			})
		}

		ReturnJson(ctx, "apiOK", gin.H{"orders": result})
	})
}
