package appointment

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

	"com.sclktx/m/v2/internal/common/model"
	"com.sclktx/m/v2/internal/common/response"
	"com.sclktx/m/v2/internal/common/utils"
	"com.sclktx/m/v2/internal/modules/workflow"
	"com.sclktx/m/v2/internal/pkg/dingtalk"
	"com.sclktx/m/v2/internal/pkg/wechat"

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

// AppointmentHandler 预约处理器
// 处理访客预约的创建、查询、审核、取消，以及员工搜索和邀请管理
type AppointmentHandler struct {
	db       *gorm.DB
	wxClient *wechat.MiniProgramClient
	dingTalk *dingtalk.Client
}

func NewAppointmentHandler(db *gorm.DB, wxClient *wechat.MiniProgramClient, dingTalk *dingtalk.Client) *AppointmentHandler {
	return &AppointmentHandler{db: db, wxClient: wxClient, dingTalk: dingTalk}
}

// ============ 员工搜索 ============

// SearchEmployees 搜索公司接待人员
// @Summary      搜索公司接待人员
// @Description  根据关键字搜索员工信息
// @Tags         员工管理
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        body  body      object{keyword=string}  true  "搜索关键字"
// @Success      200   {object}  response.Response  "员工列表"
// @Router       /appointment/employees/search [post]
func (h *AppointmentHandler) SearchEmployees(c *gin.Context) {
	var req struct {
		Keyword string `json:"keyword"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "参数错误")
		return
	}

	var employees []model.User
	query := h.db.Where("users.status = 1")
	if req.Keyword != "" {
		query = query.Joins("LEFT JOIN user_departments ON user_departments.user_id = users.id").
			Where("users.real_name LIKE ? OR users.phone LIKE ? OR user_departments.department_name LIKE ?",
				"%"+req.Keyword+"%", "%"+req.Keyword+"%", "%"+req.Keyword+"%")
	}

	if err := query.Select("users.*").Limit(20).Find(&employees).Error; err != nil {
		response.InternalError(c, "查询失败")
		return
	}

	// 批量查询部门信息
	var userIDs []uint
	for _, e := range employees {
		userIDs = append(userIDs, e.ID)
	}
	deptMap := map[uint]model.UserDepartment{}
	if len(userIDs) > 0 {
		var depts []model.UserDepartment
		h.db.Where("user_id IN ?", userIDs).Find(&depts)
		for _, d := range depts {
			deptMap[d.UserID] = d
		}
	}

	// EmployeeVO 员工视图对象
	type EmployeeVO struct {
		ID           uint   `json:"id"`            // 员工ID（Employee表主键）
		UserID       uint   `json:"user_id"`       // 关联的用户ID（User表主键）
		Name         string `json:"name"`          // 员工姓名
		Department   string `json:"department"`    // 所属部门名称
		DepartmentID *uint  `json:"department_id"` // 所属部门ID
		Position     string `json:"position"`      // 职位标签
		Phone        string `json:"phone"`         // 手机号
	}

	var result []EmployeeVO
	for _, e := range employees {
		ud, hasDept := deptMap[e.ID]
		var deptName string
		var deptID *uint
		if hasDept {
			deptName = ud.DepartmentName
			deptID = &ud.DepartmentID
		}
		result = append(result, EmployeeVO{
			ID:           e.ID,
			UserID:       e.ID,
			Name:         e.RealName,
			Department:   deptName,
			DepartmentID: deptID,
			Position:     ud.Position,
			Phone:        e.Phone,
		})
	}

	response.Success(c, result)
}

// ============ 预约管理 ============

// CreateAppointmentRequest 创建预约请求参数
type CreateAppointmentRequest struct {
	EmployeeName   string        `json:"employee_name"`                       // 公司接待人员姓名
	EmployeePhone  string        `json:"employee_phone"`                      // 公司接待人员手机号
	VisitStartTime string        `json:"visit_start_time" binding:"required"` // 预计来访开始时间（必填）
	VisitEndTime   string        `json:"visit_end_time" binding:"required"`   // 预计来访结束时间（必填）
	VisitAreas     []string      `json:"visit_areas"`                         // 可访问区域列表
	Goods          []GoodsItem   `json:"goods"`                               // 携带物品列表
	Visitors       []VisitorItem `json:"visitors"`                            // 所有外部来访人员（第一人为主要访客）
	Remark         string        `json:"remark"`                              // 备注
	VisitPurpose   string        `json:"visit_purpose"`                       // 来访目的
	HasVehicle     bool          `json:"has_vehicle"`                         // 是否有车辆
	LicensePlates  []string      `json:"license_plates"`                      // 车牌号列表
}

// GoodsItem 携带物品项
type GoodsItem struct {
	Name     string `json:"name"`     // 物品名称
	Quantity int    `json:"quantity"` // 物品数量
	Model    string `json:"model"`    // 物品型号
	Remark   string `json:"remark"`   // 物品备注
}

// VisitorItem 同行访客
type VisitorItem struct {
	Name     string `json:"name"`      // 访客姓名
	Phone    string `json:"phone"`     // 访客电话
	Company  string `json:"company"`   // 公司名称
	Gender   int    `json:"gender"`    // 性别（0未知 1男 2女）
	IDType   string `json:"id_type"`   // 证件类型
	IDNumber string `json:"id_number"` // 证件号码
}

// CreateAppointment 创建预约
// @Summary      创建预约
// @Description  创建访客预约，包含访客信息、物品、同行人等
// @Tags         预约管理
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        body  body      CreateAppointmentRequest  true  "预约信息"
// @Success      200   {object}  response.Response{data=model.Appointment}  "创建成功"
// @Router       /appointment/appointments [post]
func (h *AppointmentHandler) CreateAppointment(c *gin.Context) {
	var req CreateAppointmentRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "参数错误: "+err.Error())
		return
	}

	// 验证外部来访人员
	if len(req.Visitors) == 0 {
		response.BadRequest(c, "请至少填写一位外部来访人员")
		return
	}
	if !utils.ValidatePhone(req.Visitors[0].Phone) {
		response.BadRequest(c, "手机号格式不正确")
		return
	}

	// 解析时间（优先带时区格式，兼容无时区）
	startTime, err := time.Parse(time.RFC3339, req.VisitStartTime)
	if err != nil {
		startTime, err = time.Parse("2006-01-02 15:04:05-07:00", req.VisitStartTime)
		if err != nil {
			startTime, err = time.Parse("2006-01-02 15:04:05", req.VisitStartTime)
			if err != nil {
				response.BadRequest(c, "开始时间格式错误")
				return
			}
		}
	}
	endTime, err := time.Parse(time.RFC3339, req.VisitEndTime)
	if err != nil {
		endTime, err = time.Parse("2006-01-02 15:04:05-07:00", req.VisitEndTime)
		if err != nil {
			endTime, err = time.Parse("2006-01-02 15:04:05", req.VisitEndTime)
			if err != nil {
				response.BadRequest(c, "结束时间格式错误")
				return
			}
		}
	}
	if endTime.Before(startTime) {
		response.BadRequest(c, "结束时间不能早于开始时间")
		return
	}

	// 获取当前创建者
	creatorUserID := utils.GetUserID(c)
	var creatorUser model.User
	if err := h.db.First(&creatorUser, creatorUserID).Error; err != nil {
		response.BadRequest(c, "创建者不存在")
		return
	}

	// 创建或查找主访客（按手机号去重）
	mainVisitor := req.Visitors[0]
	var visitor model.Visitor
	result := h.db.Where("phone = ?", mainVisitor.Phone).First(&visitor)
	if result.Error != nil {
		visitor = model.Visitor{
			UserID:        &creatorUserID,
			Company:       mainVisitor.Company,
			Name:          mainVisitor.Name,
			Phone:         mainVisitor.Phone,
			Gender:        mainVisitor.Gender,
			PhoneVerified: false,
		}
		if err := h.db.Create(&visitor).Error; err != nil {
			response.InternalError(c, "创建访客信息失败")
			return
		}
	} else {
		updates := map[string]interface{}{
			"name":    mainVisitor.Name,
			"company": mainVisitor.Company,
			"gender":  mainVisitor.Gender,
		}
		if req.HasVehicle && len(req.LicensePlates) > 0 {
			updates["license_plate"] = req.LicensePlates[0]
		}
		if visitor.UserID == nil {
			updates["user_id"] = &creatorUserID
		}
		h.db.Model(&visitor).Updates(updates)
	}

	// === 构建预约数据 ===
	appointment := model.Appointment{
		CreatorUserID:  creatorUserID,
		VisitStartTime: startTime,
		VisitEndTime:   endTime,
		Status:         0, // 审核中
		Remark:         req.Remark,
		VisitPurpose:   req.VisitPurpose,
		QrCodeURL:      "", // TODO: 生成二维码
	}

	// 1. 访客信息 (visitor_info)：所有访客
	type visitorJSON struct {
		Name     string        `json:"name"`
		Phone    string        `json:"phone"`
		Company  string        `json:"company"`
		Gender   int           `json:"gender"`
		Visitors []VisitorItem `json:"visitors,omitempty"`
	}
	visitorInfo := visitorJSON{
		Name:     mainVisitor.Name,
		Phone:    mainVisitor.Phone,
		Company:  mainVisitor.Company,
		Gender:   mainVisitor.Gender,
		Visitors: req.Visitors,
	}
	visitorInfoBytes, _ := json.Marshal(visitorInfo)
	appointment.VisitorInfo = string(visitorInfoBytes)

	// 2. 创建者信息 (creator_info)
	creatorInfo := map[string]interface{}{
		"id":    creatorUser.ID,
		"name":  creatorUser.RealName,
		"phone": creatorUser.Phone,
	}
	if creatorUser.RealName == "" {
		creatorInfo["name"] = creatorUser.Nickname
	}
	creatorInfoBytes, _ := json.Marshal(creatorInfo)
	appointment.CreatorInfo = string(creatorInfoBytes)

	// 3. 被访人信息 (employee_info)
	employeeInfo := map[string]interface{}{
		"name":  req.EmployeeName,
		"phone": req.EmployeePhone,
	}
	employeeInfoBytes, _ := json.Marshal(employeeInfo)
	appointment.EmployeeInfo = string(employeeInfoBytes)

	// 4. 携带物品 (goods_info)
	if len(req.Goods) > 0 {
		goodsInfoBytes, _ := json.Marshal(req.Goods)
		appointment.GoodsInfo = string(goodsInfoBytes)
	} else {
		appointment.GoodsInfo = "[]"
	}

	// 5. 到访区域 (areas_info)
	if len(req.VisitAreas) > 0 {
		areasInfoBytes, _ := json.Marshal(req.VisitAreas)
		appointment.AreasInfo = string(areasInfoBytes)
	} else {
		appointment.AreasInfo = "[]"
	}

	// 6. 车辆信息 (vehicle_info)
	type vehicleItemJSON struct {
		Plate string `json:"plate"`
	}
	var vehicleList []vehicleItemJSON
	if req.HasVehicle && len(req.LicensePlates) > 0 {
		for _, plate := range req.LicensePlates {
			if plate != "" {
				vehicleList = append(vehicleList, vehicleItemJSON{Plate: plate})
			}
		}
	}
	if vehicleList == nil {
		vehicleList = []vehicleItemJSON{}
	}
	vehicleInfoBytes, _ := json.Marshal(vehicleList)
	appointment.VehicleInfo = string(vehicleInfoBytes)

	// 开启事务
	tx := h.db.Begin()

	// 创建预约
	if err := tx.Create(&appointment).Error; err != nil {
		tx.Rollback()
		response.InternalError(c, "创建预约失败")
		return
	}

	// === 根据员工匹配情况选择审批流程 ===
	wfEngine := workflow.NewEngine(h.db)
	resolver := workflow.NewApproverResolver(h.db)

	// 按手机号匹配员工
	var matchedEmployee model.User
	var empUD model.UserDepartment
	var defaultApproverIDs []uint

	if err := h.db.Where("phone = ? AND role = ?", req.EmployeePhone, "employee").First(&matchedEmployee).Error; err == nil {
		// 匹配到员工 → 设置 VisitUserID
		tx.Model(&appointment).Update("visit_user_id", matchedEmployee.ID)

		// 查部门审批配置
		if err := tx.Where("user_id = ?", matchedEmployee.ID).First(&empUD).Error; err == nil {
			deptApproverIDs, _ := resolver.ResolveDeptApproverIDs(empUD.DepartmentID)
			vpIDs, _ := resolver.ResolveVPIDsByDepartment(empUD.DepartmentID)

			if len(deptApproverIDs) > 0 || len(vpIDs) > 0 {
				// 有部门配置 → 走三级审批
				deptApproverIDsJSON, _ := json.Marshal(deptApproverIDs)
				vpIDsJSON, _ := json.Marshal(vpIDs)
				_, err = wfEngine.StartInstanceByCode("visitor_approval", "appointment", appointment.ID, creatorUserID, map[string]interface{}{
					"employee_id":       matchedEmployee.ID,
					"dept_approver_ids": string(deptApproverIDsJSON),
					"vp_ids":            string(vpIDsJSON),
					"department_id":     empUD.DepartmentID,
					"department_name":   empUD.DepartmentName,
				})
				if err != nil {
					tx.Rollback()
					response.InternalError(c, "启动审批流程失败: "+err.Error())
					return
				}
				goto committed
			}
		}
		// 匹配到员工但未配置部门审批人/VP → 走兜底
		defaultApproverIDs, _ = resolver.GetGlobalApproverIDs()
	} else {
		// 未匹配到员工 → 走兜底
		tx.Model(&appointment).Update("visit_user_id", 0)
		defaultApproverIDs, _ = resolver.GetGlobalApproverIDs()
	}

	// 启动兜底审批流程
	if len(defaultApproverIDs) > 0 {
		defaultIDsJSON, _ := json.Marshal(defaultApproverIDs)
		_, err := wfEngine.StartInstanceByCode("visitor_approval_simple", "appointment", appointment.ID, creatorUserID, map[string]interface{}{
			"default_approver_ids": string(defaultIDsJSON),
		})
		if err != nil {
			tx.Rollback()
			response.InternalError(c, "启动审批流程失败: "+err.Error())
			return
		}
	}
	// 兜底审批人也未配置 → 不启动审批，预约直接通过（或保持待审核）

committed:
	tx.Commit()

	// 异步发送钉钉消息给被访员工（如果匹配到员工且有钉钉ID）
	if h.dingTalk != nil && matchedEmployee.ID > 0 {
		var empDept model.UserDepartment
		if err := h.db.Where("user_id = ?", matchedEmployee.ID).First(&empDept).Error; err != nil {
			logrus.WithFields(logrus.Fields{
				"employee_id": matchedEmployee.ID,
				"employee_name": matchedEmployee.RealName,
				"appointment_id": appointment.ID,
			}).Warn("匹配到员工但未找到 UserDepartment 记录，跳过钉钉通知")
		} else if empDept.DingTalkUserID == "" {
			logrus.WithFields(logrus.Fields{
				"employee_id":   matchedEmployee.ID,
				"employee_name": matchedEmployee.RealName,
				"appointment_id": appointment.ID,
			}).Warn("员工 UserDepartment 记录中 DingTalkUserID 为空，跳过钉钉通知")
		} else {
			dingTalkUserID := empDept.DingTalkUserID
			logrus.WithFields(logrus.Fields{
				"employee_id":   matchedEmployee.ID,
				"employee_name": matchedEmployee.RealName,
				"dingtalk_user_id": dingTalkUserID,
				"appointment_id":  appointment.ID,
			}).Info("准备发送钉钉通知给被访员工")

			go func() {
				// 从预约对象中解析访客信息
				var vi struct {
					Name     string `json:"name"`
					Phone    string `json:"phone"`
					Company  string `json:"company"`
					Gender   int    `json:"gender"`
					Visitors []struct {
						Name     string `json:"name"`
						Phone    string `json:"phone"`
						Company  string `json:"company"`
						Gender   int    `json:"gender"`
						IDType   string `json:"id_type"`
						IDNumber string `json:"id_number"`
					} `json:"visitors"`
				}
				json.Unmarshal([]byte(appointment.VisitorInfo), &vi)

				// 拼接所有访客表格行（访客1、访客2、访客3……）
				type visitorRow struct {
					Name     string
					Company  string
					Phone    string
					IDCard   string
				}
				var allVisitors []visitorRow
				allVisitors = append(allVisitors, visitorRow{
					Name:    vi.Name,
					Company: vi.Company,
					Phone:   vi.Phone,
				})
				for _, v := range vi.Visitors {
					if v.Name != "" && (v.Name != vi.Name || v.Phone != vi.Phone) {
						idCard := ""
						if v.IDType != "" && v.IDNumber != "" {
							idCard = v.IDType + " " + v.IDNumber
						}
						allVisitors = append(allVisitors, visitorRow{
							Name:    v.Name,
							Company: v.Company,
							Phone:   v.Phone,
							IDCard:  idCard,
						})
					}
				}
				visitorList := ""
				for i, v := range allVisitors {
					idx := i + 1
					line := fmt.Sprintf("**访客%d：** %s", idx, v.Name)
					if v.Phone != "" {
						line += "（" + v.Phone + "）"
					}
					if v.Company != "" {
						line += "<br>" + v.Company
					}
					if v.IDCard != "" {
						line += "<br>" + v.IDCard
					}
					visitorList += line + "<br>"
				}

				// 从预约对象中解析车辆信息
				type vehicleItem struct {
					Plate string `json:"plate"`
				}
				var vehicles []vehicleItem
				json.Unmarshal([]byte(appointment.VehicleInfo), &vehicles)
				vehicleText := ""
				for _, v := range vehicles {
					if v.Plate != "" {
						vehicleText += "- " + v.Plate + "<br>"
					}
				}

				msg := &dingtalk.AppointmentMessage{
					VisitorList:   visitorList,
					EmployeeName:  req.EmployeeName,
					VisitTime:     dingtalk.FormatCSTTime(appointment.VisitStartTime, appointment.VisitEndTime),
					VisitPurpose:  req.VisitPurpose,
					Remark:        req.Remark,
					VehicleInfo:   vehicleText,
					AppointmentID: appointment.ID,
				}
				title, markdown := dingtalk.BuildNewAppointmentMsg(msg)
				logrus.WithFields(logrus.Fields{
					"dingtalk_user_id": dingTalkUserID,
					"title":            title,
					"appointment_id":   appointment.ID,
				}).Info("钉钉机器人群发消息开始")
				if err := h.dingTalk.BatchSendRobotMessage([]string{dingTalkUserID}, title, markdown); err != nil {
					logrus.WithError(err).WithFields(logrus.Fields{
						"dingtalk_user_id": dingTalkUserID,
						"appointment_id":   appointment.ID,
					}).Error("发送钉钉消息给被访员工失败")
				} else {
					logrus.WithFields(logrus.Fields{
						"dingtalk_user_id": dingTalkUserID,
						"appointment_id":   appointment.ID,
					}).Info("发送钉钉消息给被访员工成功")
				}
			}()
		}
	}

	response.SuccessWithMessage(c, "预约创建成功", appointment)
}

// GetMyAppointments 我的预约列表（包括我创建的 + 别人预约我的）
// @Summary      我的预约列表
// @Description  获取当前用户的预约列表，包括我创建的和别人预约我的
// @Tags         预约管理
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        status  query     integer  false  "预约状态筛选"
// @Success      200     {object}  response.Response{data=[]model.Appointment}  "预约列表"
// @Router       /appointment/appointments/my [get]
func (h *AppointmentHandler) GetMyAppointments(c *gin.Context) {
	userID := utils.GetUserID(c)
	page, pageSize := utils.ParsePagination(c)
	statusStr := c.DefaultQuery("status", "")
	typeFilter := c.DefaultQuery("type", "") // created / visited / approvable / mine

	var appointments []model.Appointment
	var total int64

	query := h.db.Model(&model.Appointment{})
	if typeFilter == "created" {
		query = query.Where("creator_user_id = ?", userID)
	} else if typeFilter == "visited" {
		query = query.Where("visit_user_id = ?", userID)
	} else if typeFilter == "approvable" {
		// 查询当前用户在工作流中有待审批任务的预约
		subQuery := h.db.Table("process_instances").
			Select("process_instances.business_id").
			Joins("JOIN tasks ON tasks.instance_id = process_instances.id").
			Where("process_instances.business_type = ? AND tasks.assignee_id = ? AND tasks.status = ?", "appointment", userID, 0)
		query = query.Where("id IN (?)", subQuery)
	} else if typeFilter == "mine" {
		// 预约我的：我是被访人 OR 我在工作流中有审批任务
		subQuery := h.db.Table("process_instances").
			Select("process_instances.business_id").
			Joins("JOIN tasks ON tasks.instance_id = process_instances.id").
			Where("process_instances.business_type = ? AND tasks.assignee_id = ?", "appointment", userID)
		query = query.Where("visit_user_id = ? OR id IN (?)", userID, subQuery)
	} else {
		query = query.Where("creator_user_id = ? OR visit_user_id = ?", userID, userID)
	}

	if statusStr != "" {
		status, _ := strconv.Atoi(statusStr)
		query = query.Where("status = ?", status)
	}

	query.Count(&total)
	query.Offset(utils.GetOffset(page, pageSize)).Limit(pageSize).
		Order("created_at DESC").Find(&appointments)

	response.SuccessPage(c, appointments, total, page, pageSize)
}

// GetAllAppointments 全部预约列表（员工专属）
// @Summary      全部预约列表
// @Description  获取所有预约申请，按申请时间降序排列（仅员工可用）
// @Tags         预约管理
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        page      query     integer  false  "页码"
// @Param        page_size query     integer  false  "每页数量"
// @Param        status    query     integer  false  "预约状态筛选"
// @Success      200       {object}  response.Response{data=[]model.Appointment}  "预约列表"
// @Router       /appointment/appointments/all [get]
func (h *AppointmentHandler) GetAllAppointments(c *gin.Context) {
	page, pageSize := utils.ParsePagination(c)
	statusStr := c.DefaultQuery("status", "")

	var appointments []model.Appointment
	var total int64

	query := h.db.Model(&model.Appointment{})

	if statusStr != "" {
		status, _ := strconv.Atoi(statusStr)
		query = query.Where("status = ?", status)
	}

	query.Count(&total)
	query.Offset(utils.GetOffset(page, pageSize)).Limit(pageSize).
		Order("created_at DESC").Find(&appointments)

	response.SuccessPage(c, appointments, total, page, pageSize)
}

// GetAppointmentDetail 预约详情
// @Summary      获取预约详情
// @Description  根据ID获取预约详细信息
// @Tags         预约管理
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id   path      integer  true  "预约ID"
// @Success      200  {object}  response.Response{data=model.Appointment}  "预约详情"
// @Router       /appointment/appointments/{id} [get]
func (h *AppointmentHandler) GetAppointmentDetail(c *gin.Context) {
	id, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil {
		response.BadRequest(c, "参数错误")
		return
	}

	var appointment model.Appointment
	if err := h.db.First(&appointment, id).Error; err != nil {
		response.NotFound(c, "预约不存在")
		return
	}

	// 解析 JSON 字符串字段
	var visitors []map[string]interface{}
	if err := json.Unmarshal([]byte(appointment.VisitorInfo), &visitors); err != nil {
		// 兼容旧格式：包含顶层 name/phone + 嵌套 visitors 数组
		var old struct {
			Name     string                   `json:"name"`
			Phone    string                   `json:"phone"`
			Company  string                   `json:"company"`
			Gender   int                      `json:"gender"`
			Visitors []map[string]interface{} `json:"visitors"`
		}
		if err2 := json.Unmarshal([]byte(appointment.VisitorInfo), &old); err2 == nil && len(old.Visitors) > 0 {
			visitors = old.Visitors
		} else {
			visitors = []map[string]interface{}{}
		}
	}

	var creatorInfo map[string]interface{}
	json.Unmarshal([]byte(appointment.CreatorInfo), &creatorInfo)
	if creatorInfo == nil {
		creatorInfo = map[string]interface{}{}
	}

	var employeeInfo map[string]interface{}
	json.Unmarshal([]byte(appointment.EmployeeInfo), &employeeInfo)
	if employeeInfo == nil {
		employeeInfo = map[string]interface{}{}
	}

	var goods []map[string]interface{}
	json.Unmarshal([]byte(appointment.GoodsInfo), &goods)
	if goods == nil {
		goods = []map[string]interface{}{}
	}

	var areas []string
	json.Unmarshal([]byte(appointment.AreasInfo), &areas)
	if areas == nil {
		areas = []string{}
	}

	var vehicles []map[string]interface{}
	if err := json.Unmarshal([]byte(appointment.VehicleInfo), &vehicles); err != nil {
		// 兼容旧格式：{"license_plates": [...]}
		var old map[string]interface{}
		if err2 := json.Unmarshal([]byte(appointment.VehicleInfo), &old); err2 == nil {
			if plates, ok := old["license_plates"]; ok {
				if pArr, ok := plates.([]interface{}); ok {
					for _, p := range pArr {
						vehicles = append(vehicles, map[string]interface{}{
							"plate": p,
						})
					}
				}
			}
		}
	}
	if vehicles == nil {
		vehicles = []map[string]interface{}{}
	}

	// 保安角色查看时对手机号脱敏
	if utils.GetRoleCode(c) == "guard" {
		maskPhone := func(phone string) string {
			if len(phone) == 11 {
				return phone[:3] + "****" + phone[7:]
			}
			if len(phone) > 3 {
				return phone[:3] + "****" + phone[len(phone)-4:]
			}
			return phone
		}
		for i, v := range visitors {
			if phone, ok := v["phone"].(string); ok {
				visitors[i]["phone"] = maskPhone(phone)
			}
		}
		if phone, ok := creatorInfo["phone"].(string); ok {
			creatorInfo["phone"] = maskPhone(phone)
		}
		if phone, ok := employeeInfo["phone"].(string); ok {
			employeeInfo["phone"] = maskPhone(phone)
		}
	}

	resp := map[string]interface{}{
		"id":               appointment.ID,
		"created_at":       appointment.CreatedAt,
		"updated_at":       appointment.UpdatedAt,
		"visit_user_id":    appointment.VisitUserID,
		"creator_user_id":  appointment.CreatorUserID,
		"visit_type":       appointment.VisitType,
		"visit_start_time": appointment.VisitStartTime,
		"visit_end_time":   appointment.VisitEndTime,
		"status":           appointment.Status,
		"remark":           appointment.Remark,
		"comment":          appointment.Comment,
		"visit_purpose":    appointment.VisitPurpose,
		"visitor_company":  appointment.VisitorCompany,
		"qr_code_url":      appointment.QrCodeURL,
		"visitors":         visitors,
		"creator_info":     creatorInfo,
		"employee_info":    employeeInfo,
		"goods":            goods,
		"areas":            areas,
		"vehicles":         vehicles,
	}
	response.Success(c, resp)
}

// GetApprovalSteps 获取预约的审批步骤列表
// ============ 邀请管理 ============

// CreateInvitation 生成邀请链接
// @Summary      生成邀请链接
// @Description  员工生成访客邀请链接
// @Tags         邀请管理
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        body  body      object{visitor_name=string,visitor_phone=string,visit_type=string,visit_area=string,valid_days=int,max_use_count=int}  true  "邀请信息"
// @Success      200   {object}  response.Response{data=model.Invitation}  "创建成功"
// @Router       /appointment/invitations [post]
func (h *AppointmentHandler) CreateInvitation(c *gin.Context) {
	userID := utils.GetUserID(c)

	// 查找员工信息
	var employee model.User
	if err := h.db.Where("user_id = ?", userID).First(&employee).Error; err != nil {
		response.BadRequest(c, "您不是员工，无法生成邀请")
		return
	}

	var req struct {
		VisitorName  string `json:"visitor_name"`
		VisitorPhone string `json:"visitor_phone"`
		VisitType    string `json:"visit_type"`
		VisitArea    string `json:"visit_area"`
		ValidDays    int    `json:"valid_days"`
		MaxUseCount  int    `json:"max_use_count"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "参数错误")
		return
	}

	if req.ValidDays <= 0 {
		req.ValidDays = 7
	}
	if req.MaxUseCount <= 0 {
		req.MaxUseCount = 1
	}

	now := time.Now()
	invitation := model.Invitation{
		EmployeeID:   employee.ID,
		InviteCode:   utils.GenerateInviteCode(),
		VisitorName:  req.VisitorName,
		VisitorPhone: req.VisitorPhone,
		VisitType:    req.VisitType,
		VisitArea:    req.VisitArea,
		ValidFrom:    now,
		ValidUntil:   now.AddDate(0, 0, req.ValidDays),
		MaxUseCount:  req.MaxUseCount,
		UsedCount:    0,
		IsActive:     true,
	}

	if err := h.db.Create(&invitation).Error; err != nil {
		response.InternalError(c, "创建邀请失败")
		return
	}

	response.SuccessWithMessage(c, "邀请创建成功", invitation)
}

// GetMyInvitations 我的邀请列表
// @Summary      我的邀请列表
// @Description  获取当前员工创建的邀请列表
// @Tags         邀请管理
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Success      200   {object}  response.Response{data=[]model.Invitation}  "邀请列表"
// @Router       /appointment/invitations/my [get]
func (h *AppointmentHandler) GetMyInvitations(c *gin.Context) {
	userID := utils.GetUserID(c)
	page, pageSize := utils.ParsePagination(c)

	var employee model.User
	if err := h.db.Where("user_id = ?", userID).First(&employee).Error; err != nil {
		response.BadRequest(c, "您不是员工")
		return
	}

	var invitations []model.Invitation
	var total int64

	query := h.db.Model(&model.Invitation{}).Where("employee_id = ?", employee.ID)
	query.Count(&total)
	query.Offset(utils.GetOffset(page, pageSize)).Limit(pageSize).
		Order("created_at DESC").Find(&invitations)

	response.SuccessPage(c, invitations, total, page, pageSize)
}

// ============ 公开接口 ============

// GetPublicInvitation 公开邀请详情
// @Summary      公开邀请详情
// @Description  根据邀请码获取公开的邀请详情
// @Tags         公开接口
// @Accept       json
// @Produce      json
// @Param        code  path      string  true  "邀请码"
// @Success      200   {object}  response.Response  "邀请详情"
// @Router       /public/invitations/{code} [get]
func (h *AppointmentHandler) GetPublicInvitation(c *gin.Context) {
	code := c.Param("code")

	var invitation model.Invitation
	if err := h.db.Preload("Employee").Where("invite_code = ?", code).First(&invitation).Error; err != nil {
		response.NotFound(c, "邀请不存在或已失效")
		return
	}

	if !invitation.IsActive {
		response.BadRequest(c, "邀请已失效")
		return
	}

	if time.Now().After(invitation.ValidUntil) {
		response.BadRequest(c, "邀请已过期")
		return
	}

	if invitation.UsedCount >= invitation.MaxUseCount {
		response.BadRequest(c, "邀请使用次数已达上限")
		return
	}

	// InvitationVO 邀请详情视图对象
	type InvitationVO struct {
		InviteCode    string `json:"invite_code"`    // 邀请码
		EmployeeID    uint   `json:"employee_id"`    // 发起邀请的员工ID
		EmployeeName  string `json:"employee_name"`  // 发起邀请的员工姓名
		EmployeePhone string `json:"employee_phone"` // 发起邀请的员工电话
		Department    string `json:"department"`     // 员工所属部门
		VisitorName   string `json:"visitor_name"`   // 被邀请访客姓名
		VisitorPhone  string `json:"visitor_phone"`  // 被邀请访客电话
		VisitType     string `json:"visit_type"`     // 来访目的
		VisitArea     string `json:"visit_area"`     // 允许访问的区域
		ValidUntil    string `json:"valid_until"`    // 邀请失效时间
	}

	// 查询邀请人部门信息
	deptName := ""
	var empUD model.UserDepartment
	if err := h.db.Where("user_id = ?", invitation.EmployeeID).First(&empUD).Error; err == nil {
		deptName = empUD.DepartmentName
	}

	vo := InvitationVO{
		InviteCode:    invitation.InviteCode,
		EmployeeID:    invitation.EmployeeID,
		EmployeeName:  invitation.Employee.RealName,
		EmployeePhone: invitation.Employee.Phone,
		Department:    deptName,
		VisitorName:   invitation.VisitorName,
		VisitorPhone:  invitation.VisitorPhone,
		VisitType:     invitation.VisitType,
		VisitArea:     invitation.VisitArea,
		ValidUntil:    invitation.ValidUntil.Format("2006-01-02 15:04:05"),
	}

	response.Success(c, vo)
}

// ============ 保安核验 ============

// GetTodayAppointments 今日预约列表
// @Summary      今日预约列表
// @Description  保安查看今日已审核通过的预约列表
// @Tags         保安核验
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Success      200   {object}  response.Response{data=object{stats=object{total=int,checked_in=int,checked_out=int},appointments=[]model.Appointment}}  "预约列表"
// @Router       /guard/today-appointments [get]
func (h *AppointmentHandler) GetTodayAppointments(c *gin.Context) {
	today := time.Now().Format("2006-01-02")
	startOfDay, _ := time.Parse("2006-01-02", today)
	endOfDay := startOfDay.Add(24 * time.Hour)

	var total int64

	query := h.db.Model(&model.Appointment{}).
		Where("status = 1"). // 已通过
		Where("visit_start_time >= ? AND visit_start_time < ?", startOfDay, endOfDay)

	query.Count(&total)

	// 统计已进场/已出场数量
	var checkedIn, checkedOut int64
	h.db.Model(&model.VisitRecord{}).
		Where("created_at >= ? AND created_at < ?", startOfDay, endOfDay).
		Where("check_type = ?", 1).
		Count(&checkedIn)
	h.db.Model(&model.VisitRecord{}).
		Where("created_at >= ? AND created_at < ?", startOfDay, endOfDay).
		Where("check_type = ?", 2).
		Count(&checkedOut)

	stats := map[string]interface{}{
		"total":       total,
		"checked_in":  checkedIn,
		"checked_out": checkedOut,
	}

	response.Success(c, map[string]interface{}{
		"stats": stats,
	})
}

// CheckIn 登记进场
// @Summary      登记进场
// @Description  保安登记访客进场
// @Tags         保安核验
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        body  body      object{appointment_id=uint,remark=string}  true  "登记信息"
// @Success      200   {object}  response.Response  "登记进场成功"
// @Router       /guard/check-in [post]
func (h *AppointmentHandler) CheckIn(c *gin.Context) {
	h.checkRecord(c, 1)
}

// CheckOut 登记出场
// @Summary      登记出场
// @Description  保安登记访客出场
// @Tags         保安核验
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        body  body      object{appointment_id=uint,remark=string}  true  "登记信息"
// @Success      200   {object}  response.Response  "登记出场成功"
// @Router       /guard/check-out [post]
func (h *AppointmentHandler) CheckOut(c *gin.Context) {
	h.checkRecord(c, 2)
}

// GetVisitRecords 获取预约的进出场记录
// @Summary      获取进出场记录
// @Description  按预约ID查询进出场记录列表
// @Tags         保安核验
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        appointment_id  query  uint  true  "预约ID"
// @Success      200  {object}  response.Response{data=[]model.VisitRecord}
// @Router       /guard/records [get]
func (h *AppointmentHandler) GetVisitRecords(c *gin.Context) {
	appointmentIDStr := c.Query("appointment_id")
	appointmentID, err := strconv.ParseUint(appointmentIDStr, 10, 64)
	if err != nil {
		response.BadRequest(c, "appointment_id 参数错误")
		return
	}

	var records []model.VisitRecord
	h.db.Where("appointment_id = ?", appointmentID).
		Order("check_time ASC").
		Find(&records)
	if records == nil {
		records = []model.VisitRecord{}
	}
	response.Success(c, records)
}

func (h *AppointmentHandler) checkRecord(c *gin.Context, checkType int) {
	var req struct {
		AppointmentID uint   `json:"appointment_id" binding:"required"`
		Remark        string `json:"remark"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "参数错误")
		return
	}

	var appointment model.Appointment
	if err := h.db.First(&appointment, req.AppointmentID).Error; err != nil {
		response.NotFound(c, "预约不存在")
		return
	}

	if appointment.Status != 1 {
		response.BadRequest(c, "预约未通过审核")
		return
	}

	// 出场时校验是否已进场
	if checkType == 2 {
		var checkInCount int64
		h.db.Model(&model.VisitRecord{}).
			Where("appointment_id = ? AND check_type = 1 AND is_valid = true", req.AppointmentID).
			Count(&checkInCount)
		if checkInCount == 0 {
			response.BadRequest(c, "该预约尚未进场，无法登记出场")
			return
		}
	}

	guardID := utils.GetUserID(c)
	record := model.VisitRecord{
		AppointmentID: req.AppointmentID,
		GuardID:       guardID,
		CheckType:     checkType,
		CheckTime:     time.Now(),
		IsValid:       true,
		Remark:        req.Remark,
	}

	if err := h.db.Create(&record).Error; err != nil {
		response.InternalError(c, "登记失败")
		return
	}

	action := "进场"
	if checkType == 2 {
		action = "出场"
	}

	response.SuccessWithMessage(c, "登记"+action+"成功", record)
}

// ============ 领导追溯 ============

// GetLeaderAppointments 领导查看预约（仅部门领导可查看）
// @Summary      领导查看预约
// @Description  部门领导查看本部门员工的预约列表
// @Tags         领导审批
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Success      200   {object}  response.Response{data=[]model.Appointment}  "预约列表"
// @Router       /leader/appointments [get]
func (h *AppointmentHandler) GetLeaderAppointments(c *gin.Context) {
	userID := utils.GetUserID(c)
	page, pageSize := utils.ParsePagination(c)

	// 查找当前用户的员工信息，校验是否为领导
	var employee model.User
	if err := h.db.Where("user_id = ?", userID).First(&employee).Error; err != nil {
		response.BadRequest(c, "您不是员工")
		return
	}
	var empUD model.UserDepartment
	if err := h.db.Where("user_id = ?", employee.ID).First(&empUD).Error; err != nil {
		response.BadRequest(c, "您未分配部门")
		return
	}

	// 只有领导可以查看部门预约
	isLeader := empUD.EmployeeType == "department_leader" || empUD.EmployeeType == "deputy_leader" || empUD.EmployeeType == "vice_president" || empUD.EmployeeType == "general_manager"
	if !isLeader {
		response.Forbidden(c, "仅部门领导可查看")
		return
	}

	// 查找同部门员工的预约 + 被指定为审批人的预约
	deptID := empUD.DepartmentID

	var employeeIDs []uint
	h.db.Model(&model.UserDepartment{}).
		Where("department_id = ?", deptID).
		Pluck("user_id", &employeeIDs)

	var appointments []model.Appointment
	var total int64

	query := h.db.Model(&model.Appointment{}).
		Where("visit_user_id IN ? OR id IN (SELECT pi.business_id FROM process_instances pi JOIN tasks t ON t.instance_id = pi.id WHERE pi.business_type = ? AND t.assignee_id = ?)", employeeIDs, "appointment", employee.ID)

	query.Count(&total)
	query.Offset(utils.GetOffset(page, pageSize)).Limit(pageSize).
		Order("created_at DESC").Find(&appointments)

	response.SuccessPage(c, appointments, total, page, pageSize)
}

// ============ 员工验证 ============

// VerifyEmployee 验证员工姓名+手机号
// @Summary      验证员工姓名+手机号
// @Description  根据姓名和手机号验证是否为正式员工
// @Tags         员工管理
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        body  body      object{name=string,phone=string}  true  "姓名和手机号"
// @Success      200   {object}  response.Response  "员工信息"
// @Router       /appointment/employees/verify [post]
func (h *AppointmentHandler) VerifyEmployee(c *gin.Context) {
	var req struct {
		Name  string `json:"name" binding:"required"`
		Phone string `json:"phone" binding:"required"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "参数错误")
		return
	}

	var employee model.User
	if err := h.db.Where("real_name = ? AND phone = ? AND role = 'employee' AND status = 1", req.Name, req.Phone).First(&employee).Error; err != nil {
		response.NotFound(c, "未找到匹配的员工，请确认姓名和手机号是否正确")
		return
	}

	type EmployeeVO struct {
		ID           uint   `json:"id"`
		UserID       uint   `json:"user_id"`
		Name         string `json:"name"`
		Department   string `json:"department"`
		DepartmentID *uint  `json:"department_id"`
		Phone        string `json:"phone"`
		Position     string `json:"position"`
	}

	// 查询部门信息
	deptName := ""
	var deptID *uint
	var empUD model.UserDepartment
	if err := h.db.Where("user_id = ?", employee.ID).First(&empUD).Error; err == nil {
		deptName = empUD.DepartmentName
		deptID = &empUD.DepartmentID
	}

	response.Success(c, EmployeeVO{
		ID:           employee.ID,
		UserID:       employee.ID,
		Name:         employee.RealName,
		Department:   deptName,
		DepartmentID: deptID,
		Phone:        employee.Phone,
		Position:     empUD.Position,
	})
}

// ============ 访客历史 ============

// GetMyVisitors 获取当前用户的历史访客列表（按手机号去重，取最新记录）
// @Summary      获取历史访客列表
// @Description  获取当前用户的常用访客列表，按手机号去重
// @Tags         访客管理
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Success      200   {object}  response.Response  "访客列表"
// @Router       /appointment/visitors/my [get]
func (h *AppointmentHandler) GetMyVisitors(c *gin.Context) {
	userID := utils.GetUserID(c)

	// 查找当前用户所有预约的访客（作为访客创建时的 user_id 查找）
	var visitors []model.Visitor
	if err := h.db.Where("user_id = ?", userID).
		Order("created_at DESC").
		Find(&visitors).Error; err != nil {
		response.InternalError(c, "查询失败")
		return
	}

	// 按手机号去重
	seen := make(map[string]bool)
	type VisitorVO struct {
		ID       uint   `json:"id"`
		Company  string `json:"company"`
		Name     string `json:"name"`
		Phone    string `json:"phone"`
		Gender   int    `json:"gender"`
		IDType   string `json:"id_type"`
		IDNumber string `json:"id_number"`
	}
	var result []VisitorVO
	for _, v := range visitors {
		if seen[v.Phone] {
			continue
		}
		seen[v.Phone] = true
		result = append(result, VisitorVO{
			ID:       v.ID,
			Company:  v.Company,
			Name:     v.Name,
			Phone:    v.Phone,
			Gender:   v.Gender,
			IDType:   v.IDType,
			IDNumber: v.IDNumber,
		})
	}

	response.Success(c, result)
}

// SaveVisitorTemplate 保存常用访客
// @Summary      保存常用访客
// @Description  保存或更新常用访客信息
// @Tags         访客管理
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        body  body      object{name=string,phone=string,company=string,gender=int,id_type=string,id_number=string}  true  "访客信息"
// @Success      200   {object}  response.Response  "保存成功"
// @Router       /appointment/visitor-templates [post]
func (h *AppointmentHandler) SaveVisitorTemplate(c *gin.Context) {
	userID := utils.GetUserID(c)

	var req struct {
		Name     string `json:"name" binding:"required"`
		Phone    string `json:"phone" binding:"required"`
		Company  string `json:"company"`
		Gender   int    `json:"gender"`
		IDType   string `json:"id_type"`
		IDNumber string `json:"id_number"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "参数错误")
		return
	}

	// 先按手机号查找是否已存在
	var existing model.Visitor
	if err := h.db.Where("phone = ? AND user_id = ?", req.Phone, userID).First(&existing).Error; err == nil {
		// 更新已有记录
		h.db.Model(&existing).Updates(map[string]interface{}{
			"name":      req.Name,
			"company":   req.Company,
			"gender":    req.Gender,
			"id_type":   req.IDType,
			"id_number": req.IDNumber,
		})
		response.SuccessWithMessage(c, "已更新", nil)
		return
	}

	visitor := model.Visitor{
		UserID:   &userID,
		Name:     req.Name,
		Phone:    req.Phone,
		Company:  req.Company,
		Gender:   req.Gender,
		IDType:   req.IDType,
		IDNumber: req.IDNumber,
	}
	if err := h.db.Create(&visitor).Error; err != nil {
		response.InternalError(c, "保存失败")
		return
	}
	response.SuccessWithMessage(c, "保存成功", nil)
}

// DeleteVisitorTemplate 删除常用访客
// @Summary      删除常用访客
// @Description  根据ID删除常用访客记录
// @Tags         访客管理
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id    path      integer  true  "访客ID"
// @Success      200   {object}  response.Response  "删除成功"
// @Router       /appointment/visitor-templates/{id} [delete]
func (h *AppointmentHandler) DeleteVisitorTemplate(c *gin.Context) {
	userID := utils.GetUserID(c)
	id, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil {
		response.BadRequest(c, "参数错误")
		return
	}
	result := h.db.Where("id = ? AND user_id = ?", id, userID).Delete(&model.Visitor{})
	if result.RowsAffected == 0 {
		response.NotFound(c, "记录不存在")
		return
	}
	response.SuccessWithMessage(c, "删除成功", nil)
}

// ============ 物品模板管理 ============

// ListGoodsTemplates 获取当前用户的物品模板列表
// @Summary      获取物品模板列表
// @Description  获取当前用户的物品模板列表
// @Tags         物品模板
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Success      200   {object}  response.Response{data=[]model.GoodsTemplate}  "物品模板列表"
// @Router       /appointment/goods-templates [get]
func (h *AppointmentHandler) ListGoodsTemplates(c *gin.Context) {
	userID := utils.GetUserID(c)

	var templates []model.GoodsTemplate
	if err := h.db.Where("user_id = ?", userID).
		Order("created_at DESC").
		Find(&templates).Error; err != nil {
		response.InternalError(c, "查询失败")
		return
	}

	response.Success(c, templates)
}

// CreateGoodsTemplate 创建物品模板
// @Summary      创建物品模板
// @Description  创建新的物品模板
// @Tags         物品模板
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        body  body      object{name=string,quantity=int,model=string,remark=string}  true  "物品模板信息"
// @Success      200   {object}  response.Response{data=model.GoodsTemplate}  "创建成功"
// @Router       /appointment/goods-templates [post]
func (h *AppointmentHandler) CreateGoodsTemplate(c *gin.Context) {
	userID := utils.GetUserID(c)

	var req struct {
		Name     string `json:"name" binding:"required"`
		Quantity int    `json:"quantity"`
		Model    string `json:"model"`
		Remark   string `json:"remark"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "参数错误")
		return
	}

	if req.Quantity <= 0 {
		req.Quantity = 1
	}

	template := model.GoodsTemplate{
		UserID:   userID,
		Name:     req.Name,
		Quantity: req.Quantity,
		Model:    req.Model,
		Remark:   req.Remark,
	}

	if err := h.db.Create(&template).Error; err != nil {
		response.InternalError(c, "创建失败")
		return
	}

	response.SuccessWithMessage(c, "创建成功", template)
}

// UpdateGoodsTemplate 更新物品模板
// @Summary      更新物品模板
// @Description  根据ID更新物品模板信息
// @Tags         物品模板
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id    path      integer  true  "物品模板ID"
// @Param        body  body      object{name=string,quantity=int,model=string,remark=string}  true  "物品模板信息"
// @Success      200   {object}  response.Response  "更新成功"
// @Router       /appointment/goods-templates/{id} [put]
func (h *AppointmentHandler) UpdateGoodsTemplate(c *gin.Context) {
	userID := utils.GetUserID(c)
	id, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil {
		response.BadRequest(c, "参数错误")
		return
	}

	var template model.GoodsTemplate
	if err := h.db.Where("id = ? AND user_id = ?", id, userID).First(&template).Error; err != nil {
		response.NotFound(c, "物品模板不存在")
		return
	}

	var req struct {
		Name     string `json:"name"`
		Quantity int    `json:"quantity"`
		Model    string `json:"model"`
		Remark   string `json:"remark"`
	}

	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "参数错误")
		return
	}

	updates := map[string]interface{}{}
	if req.Name != "" {
		updates["name"] = req.Name
	}
	if req.Quantity > 0 {
		updates["quantity"] = req.Quantity
	}
	updates["model"] = req.Model
	updates["remark"] = req.Remark

	if err := h.db.Model(&template).Updates(updates).Error; err != nil {
		response.InternalError(c, "更新失败")
		return
	}

	response.SuccessWithMessage(c, "更新成功", nil)
}

// DeleteGoodsTemplate 删除物品模板
// @Summary      删除物品模板
// @Description  根据ID删除物品模板
// @Tags         物品模板
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id    path      integer  true  "物品模板ID"
// @Success      200   {object}  response.Response  "删除成功"
// @Router       /appointment/goods-templates/{id} [delete]
func (h *AppointmentHandler) DeleteGoodsTemplate(c *gin.Context) {
	userID := utils.GetUserID(c)
	id, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil {
		response.BadRequest(c, "参数错误")
		return
	}

	result := h.db.Where("id = ? AND user_id = ?", id, userID).Delete(&model.GoodsTemplate{})
	if result.RowsAffected == 0 {
		response.NotFound(c, "物品模板不存在")
		return
	}

	response.SuccessWithMessage(c, "删除成功", nil)
}

// ============ 车辆信息管理 ============

// ListVehicleInfos 获取车辆信息列表
// @Summary      获取车辆信息列表
// @Description  获取当前用户保存的车辆信息列表
// @Tags         车辆信息
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Success      200  {object}  response.Response{data=[]model.VehicleInfo}  "车辆列表"
// @Router       /appointment/vehicles [get]
func (h *AppointmentHandler) ListVehicleInfos(c *gin.Context) {
	userID := utils.GetUserID(c)
	var vehicles []model.VehicleInfo
	h.db.Where("user_id = ?", userID).Order("created_at DESC").Find(&vehicles)
	response.Success(c, vehicles)
}

// CreateVehicleInfo 创建车辆信息
// @Summary      创建车辆信息
// @Description  保存新的车辆信息到当前用户
// @Tags         车辆信息
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        body  body      object{license_plate=string,brand=string,color=string}  true  "车辆信息"
// @Success      200   {object}  response.Response{data=model.VehicleInfo}  "创建成功"
// @Router       /appointment/vehicles [post]
func (h *AppointmentHandler) CreateVehicleInfo(c *gin.Context) {
	userID := utils.GetUserID(c)
	var req struct {
		LicensePlate string `json:"license_plate" binding:"required"`
		Brand        string `json:"brand"`
		Color        string `json:"color"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "参数错误")
		return
	}
	vehicle := model.VehicleInfo{
		UserID:       userID,
		LicensePlate: req.LicensePlate,
		Brand:        req.Brand,
		Color:        req.Color,
	}
	if err := h.db.Create(&vehicle).Error; err != nil {
		response.InternalError(c, "保存失败")
		return
	}
	response.SuccessWithMessage(c, "保存成功", vehicle)
}

// DeleteVehicleInfo 删除车辆信息
// @Summary      删除车辆信息
// @Description  根据ID删除保存的车辆信息
// @Tags         车辆信息
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id    path      integer  true  "车辆信息ID"
// @Success      200   {object}  response.Response  "删除成功"
// @Router       /appointment/vehicles/{id} [delete]
func (h *AppointmentHandler) DeleteVehicleInfo(c *gin.Context) {
	userID := utils.GetUserID(c)
	id, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil {
		response.BadRequest(c, "参数错误")
		return
	}
	result := h.db.Where("id = ? AND user_id = ?", id, userID).Delete(&model.VehicleInfo{})
	if result.RowsAffected == 0 {
		response.NotFound(c, "车辆信息不存在")
		return
	}
	response.SuccessWithMessage(c, "删除成功", nil)
}

// UpdateVehicleInfo 更新车辆信息
// @Summary      更新车辆信息
// @Description  更新已保存的车辆信息
// @Tags         车辆信息
// @Accept       json
// @Produce      json
// @Security     BearerAuth
// @Param        id    path      integer  true  "车辆信息ID"
// @Param        body  body      object{license_plate=string,brand=string,color=string}  true  "车辆信息"
// @Success      200   {object}  response.Response  "更新成功"
// @Router       /appointment/vehicles/{id} [put]
func (h *AppointmentHandler) UpdateVehicleInfo(c *gin.Context) {
	userID := utils.GetUserID(c)
	id, err := strconv.ParseUint(c.Param("id"), 10, 64)
	if err != nil {
		response.BadRequest(c, "参数错误")
		return
	}
	var req struct {
		LicensePlate string `json:"license_plate" binding:"required"`
		Brand        string `json:"brand"`
		Color        string `json:"color"`
	}
	if err := c.ShouldBindJSON(&req); err != nil {
		response.BadRequest(c, "参数错误")
		return
	}
	result := h.db.Model(&model.VehicleInfo{}).Where("id = ? AND user_id = ?", id, userID).Updates(map[string]interface{}{
		"license_plate": req.LicensePlate,
		"brand":         req.Brand,
		"color":         req.Color,
	})
	if result.RowsAffected == 0 {
		response.NotFound(c, "车辆信息不存在")
		return
	}
	response.SuccessWithMessage(c, "更新成功", nil)
}

// ============ 通用数据查询 ============

// ListVisitTypes 获取全部启用的来访目的（公开接口）
func (h *AppointmentHandler) ListVisitTypes(c *gin.Context) {
	var types []model.VisitType
	h.db.Where("status = 1").Order("sort ASC").Find(&types)
	if types == nil {
		types = []model.VisitType{}
	}
	response.Success(c, types)
}

// ListVisitorAreas 获取全部启用的到访区域（公开接口）
func (h *AppointmentHandler) ListVisitorAreas(c *gin.Context) {
	var areas []model.VisitorArea
	h.db.Where("status = 1").Order("sort ASC").Find(&areas)
	if areas == nil {
		areas = []model.VisitorArea{}
	}
	response.Success(c, areas)
}

// formatVisitTimeRange 格式化访问时间段：06月20日08:00 ~ 23:59
func formatVisitTimeRange(a *model.Appointment) string {
	if a.VisitStartTime.IsZero() {
		return ""
	}
	return a.VisitStartTime.Format("01-02 15:04") + "-" + a.VisitEndTime.Format("15:04")
}

// formatVisitTimeShort 格式化简短时间段：01-02 15:04 ~ 15:04
func formatVisitTimeShort(a *model.Appointment) string {
	if a.VisitStartTime.IsZero() {
		return ""
	}
	start := a.VisitStartTime.Format("01-02 15:04")
	endTime := a.VisitEndTime.Format("15:04")
	return fmt.Sprintf("%s ~ %s", start, endTime)
}

// RegisterRoutes 注册预约路由
func (h *AppointmentHandler) RegisterRoutes(r *gin.RouterGroup) {
	appt := r.Group("/appointment")
	{
		// 员工搜索 & 验证
		appt.POST("/employees/search", h.SearchEmployees)
		appt.POST("/employees/verify", h.VerifyEmployee)

		// 访客历史
		appt.GET("/visitors/my", h.GetMyVisitors)
		appt.POST("/visitor-templates", h.SaveVisitorTemplate)
		appt.DELETE("/visitor-templates/:id", h.DeleteVisitorTemplate)

		// 物品模板管理
		appt.GET("/goods-templates", h.ListGoodsTemplates)
		appt.POST("/goods-templates", h.CreateGoodsTemplate)
		appt.PUT("/goods-templates/:id", h.UpdateGoodsTemplate)
		appt.DELETE("/goods-templates/:id", h.DeleteGoodsTemplate)

		// 车辆信息管理
		appt.GET("/vehicles", h.ListVehicleInfos)
		appt.POST("/vehicles", h.CreateVehicleInfo)
		appt.DELETE("/vehicles/:id", h.DeleteVehicleInfo)
		appt.PUT("/vehicles/:id", h.UpdateVehicleInfo)

		// 预约管理
		appt.POST("/appointments", h.CreateAppointment)
		appt.GET("/appointments/my", h.GetMyAppointments)
		appt.GET("/appointments/all", h.GetAllAppointments)
		appt.GET("/appointments/:id", h.GetAppointmentDetail)

		// 邀请管理
		appt.POST("/invitations", h.CreateInvitation)
		appt.GET("/invitations/my", h.GetMyInvitations)
	}

	// 公开接口（无需认证）
	public := r.Group("/public")
	{
		public.GET("/invitations/:code", h.GetPublicInvitation)
		public.GET("/visit-types", h.ListVisitTypes)
		public.GET("/visitor-areas", h.ListVisitorAreas)
	}

	// 保安接口
	guard := r.Group("/guard")
	{
		guard.GET("/today-appointments", h.GetTodayAppointments)
		guard.POST("/check-in", h.CheckIn)
		guard.POST("/check-out", h.CheckOut)
		guard.GET("/records", h.GetVisitRecords)
	}

	// 领导接口
	leader := r.Group("/leader")
	{
		leader.GET("/appointments", h.GetLeaderAppointments)
	}
}
