package oa

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"github.com/fonchain_enterprise/fonchain-main/api/account"
	"github.com/fonchain_enterprise/fonchain-main/api/rule"
	"github.com/fonchain_enterprise/fonchain-main/pkg/config"
	"github.com/fonchain_enterprise/fonchain-main/pkg/model"
	account2 "github.com/fonchain_enterprise/fonchain-main/pkg/service/account"
	"github.com/fonchain_enterprise/fonchain-main/pkg/service/oa_new/common"
	"github.com/fonchain_enterprise/utils/objstorage"
	uuid "github.com/satori/go.uuid"
	"io"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/fonchain_enterprise/fonchain-main/api/oa"
	"github.com/fonchain_enterprise/fonchain-main/pkg/e"
	"github.com/fonchain_enterprise/fonchain-main/pkg/logic/oa_logic"
	"github.com/fonchain_enterprise/fonchain-main/pkg/model/login"
	"github.com/fonchain_enterprise/fonchain-main/pkg/model/oa_model"
	"github.com/fonchain_enterprise/fonchain-main/pkg/serializer"
	"github.com/fonchain_enterprise/fonchain-main/pkg/service"
	"github.com/fonchain_enterprise/fonchain-main/pkg/utils/holiday"

	"dubbo.apache.org/dubbo-go/v3/common/logger"
	"github.com/gin-gonic/gin"
	"github.com/jinzhu/copier"
)

// ClockIn
// 打卡 (包含 正常打卡 外勤打卡  迟到打卡  早退打卡)
func ClockIn(c *gin.Context) {
	req := oa_model.ClockInReq{}
	if err := c.ShouldBind(&req); err != nil {
		logger.Errorf("ClockIn ShouldBind err", err)
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	/*//检查打卡 是否合规  前端 操作
	ip := c.ClientIP()
	if ip == "" {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    e.ERR_GETIP,
			Status: e.Failed,
		})
		return
	}

	isWifi, err := service.CheckWifi(ip)
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	if !isWifi {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    e.ERR_WIFI_IS_NOT_IN,
			Data:   ip,
			Status: e.Failed,
		})
		return
	}*/

	//  经纬度限制
	if (req.Latitude != "" && req.Longitude != "") && req.CheckAddress == "" {
		in, err := service.ConfirmIsInCompany(req.Longitude, req.Latitude)
		if err != nil {
			service.ResponseMsg(c, e.SUCCESS, serializer.Response{
				Msg:    err.Error(),
				Status: e.Failed,
			})
			return
		}
		if !in {
			service.ResponseMsg(c, e.SUCCESS, serializer.Response{
				Msg:    e.ErrNotInChickInMap,
				Status: e.Failed,
			})
			return
		}
	}

	oaProfile := new(oa.OAProfile)

	err := copier.CopyWithOption(&oaProfile, req, copier.Option{DeepCopy: true})
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	// 组装 考勤班次信息
	userInfo := login.GetUserInfoFromC(c)
	departmentUUIDs := make([]string, 0)
	//for i := 0; i < len(userInfo.PositionUsers); i++ {
	//	departmentUUIDs = append(departmentUUIDs, strconv.FormatUint(userInfo.PositionUsers[i].DepartmentId, 10))
	//}
	//listByDidReq := new(position.ListByDidRequest)
	//listByDidReq.UserId = userInfo.ID
	//listByDidRes, err := service.PositionProvider.ListByDid(context.Background(), listByDidReq)
	//if err != nil {
	//	service.ResponseMsg(c, e.SUCCESS, serializer.Response{
	//		Msg:    err.Error(),
	//		Status: e.Ok,
	//	})
	//	return
	//}
	queryTimeOptions := make([]oa_model.QueryTimeOption, 0)
	for i := 0; i < len(userInfo.PositionUsers); i++ {
		departmentUUIDs = append(departmentUUIDs, strconv.FormatUint(userInfo.PositionUsers[i].DepartmentId, 10))
		queryTimeOption := oa_model.QueryTimeOption{
			PositionUID:   strconv.FormatUint(userInfo.PositionUsers[i].PositionID, 10),
			DepartmentUID: strconv.FormatUint(userInfo.PositionUsers[i].DepartmentId, 10),
		}
		queryTimeOptions = append(queryTimeOptions, queryTimeOption)
	}

	nowTime := time.Now()

	oaProfile.StaffUID = userInfo.ID
	oaProfile.Domain = userInfo.Domain
	oaProfile.StaffName = userInfo.NickName
	oaProfile.DepartmentUID = strings.Join(departmentUUIDs, ",")
	oaProfile.Week = oa_model.Week[time.Now().Weekday().String()]
	oaProfile.WorkDate = nowTime.Format("2006-01-02")
	oaProfile.ActionTime = nowTime.Format("2006-01-02 15:04")
	oaProfile.IsWork = oa_model.IsFree

	timesOptionBest, err := service.WorkingTimeAndWeekBest(0, queryTimeOptions)
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	// 将 考勤班次 记录到 打卡 记录中
	err = copier.CopyWithOption(&oaProfile.Time, timesOptionBest.Times, copier.Option{DeepCopy: true})
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}
	//oaProfile.Time = times

	// 记录 打卡 周期
	isWork := service.ConfirmIsWork(timesOptionBest.Week, oaProfile.WorkDate)
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    e.ErrConfirmweek,
			Status: e.Failed,
		})
		return
	}
	// 工作日 打卡
	commonRes := new(oa.CommonRes)

	if isWork {
		oaProfile.IsWork = oa_model.IsWork
		// 计算 考勤时间点
		oaProfile.ActionType, oaProfile.WorkTime, oaProfile.OnOff, err = service.ConfirmActionTypeWithOaRecord(userInfo, nowTime, timesOptionBest.Times)
		if oaProfile.ActionType == "" || oaProfile.WorkTime == "" || err != nil {
			service.ResponseMsg(c, e.SUCCESS, serializer.Response{
				Msg:    e.ErrConfirmactiontype,
				Status: e.Failed,
			})
			return
		}

		fmt.Println("===================================================")
		fmt.Println(oaProfile)
		fmt.Println("===================================================")

		commonRes, err = service.GrpcOAImpl.CreateOARecordWithMiss(context.Background(), oaProfile)
	} else {
		// 休息日打卡  考勤类型 均为  正常打卡  workTime  则记为 当前打卡时间
		oaProfile.ActionType, oaProfile.WorkTime, oaProfile.IsWork = oa_model.TypeCommon, strings.Split(oaProfile.ActionTime, " ")[1], oa_model.IsFree
		oaProfile.OnOff, err = service.OneRoundConfirmCurrentClickInTime(userInfo)
		if err != nil {
			service.ResponseMsg(c, e.SUCCESS, serializer.Response{
				Msg:    err.Error(),
				Status: e.Failed,
			})
			return
		}
		if oaProfile.OnOff == "" {
			oaProfile.OnOff = oa_model.OnWork
		}
		fmt.Println("===================================================")
		fmt.Println(oaProfile)
		fmt.Println("===================================================")

		commonRes, err = service.GrpcOAImpl.UseFaceCreateOARecord(context.Background(), oaProfile)
	}

	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	service.ResponseMsg(c, e.SUCCESS, serializer.Response{
		Msg:    commonRes.Msg,
		Status: e.Ok,
	})

}

func Stranger(c *gin.Context) {
	var data oa_model.ClockInUseFace
	if err := c.ShouldBind(&data); err != nil {
		logger.Errorf("ClockIn ShouldBind err", err)
		c.JSON(200, serializer.UseFaceClockIn{
			Result: 0,
			Msg:    err.Error(),
		})
		return
	}
	logger.Errorf("打卡失败记录==========> %+v", data)
	if data.Logs[0].UserID == "F1000001" {
		if data.Logs[0].Photo == "" {
			c.JSON(200, serializer.UseFaceClockIn{
				Result: 0,
				Msg:    e.GetMsg(e.ErrorGetUserInfo),
			})
			return
		}
		Url, err := Base64ToUrl(data.Logs[0].Photo)
		if err != nil {
			c.JSON(200, serializer.UseFaceClockIn{
				Result: 0,
				Msg:    e.GetMsg(e.ErrorGetUserInfo),
			})
			return
		}
		log := account.ClockLogInfo{
			Sn:           data.Sn,
			UserId:       data.Logs[0].UserID,
			RecogType:    data.Logs[0].RecogType,
			RecogTime:    data.Logs[0].RecogTime,
			Gender:       float32(data.Logs[0].Gender),
			Photo:        Url,
			PassStatus:   float32(data.Logs[0].PassStatus),
			UserName:     data.Logs[0].UserName,
			UserType:     float32(data.Logs[0].UserType),
			Confidence:   data.Logs[0].Confidence,
			Reflectivity: float32(data.Logs[0].Reflectivity),
			CardNumber:   data.Logs[0].CardNumber,
			PassWord:     data.Logs[0].Password,
			QrCode:       data.Logs[0].QrCode,
		}
		_, err = service.AccountProvider.CreateClockLog(c, &log)
		if err != nil {
			c.JSON(200, serializer.UseFaceClockIn{
				Result: 0,
				Msg:    e.GetMsg(e.ErrorGetUserInfo),
			})
			return
		}
	}
	c.JSON(200, serializer.UseFaceClockIn{
		Result: 0,
		Msg:    data.Logs[0].CardNumber,
	})
	return
}
func CallPhone3(c *gin.Context) {
	var requestData map[string]interface{}

	decoder := json.NewDecoder(c.Request.Body)
	if err := decoder.Decode(&requestData); err != nil {
		// 如果解码JSON数据时出错,返回错误
		c.JSON(200, serializer.UseFaceClockIn{
			Result: 0,
			Msg:    e.GetMsg(e.ErrorGetUserInfo),
		})
		return
	}

	// requestData 现在包含了请求的 JSON 数据,是一个 map[string]interface{}

	// 在这里执行其他操作
	// 例如:输出 requestData
	fmt.Printf("Received JSON data: %v\n", requestData)

	// 响应客户端
	c.JSON(200, serializer.UseFaceClockIn{
		Result: 0,
		Msg:    e.GetMsg(e.ErrorGetUserInfo),
	})
	return
}
func CallPhone2(c *gin.Context) {
	var requestData map[string]interface{}

	decoder := json.NewDecoder(c.Request.Body)
	if err := decoder.Decode(&requestData); err != nil {
		// 如果解码JSON数据时出错,返回错误
		c.JSON(200, serializer.UseFaceClockIn{
			Result: 0,
			Msg:    e.GetMsg(e.ErrorGetUserInfo),
		})
		return
	}

	// requestData 现在包含了请求的 JSON 数据,是一个 map[string]interface{}

	// 在这里执行其他操作
	// 例如:输出 requestData
	fmt.Printf("Received JSON data: %v\n", requestData)

	// 响应客户端
	c.JSON(200, serializer.UseFaceClockIn{
		Result: 0,
		Msg:    e.GetMsg(e.ErrorGetUserInfo),
	})
	return
}
func ClockFaceLog(c *gin.Context) {
	req := oa_model.ClockInUseFace{}
	if err := c.ShouldBind(&req); err != nil {
		logger.Errorf("ClockIn ShouldBind err", err)
		c.JSON(200, serializer.UseFaceClockIn{
			Result: 0,
			Msg:    e.GetMsg(e.ErrorGetUserInfo),
		})
		return
	}
	//体制外
	isOut := strings.Contains(req.Logs[0].UserID, "out")
	if config.IsOut == false && config.Env == "prod" && isOut {
		if strings.HasSuffix(req.Logs[0].UserID, "out") {
			req.Logs[0].UserID = strings.TrimSuffix(req.Logs[0].UserID, "out")
			fmt.Println("user_id 的 'out' 后缀已删除,新的 user_id 为:", req.Logs[0].UserID)
		}
		// 将请求转发到另一个 URL
		err := forwardRequest(c, req)
		if err != nil {
			logger.Errorf("转发请求失败: %v", err)
			c.JSON(200, serializer.UseFaceClockIn{
				Result: 0,
				Msg:    "请求转发失败",
			})
			return
		}
		c.JSON(200, serializer.UseFaceClockIn{
			Result: 0,
			Msg:    e.GetMsg(e.ErrorGetUserInfo),
		})
		return
	}
	var Url string
	var err error

	for i := 0; i < len(req.Logs); i++ {
		Url, err = Base64ToUrl(req.Logs[i].Photo)
		logger.Errorf("打卡人脸照片url==========> %+v", Url)
		if err != nil {
			c.JSON(200, serializer.UseFaceClockIn{
				Result: 0,
				Msg:    e.GetMsg(e.ErrorGetUserInfo),
			})
			return
		}
		req.Logs[i].Photo = Url
	}

	logger.Errorf("打卡成功记录==========> %+v", req)
	err = ClockInUseFace(req)
	if err != nil {
		c.JSON(200, serializer.UseFaceClockIn{
			Result: 0,
			Msg:    e.GetMsg(e.ErrorGetUserInfo),
		})
		return
	}
	if len(req.Logs) == 0 {
		c.JSON(200, serializer.UseFaceClockIn{
			Result: 0,
			Msg:    e.GetMsg(e.ErrorGetUserInfo),
		})
		return
	}
	for _, i := range req.Logs {
		if i.Photo == "" {
			c.JSON(200, serializer.UseFaceClockIn{
				Result: 0,
				Msg:    e.GetMsg(e.ErrorGetUserInfo),
			})
			return
		}
		//Url, err := Base64ToUrl(i.Photo)
		//logger.Errorf("打卡人脸照片url==========> %+v", Url)
		//if err != nil {
		//	c.JSON(200, serializer.UseFaceClockIn{
		//		Result: 0,
		//		Msg:    e.GetMsg(e.ErrorGetUserInfo),
		//	})
		//	return
		//}
		log := &account.ClockLogInfo{
			Sn:           req.Sn,
			UserId:       i.UserID,
			RecogType:    i.RecogType,
			RecogTime:    i.RecogTime,
			Gender:       float32(i.Gender),
			Photo:        Url,
			PassStatus:   float32(i.PassStatus),
			UserName:     i.UserName,
			UserType:     float32(i.UserType),
			Confidence:   i.Confidence,
			Reflectivity: float32(i.Reflectivity),
			CardNumber:   i.CardNumber,
			PassWord:     i.Password,
			QrCode:       i.QrCode,
		}
		logger.Errorf("打卡人脸纪录log==========> %+v", log)
		logRes, err := service.AccountProvider.CreateClockLog(c, log)
		if err != nil {
			c.JSON(200, serializer.UseFaceClockIn{
				Result: 0,
				Msg:    e.GetMsg(e.ErrorGetUserInfo),
			})
		}
		logger.Errorf("打卡机日志创建成功==========> %+v", logRes)
	}
	c.JSON(200, serializer.UseFaceClockIn{
		Result: 0,
		Msg:    e.GetMsg(e.ErrorGetUserInfo),
	})
	return
}
func forwardRequest(c *gin.Context, req oa_model.ClockInUseFace) error {
	client := &http.Client{}

	// 将请求对象序列化为 JSON
	jsonData, err := json.Marshal(req)
	if err != nil {
		return fmt.Errorf("序列化请求数据失败: %v", err)
	}

	// 创建一个新的 POST 请求,URL 替换为你想转发的目标 URL
	targetURL := config.OutUrl + "/api/v1/record/face" // 替换为实际的转发地址
	reqBody := bytes.NewBuffer(jsonData)
	newReq, err := http.NewRequest("POST", targetURL, reqBody)
	if err != nil {
		return fmt.Errorf("创建转发请求失败: %v", err)
	}

	// 设置请求头部,与原请求保持一致
	newReq.Header.Set("Content-Type", "application/json")
	newReq.Header.Set("Authorization", c.Request.Header.Get("Authorization")) // 如果有需要转发的头部信息

	// 发送请求
	resp, err := client.Do(newReq)
	if err != nil {
		return fmt.Errorf("发送转发请求失败: %v", err)
	}
	defer resp.Body.Close()

	// 检查响应状态
	if resp.StatusCode != http.StatusOK {
		body, _ := io.ReadAll(resp.Body)
		return fmt.Errorf("转发请求返回错误: %v - %s", resp.Status, string(body))
	}

	return nil
}
func Base64ToUrl(req string) (string, error) {
	uuids, _ := uuid.NewV4()
	var objectName = fmt.Sprintf("%s/%s/%s.jpeg", config.ConfigData.Oss.BaseDir, config.Env, uuids)
	BOSClient, _ := objstorage.NewOSS(config.ConfigData.Oss.AccessKeyId, config.ConfigData.Oss.AccessKeySecret, config.ConfigData.Oss.Endpoint)
	decodedData, err := url.QueryUnescape(req)
	index := strings.Index(decodedData, ",")
	if index == -1 {
		return "", errors.New("图片格式错误")
	}
	imageBytes, err := base64.StdEncoding.DecodeString(decodedData[index+1:])
	_, err = BOSClient.PutObjectFromBytes(config.ConfigData.Oss.BucketName, objectName, imageBytes)
	Url := fmt.Sprintf("%s/%s", config.ConfigData.Oss.CdnHost, objectName)
	return Url, err
}

// ClockInUseFace
// 打卡机打卡 ( 工作期间不做处理,  周末 加班 只记录 一个完整的周期 <上班卡只记录 第一次   下班卡 时间点做 更新>)
func ClockInUseFace(req oa_model.ClockInUseFace) error {
	if req.Logs == nil {
		return errors.New(e.GetMsg(e.ErrNoID))
	}
	jsonString, _ := json.Marshal(req)
	//if err != nil {
	//	fmt.Println("JSON serialization error:", err)
	//	return
	//}

	// 将JSON字符串打印到控制台
	fmt.Println(string(jsonString))
	/* 检查打卡 是否合规  前端 操作*/

	oaProfile := new(oa.OAProfile)

	err := copier.CopyWithOption(&oaProfile, req, copier.Option{DeepCopy: true})
	if err != nil {
		//c.JSON(200, serializer.UseFaceClockIn{
		//	Result: 0,
		//	Msg:    err.Error(),
		//})
		return errors.New("考勤信息错误")
	}

	userIDStr := ""
	facePhoto := ""
	for i := 0; i < len(req.Logs); i++ {
		userIDStr = req.Logs[i].UserID
		facePhoto = req.Logs[i].Photo
	}

	if userIDStr == "" || userIDStr == "0" {
		return errors.New(e.GetMsg(e.ErrorGetUserInfo))
	}

	userID, _ := strconv.ParseUint(userIDStr, 0, 64)
	infoReq := account.InfoRequest{ID: userID}

	fmt.Println("======== infoReq  1  ==============")
	fmt.Printf("infoReq %+v\n", infoReq)

	infoRes, err := service.AccountProvider.Info(context.Background(), &infoReq)
	if err != nil {
		fmt.Println("======== AccountProvider.Info  2.1 ==============")
		fmt.Printf("err %+v\n", err)
		fmt.Printf("infoRes %+v\n", infoRes)
		return errors.New(e.GetMsg(e.ErrorGetUserInfo))
	}

	fmt.Println("======== userInfo  2 ==============")
	fmt.Printf("userInfo %+v\n", infoRes)

	if infoRes.Info.ID == 0 || infoRes.Info.NickName == "" || infoRes.Info.Domain == "" {
		return errors.New(e.GetMsg(e.ErrorGetUserInfo))
	}

	// 组装 考勤班次信息
	//userInfo := login.GetUserInfoFromC(c)
	departmentUUIDs := make([]string, 0)
	departmentAndPositionReq := new(rule.RulesRequest)
	departmentAndPositionReq.AccountID = infoRes.Info.ID
	departmentAndPositionRes, err := service.RuleProvider.UserInfo(context.Background(), departmentAndPositionReq)
	if err != nil {
		return err
	}

	userInfo := login.Info{
		ID:       infoRes.Info.ID,
		NickName: infoRes.Info.NickName,
		Domain:   infoRes.Info.Domain,
	}

	queryTimeOptions := make([]oa_model.QueryTimeOption, 0)
	for i := 0; i < len(departmentAndPositionRes.PositionUsers); i++ {
		departmentUUIDs = append(departmentUUIDs, strconv.FormatUint(departmentAndPositionRes.PositionUsers[i].DepartmentId, 10))
		queryTimeOption := oa_model.QueryTimeOption{
			PositionUID:   strconv.FormatUint(departmentAndPositionRes.PositionUsers[i].PositionID, 10),
			DepartmentUID: strconv.FormatUint(departmentAndPositionRes.PositionUsers[i].DepartmentId, 10),
		}
		queryTimeOptions = append(queryTimeOptions, queryTimeOption)
	}

	fmt.Println("======== queryTimeOptions  3 ==============")
	fmt.Printf("queryTimeOptions %+v\n", queryTimeOptions)

	var nowTime time.Time
	// 给默认值
	nowTime = time.Now()
	if len(req.Logs) == 1 {
		nowTime, _ = time.ParseInLocation("2006-01-02 15:04:05", req.Logs[0].RecogTime, time.Local)
	}

	oaProfile.StaffUID = userInfo.ID
	oaProfile.Domain = userInfo.Domain
	oaProfile.StaffName = userInfo.NickName
	oaProfile.DepartmentUID = strings.Join(departmentUUIDs, ",")
	oaProfile.Week = oa_model.Week[time.Now().Weekday().String()]
	oaProfile.WorkDate = nowTime.Format("2006-01-02")
	oaProfile.ActionTime = nowTime.Format("2006-01-02 15:04")
	oaProfile.IsWork = oa_model.IsFree
	oaProfile.FacePhoto = facePhoto
	oaProfile.CheckAddress = req.Sn
	oaProfile.CheckMethod = oa_model.CheckMethodPunchClock

	timesOptionBest, err := service.WorkingTimeAndWeekBest(0, queryTimeOptions)
	if err != nil {
		return err
	}

	fmt.Println("======== times  4 ==============")
	fmt.Printf("times %+v\n", timesOptionBest)

	// 将 考勤班次 记录到 打卡 记录中
	err = copier.CopyWithOption(&oaProfile.Time, timesOptionBest.Times, copier.Option{DeepCopy: true})
	if err != nil {
		return errors.New("考勤信息错误")
	}

	// 记录 打卡 周期
	isWork := service.ConfirmIsWork(timesOptionBest.Week, oaProfile.WorkDate)
	if err != nil {
		return errors.New(e.ErrConfirmweek)
	}

	fmt.Println("======== oaProfile  5 ==============")
	fmt.Printf("oaProfile %+v\n", oaProfile)

	// 工作日 打卡
	commonRes := new(oa.CommonRes)

	if isWork {
		oaProfile.IsWork = oa_model.IsWork
		// 计算 考勤时间点
		oaProfile.ActionType, oaProfile.WorkTime, oaProfile.OnOff, err = service.ConfirmActionTypeWithOaRecord(userInfo, nowTime, timesOptionBest.Times)
		if oaProfile.ActionType == "" || oaProfile.WorkTime == "" || err != nil {
			return errors.New(e.ErrConfirmactiontype)
		}

		fmt.Println("===================================================")
		fmt.Println(oaProfile)
		fmt.Println("===================================================")

		if oaProfile.ActionType == oa_model.TypeCommon {
			commonRes, err = service.GrpcOAImpl.CreateOARecordWithMiss(context.Background(), oaProfile)
			logger.Errorf("模版消息---1")
			templateMsgReq := account.SendClockInWechatRequest{
				OperatedAt: nowTime.Format(model.DateTimeFormat),
				ClockType:  oaProfile.OnOff,
				UserId:     uint32(userID),
				GhId:       "gh_d838598b1a23",
				Address:    req.Sn,
			}

			go service.AccountProvider.SendClockInWechat(context.Background(), &templateMsgReq)

			fmt.Println("================模版发送完毕=============================")
		}

		commonRes, err = service.GrpcOAImpl.CreateOtherRecord(context.Background(), oaProfile)

	} else {
		// 休息日打卡  考勤类型 均为  正常打卡  workTime  则记为 当前打卡时间
		oaProfile.ActionType, oaProfile.WorkTime, oaProfile.IsWork = oa_model.TypeCommon, strings.Split(oaProfile.ActionTime, " ")[1], oa_model.IsFree
		oaProfile.OnOff, err = service.OneRoundConfirmCurrentClickInTime(userInfo)
		if err != nil {
			return err
		}
		if oaProfile.OnOff == "" {
			oaProfile.OnOff = oa_model.OnWork
		}
		fmt.Println("===================================================")
		fmt.Println(oaProfile)
		fmt.Println("===================================================")

		commonRes, err = service.GrpcOAImpl.UseFaceCreateOARecord(context.Background(), oaProfile)
		// 加 消息通知
	}
	_ = commonRes
	return err
}

func OARecord(c *gin.Context) {
	req := oa_model.OARecordReq{}
	if err := c.ShouldBind(&req); err != nil {
		logger.Errorf("OARecord ShouldBind err", err)
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	userInfo := login.GetUserInfoFromC(c)

	if req.StaffUID == 0 {

		req.StaffUID = userInfo.ID
	}

	microReq := new(oa.OARecordReq)

	err := copier.CopyWithOption(&microReq, req, copier.Option{DeepCopy: true})
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	microReq.Dates = holiday.SplitDates(req.StartDate, req.EndDate, "2006-01-02")

	microRes, err := service.GrpcOAImpl.OARecord(context.Background(), microReq)
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	departmentUUIDs := make([]string, 0)
	queryTimeOptions := make([]oa_model.QueryTimeOption, 0)
	for i := 0; i < len(userInfo.PositionUsers); i++ {
		departmentUUIDs = append(departmentUUIDs, strconv.FormatUint(userInfo.PositionUsers[i].DepartmentId, 10))
		queryTimeOption := oa_model.QueryTimeOption{
			PositionUID:   strconv.FormatUint(userInfo.PositionUsers[i].PositionID, 10),
			DepartmentUID: strconv.FormatUint(userInfo.PositionUsers[i].DepartmentId, 10),
		}
		queryTimeOptions = append(queryTimeOptions, queryTimeOption)
	}

	timesOptionBest, err := service.WorkingTimeAndWeekBest(0, queryTimeOptions)
	if err != nil {
		return
	}

	// 处理 打卡记录
	/*
	   除 正常打卡外  action_time 均为 空
	   除 正常、迟到、早退、缺卡  还需要 添加  请假
	*/

	oaRecordRes, err := MakeRecord(microRes, req.StaffUID, timesOptionBest.Week)
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	service.ResponseMsg(c, e.SUCCESS, serializer.Response{
		Msg:    e.GetMsg(e.SUCCESS),
		Data:   oaRecordRes,
		Status: e.Ok,
	})
}

// MakeRecord
// 查询 异常  及其他
func MakeRecord(microRecord *oa.OARecordRes, staffUID uint64, cycle string) (oaRecordRes *oa_model.OARecordRes, err error) {
	basicReq := new(oa.ApplyRecordReq)
	basicReq.StaffUID = staffUID
	basicReq.Page = 1
	basicReq.PageSize = 10
	basicReq.ApplyStatus = []int32{e.ApprovalWorkStatusOk}

	oaRecordRes = new(oa_model.OARecordRes)
	oaRecordRes.Records = make([]oa_model.OARecord, 0)

	currentDate, _ := time.ParseInLocation("2006-01-02 15:04:05", time.Now().Format("2006-01-02")+" 00:00:00", time.Local)

	var lastCycle string

	for i := 0; i < len(microRecord.Data); i++ {

		fmt.Println(" microRecord.Data[i].Cycle is == ", microRecord.Data[i].Cycle)
		fmt.Println(" microRecord.Data[i].WorkDate is == ", microRecord.Data[i].WorkDate)
		fmt.Println(" lastCycle is == ", lastCycle)

		// 如果 考勤 中的 打卡周期 不为空  则将 此次 打卡周期记录 为 之后 考勤记录为 空的 提供 判断依据
		if microRecord.Data[i].Cycle != "" {
			lastCycle = microRecord.Data[i].Cycle
		}

		if lastCycle == "" {
			lastCycle = cycle
		}

		// 全天考勤正常 与否
		oaRecord := oa_model.OARecord{}
		var isWork bool

		// 如果 考勤记录不为 空  则将 记录中的 IsWork 赋值给 统计中的 IsWork (不用)
		// 无论记录是否存在 都将 记录中的 IsWork 赋值给 统计中的 IsWork
		isWork = service.ConfirmIsWork(lastCycle, microRecord.Data[i].WorkDate)

		if isWork {
			oaRecord.IsWork = oa_model.IsWork
		} else {
			oaRecord.IsWork = int8(microRecord.Data[i].IsWork)
		}
		workDate, _ := time.ParseInLocation("2006-01-02 15:04:05", microRecord.Data[i].WorkDate+" 00:00:00", time.Local)
		if workDate.Before(currentDate) && len(microRecord.Data[i].Records) == 0 && oaRecord.IsWork == oa_model.IsWork {
			oaRecord.Abnormal = true
		}

		isRight := 0

		oaRecord.WorkDate = microRecord.Data[i].WorkDate
		for j := 0; j < len(microRecord.Data[i].Records); j++ {
			record := oa_model.Record{
				ActionType:   microRecord.Data[i].Records[j].ActionType,
				WorkTime:     microRecord.Data[i].Records[j].WorkTime,
				ActionTime:   microRecord.Data[i].Records[j].ActionTime,
				OnOff:        microRecord.Data[i].Records[j].OnOff,
				CheckAddress: microRecord.Data[i].Records[j].CheckAddress,
			}
			//if microRecord.Data[i].Records[j].ActionType != oa_model.TypeMiss {
			//	record.ActionTime = microRecord.Data[i].Records[j].ActionTime
			//}
			if microRecord.Data[i].Records[j].ActionType == oa_model.TypeCommon || microRecord.Data[i].Records[j].ActionType == oa_model.TypeOverTime || microRecord.Data[i].Records[j].ActionType == oa_model.TypeOutWork {
				isRight += 1
			} else {
				oaRecord.Abnormal = true
			}
			oaRecord.Records = append(oaRecord.Records, record)
		}

		if isRight == len(microRecord.Data[i].Records) && len(microRecord.Data[i].Records) != 0 && isRight != 0 {
			oaRecord.Right = true
		}

		fmt.Println(" oaRecord.IsWork ===  ", oaRecord.IsWork)
		if oaRecord.IsWork == oa_model.IsWork || len(oaRecord.Records) > 0 {

			basicReq.BeginTime = oaRecord.WorkDate
			basicReq.EndTime = oaRecord.WorkDate

			oaRecord.LeaveRecord, err = LeaveRecord(basicReq)
			if err != nil {
				return nil, err
			}
			if len(oaRecord.LeaveRecord) > 0 {
				oaRecord.Leave = true
			}

			oaRecord.MakeUpRecord, err = MakeUpRecord(basicReq)
			if err != nil {
				return nil, err
			}
			if len(oaRecord.MakeUpRecord) > 0 {
				oaRecord.Leave = true
			}

			oaRecord.OverTimeRecord, err = OverTimeRecord(basicReq)
			if err != nil {
				return nil, err
			}
			if len(oaRecord.OverTimeRecord) > 0 {
				oaRecord.Leave = true
			}

			oaRecord.DayOffRecord, err = DayOffRecord(basicReq)
			if err != nil {
				return nil, err
			}
			if len(oaRecord.DayOffRecord) > 0 {
				oaRecord.Leave = true
			}

			oaRecord.OutWorkRecord, err = OutWorkRecord(basicReq)
			if err != nil {
				return nil, err
			}
			if len(oaRecord.OutWorkRecord) > 0 {
				oaRecord.Leave = true
			}
		}

		oaRecordRes.Records = append(oaRecordRes.Records, oaRecord)
	}

	return
}

func OARecordSingle(c *gin.Context) {
	req := oa_model.OARecordSingleReq{}
	if err := c.ShouldBind(&req); err != nil {
		logger.Errorf("OARecord ShouldBind err", err)
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	userInfo := login.Info{}

	if req.StaffUID == 0 {
		userInfo = login.GetUserInfoFromC(c)
		req.StaffUID = userInfo.ID
	} else {
		res, err := account2.GetUserInfoById(c, req.StaffUID, "")
		if err != nil {
			service.ResponseMsg(c, e.SUCCESS, serializer.Response{
				Msg:    err.Error(),
				Status: e.Failed,
			})
			return
		}
		userInfo.ID = res.ID
		userInfo.PositionUsers = res.PositionUsers
		userInfo.NickName = res.NickName
	}

	// 获取 工作周期
	queryTimeOptions := make([]oa_model.QueryTimeOption, 0)
	for i := 0; i < len(userInfo.PositionUsers); i++ {
		queryTimeOption := oa_model.QueryTimeOption{
			PositionUID:   strconv.FormatUint(userInfo.PositionUsers[i].PositionID, 10),
			DepartmentUID: strconv.FormatUint(userInfo.PositionUsers[i].DepartmentId, 10),
		}
		queryTimeOptions = append(queryTimeOptions, queryTimeOption)
	}
	startDay, endDay, err := service.WorkingTimeWeekBest(queryTimeOptions)
	if err != nil {
		fmt.Println("WorkingTimeWeekBest err is :", err.Error())
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    "获取工作周期失败",
			Status: e.Failed,
		})
		return
	}

	isWork := service.ConfirmIsWork(strings.Join([]string{strconv.Itoa(startDay), strconv.Itoa(endDay)}, ","), req.WorkDate)

	microReq := new(oa.OARecordReq)

	microReq.StaffUID = req.StaffUID
	microReq.Dates = append(microReq.Dates, req.WorkDate)

	//copier.CopyWithOption(&microReq, req, copier.Option{DeepCopy: true})

	oaRecordSingleRes := new(oa_model.OARecord)

	fmt.Println("===================================================")
	fmt.Printf("oaRecordSingleRes ==== %+v\n", oaRecordSingleRes)
	fmt.Println("oaRecordSingleRes isWork ==== ", isWork)
	fmt.Println("===================================================")

	mircoRes, err := service.GrpcOAImpl.OARecord(context.Background(), microReq)
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	fmt.Println("打卡记录 ======================================")
	fmt.Printf("%+v\n", mircoRes)
	fmt.Println("打卡记录 ======================================")

	oaRecordSingleRes.Records = make([]oa_model.Record, 0)
	for i := 0; i < len(mircoRes.Data); i++ {
		oaRecordSingleRes.WorkDate = mircoRes.Data[i].WorkDate
		if mircoRes.Data[i].Records == nil && mircoRes.Data[i].IsWork == oa_model.IsWork {
			oaRecordSingleRes.Abnormal = true
			oaRecordSingleRes.Right = false
		}
		// 将 记录中的 IsWork 赋值给 统计中的 IsWork
		oaRecordSingleRes.IsWork = int8(mircoRes.Data[i].IsWork)

		currentMap := make(map[string]*oa_model.Record, 0) // 剔除重复考勤记录数据
		for j := 0; j < len(mircoRes.Data[i].Records); j++ {
			if mircoRes.Data[i].Records[j].ActionType == oa_model.TypeMiss || mircoRes.Data[i].Records[j].ActionType == oa_model.TypeLate || mircoRes.Data[i].Records[j].ActionType == oa_model.TypeBefore {
				oaRecordSingleRes.Abnormal = true
				oaRecordSingleRes.Right = false
			}
			//if currentMap[mircoRes.Data[i].Records[j].WorkTime] != "" {
			//	continue
			//}
			record := new(oa_model.Record)
			err = copier.CopyWithOption(&record, mircoRes.Data[i].Records[j], copier.Option{DeepCopy: true})
			record.IsWork = mircoRes.Data[i].IsWork
			if err != nil {
				return
			}
			if mircoRes.Data[i].IsWork == oa_model.IsWork {
				if _, ok := currentMap[mircoRes.Data[i].Records[j].WorkTime]; ok {
					oaRecordSingleRes.Records = oaRecordSingleRes.Records[:len(oaRecordSingleRes.Records)-1]
				}
				currentMap[mircoRes.Data[i].Records[j].WorkTime] = record
			}
			oaRecordSingleRes.Records = append(oaRecordSingleRes.Records, *record)
		}
	}

	if isWork {
		oaRecordSingleRes.IsWork = oa_model.IsWork
	}

	//if len(mircoRes.Data) > 0 || mircoRes.Data[len(mircoRes.Data)-len(mircoRes.Data)].IsWork == oa_model.IsWork || isWeek {
	if len(mircoRes.Data) > 0 || isWork {
		// 处理 打卡记录
		/*
		   除 正常打卡外  action_time 均为 空
		   除 正常、迟到、早退、缺卡  还需要 添加  请假
		*/

		basicReq := new(oa.ApplyRecordReq)
		basicReq.StaffUID = req.StaffUID
		basicReq.Page = 1
		basicReq.PageSize = 9999
		basicReq.ApplyStatus = []int32{e.ApprovalWorkStatusOk}

		basicReq.BeginTime = req.WorkDate
		basicReq.EndTime = req.WorkDate

		oaRecordSingleRes.LeaveRecord, err = LeaveRecord(basicReq)
		if err != nil {
			service.ResponseMsg(c, e.SUCCESS, serializer.Response{
				Msg:    err.Error(),
				Status: e.Failed,
			})
			return
		}
		oaRecordSingleRes.MakeUpRecord, err = MakeUpRecord(basicReq)
		if err != nil {
			service.ResponseMsg(c, e.SUCCESS, serializer.Response{
				Msg:    err.Error(),
				Status: e.Failed,
			})
			return
		}
		oaRecordSingleRes.OverTimeRecord, err = OverTimeRecord(basicReq)
		if err != nil {
			service.ResponseMsg(c, e.SUCCESS, serializer.Response{
				Msg:    err.Error(),
				Status: e.Failed,
			})
			return
		}
		oaRecordSingleRes.DayOffRecord, err = DayOffRecord(basicReq)
		if err != nil {
			service.ResponseMsg(c, e.SUCCESS, serializer.Response{
				Msg:    err.Error(),
				Status: e.Failed,
			})
			return
		}
		oaRecordSingleRes.OutWorkRecord, err = OutWorkRecord(basicReq)
		if err != nil {
			service.ResponseMsg(c, e.SUCCESS, serializer.Response{
				Msg:    err.Error(),
				Status: e.Failed,
			})
			return
		}

		if oaRecordSingleRes.OutWorkRecord != nil {
			for i := 0; i < len(oaRecordSingleRes.OutWorkRecord); i++ {
				outWorkCheckReq := new(oa.OutWorkCheckReq)
				outWorkCheckReq.StaffUID = oaRecordSingleRes.OutWorkRecord[i].StaffUID
				outWorkCheckReq.ApplyUUID = oaRecordSingleRes.OutWorkRecord[i].UUID
				outWorkCheckRes, err := service.GrpcOAImpl.QueryCheckRecord(context.Background(), outWorkCheckReq)
				if err != nil {
					service.ResponseMsg(c, e.SUCCESS, serializer.Response{
						Msg:    err.Error(),
						Status: e.Failed,
					})
					return
				}
				if outWorkCheckRes.Data != nil {
					for j := 0; j < len(outWorkCheckRes.Data); j++ {
						oaRecordSingleRes.Records = append(oaRecordSingleRes.Records, oa_model.Record{
							OnOff:      outWorkCheckRes.Data[j].ActionType,
							ActionTime: outWorkCheckRes.Data[j].ActionTime,
						})
					}
				}
			}
		}
	}

	oaRecordSingleRes.Records = service.OrderByRecord(oaRecordSingleRes.Records)

	for i := 0; i < len(oaRecordSingleRes.Records)-1; i++ {
		curr := oaRecordSingleRes.Records[i]
		next := oaRecordSingleRes.Records[i+1]
		currDate := ""
		nextDate := ""

		if curr.ActionTime != "" {
			currDate = strings.Split(curr.ActionTime, " ")[0]
		}
		if next.ActionTime != "" {
			nextDate = strings.Split(next.ActionTime, " ")[0]
		}

		if currDate != "" && nextDate != "" && currDate != nextDate {
			if curr.ActionTime > next.ActionTime {
				oaRecordSingleRes.Records[i], oaRecordSingleRes.Records[i+1] = oaRecordSingleRes.Records[i+1], oaRecordSingleRes.Records[i]
				i = -1
			}
		}
	}
	service.ResponseMsg(c, e.SUCCESS, serializer.Response{
		Msg:    e.GetMsg(e.SUCCESS),
		Data:   oaRecordSingleRes,
		Status: e.Ok,
	})
}

func MakeRecordSingle(microRecord *oa.OARecord, staffUID uint64) (oaRecord *oa_model.OARecord, err error) {
	basicReq := oa.ApplyRecordReq{}
	basicReq.StaffUID = staffUID
	basicReq.Page = 1
	basicReq.PageSize = 10
	basicReq.ApplyStatus = []int32{1, 2, 3}

	//oaRecordRes = new(oa_model.OARecord)
	//for i := 0; i < len(microRecord.Data); i++ {

	// 全天考勤正常 与否
	oaRecord = new(oa_model.OARecord)
	oaRecord.WorkDate = "2023-05-31"

	oaRecord.Records = append(oaRecord.Records, oa_model.Record{
		ActionType: "common",
		WorkTime:   "09:00",
	})

	oaRecord.Records = append(oaRecord.Records, oa_model.Record{
		ActionType: oa_model.TypeLeave,
		WorkTime:   "10:00",
	})

	oaRecord.Records = append(oaRecord.Records, oa_model.Record{
		ActionType: oa_model.TypeLeave,
		WorkTime:   "11:00",
	})

	oaRecord.Records = append(oaRecord.Records, oa_model.Record{
		ActionType: "common",
		WorkTime:   "12:00",
	})

	oaRecord.Records = append(oaRecord.Records, oa_model.Record{
		ActionType: "common",
		WorkTime:   "13:00",
	})

	//for j := 0; j < len(microRecord.Records); j++ {
	//	record := oa_model.Record{
	//		ActionType: microRecord.Records[j].ActionType,
	//		WorkTime:   microRecord.Records[j].WorkTime,
	//	}
	//	if microRecord.Records[j].ActionType != oa_model.TypeMiss {
	//		record.ActionTime = microRecord.Records[j].ActionTime
	//	}
	//	oaRecord.Records = append(oaRecord.Records, record)
	//	if record.WorkTime == "09:00" {
	//		oaRecord.Records = append(oaRecord.Records, oa_model.Record{
	//			ActionType: oa_model.TypeLeave,
	//			WorkTime:   "10:00",
	//		})
	//
	//		oaRecord.Records = append(oaRecord.Records, oa_model.Record{
	//			ActionType: oa_model.TypeLeave,
	//			WorkTime:   "11:00",
	//		})
	//	}
	//}

	//req := basicReq
	//req.ApplyStartTime = oaRecord.WorkDate + " 00:00:00"
	//req.ApplyEndTime = oaRecord.WorkDate + " 23:59:59"
	//req.ApplyType = []string{
	//	oa_model.TypeLeave,
	//	oa_model.TypeSick,
	//	oa_model.TypeDayOff,
	//	oa_model.TypeAnnualLeave,
	//	oa_model.TypeMaritalLeave,
	//	oa_model.TypeMatingCheckLeave,
	//	oa_model.TypeMaternityLeave,
	//	oa_model.TypePaternityLeave,
	//	oa_model.TypeParentalLeave,
	//	oa_model.TypeNursingLeave,
	//	oa_model.TypeFuneralLeave,
	//	oa_model.TypeMakeUp,
	//	oa_model.TypeOverTime,
	//	oa_model.TypeOutwork,
	//}
	//applyInfo := new(oa.ApplyRecordRes)
	//applyInfo, err = service.GrpcOAImpl.QueryOaApply(context.Background(), &req)
	//if err != nil {
	//	return nil, err
	//}
	//if applyInfo.Total > 0 {
	//	oaRecord.Leave = true
	//} else {
	//	oaRecord.Leave = false
	//}
	//oaRecordRes.Records = append(oaRecordRes.Records, oaRecord)
	//}

	return
}

func LeaveRecord(basicReq *oa.ApplyRecordReq) (oaApplys []oa_model.OaApply, err error) {

	leaveReq := new(oa.ApplyRecordReq)
	err = copier.CopyWithOption(&leaveReq, basicReq, copier.Option{DeepCopy: true})
	if err != nil {
		return nil, err
	}

	leaveReq.ApplyType = []string{
		oa_model.TypeLeave, // 请假
		oa_model.TypeSick,
		oa_model.TypeAnnualLeave,
		oa_model.TypeMaritalLeave,
		oa_model.TypeMatingCheckLeave,
		oa_model.TypeMaternityLeave,
		oa_model.TypePaternityLeave,
		oa_model.TypeParentalLeave,
		oa_model.TypeNursingLeave,
		oa_model.TypeFuneralLeave,
		oa_model.TypeAbortLeave,
		oa_model.TypeBreastFeedingLeave,
	}
	leaveApplyInfo := new(oa.ApplyRecordRes)
	leaveApplyInfo, err = service.GrpcOAImpl.QueryOaApply(context.Background(), leaveReq)
	if err != nil {
		return nil, err
	}

	oaApplys = make([]oa_model.OaApply, 0)

	err = copier.CopyWithOption(&oaApplys, leaveApplyInfo.Data, copier.Option{DeepCopy: true})
	if err != nil {
		return nil, err
	}

	fmt.Println("请假记录 ======================================")
	fmt.Printf("%+v\n", oaApplys)
	fmt.Println("请假记录 ======================================")

	return
}

func MakeUpRecord(basicReq *oa.ApplyRecordReq) (oaApplys []oa_model.OaApply, err error) {

	makeUpReq := new(oa.ApplyRecordReq)
	err = copier.CopyWithOption(&makeUpReq, basicReq, copier.Option{DeepCopy: true})
	if err != nil {
		return nil, err
	}

	makeUpReq.ApplyType = []string{
		oa_model.TypeMakeUp, // 补卡
	}
	makeUpApplyInfo := new(oa.ApplyRecordRes)
	makeUpApplyInfo, err = service.GrpcOAImpl.QueryOaApply(context.Background(), makeUpReq)
	if err != nil {
		return nil, err
	}

	oaApplys = make([]oa_model.OaApply, 0)

	err = copier.CopyWithOption(&oaApplys, makeUpApplyInfo.Data, copier.Option{DeepCopy: true})
	if err != nil {
		return nil, err
	}

	fmt.Println("补卡记录 ======================================")
	fmt.Printf("%+v\n", oaApplys)
	fmt.Println("补卡记录 ======================================")

	return
}

func OverTimeRecord(basicReq *oa.ApplyRecordReq) (oaApplys []oa_model.OaApply, err error) {

	overTimeReq := new(oa.ApplyRecordReq)
	err = copier.CopyWithOption(&overTimeReq, basicReq, copier.Option{DeepCopy: true})
	if err != nil {
		return nil, err
	}

	overTimeReq.ApplyType = []string{
		oa_model.TypeOverTime, // 加班
	}
	overTimeApplyInfo := new(oa.ApplyRecordRes)
	overTimeApplyInfo, err = service.GrpcOAImpl.QueryOaApply(context.Background(), overTimeReq)
	if err != nil {
		return nil, err
	}

	oaApplys = make([]oa_model.OaApply, 0)

	err = copier.CopyWithOption(&oaApplys, overTimeApplyInfo.Data, copier.Option{DeepCopy: true})
	if err != nil {
		return nil, err
	}

	fmt.Println("加班记录 ======================================")
	fmt.Printf("%+v\n", oaApplys)
	fmt.Println("加班记录 ======================================")

	return
}

func DayOffRecord(basicReq *oa.ApplyRecordReq) (oaApplys []oa_model.OaApply, err error) {

	dayOffReq := new(oa.ApplyRecordReq)
	err = copier.CopyWithOption(&dayOffReq, basicReq, copier.Option{DeepCopy: true})
	if err != nil {
		return nil, err
	}

	dayOffReq.ApplyType = []string{
		oa_model.TypeDayOff, //调休
	}
	dayOffApplyInfo := new(oa.ApplyRecordRes)
	dayOffApplyInfo, err = service.GrpcOAImpl.QueryOaApply(context.Background(), dayOffReq)
	if err != nil {
		return nil, err
	}

	oaApplys = make([]oa_model.OaApply, 0)

	err = copier.CopyWithOption(&oaApplys, dayOffApplyInfo.Data, copier.Option{DeepCopy: true})
	if err != nil {
		return nil, err
	}

	fmt.Println("调休记录 ======================================")
	fmt.Printf("%+v\n", oaApplys)
	fmt.Println("调休记录 ======================================")

	return
}

func OutWorkRecord(basicReq *oa.ApplyRecordReq) (oaApplys []oa_model.OaApply, err error) {

	outWorkReq := new(oa.ApplyRecordReq)
	err = copier.CopyWithOption(&outWorkReq, basicReq, copier.Option{DeepCopy: true})
	if err != nil {
		return nil, err
	}

	outWorkReq.ApplyType = []string{
		oa_model.TypeOutWork, // 外勤
	}
	outWorkReq.ApplyStatus = append(outWorkReq.ApplyStatus, e.ApprovalWorkStatusDoing)
	outWorkApplyInfo := new(oa.ApplyRecordRes)
	outWorkApplyInfo, err = service.GrpcOAImpl.QueryOaApply(context.Background(), outWorkReq)
	if err != nil {
		return nil, err
	}

	oaApplys = make([]oa_model.OaApply, 0)

	err = copier.CopyWithOption(&oaApplys, outWorkApplyInfo.Data, copier.Option{DeepCopy: true})
	if err != nil {
		return nil, err
	}

	fmt.Println("外勤记录 ======================================")
	fmt.Printf("%+v\n", oaApplys)
	fmt.Println("外勤记录 ======================================")

	return
}

func ConfirmActionType(c *gin.Context) {

	req := oa_model.ClockInReq{}
	if err := c.ShouldBind(&req); err != nil {
		logger.Errorf("ClockIn ShouldBind err", err)
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	//in, err := service.ConfirmIsInCompany(req.Longitude, req.Latitude)
	//if err != nil {
	//	service.ResponseMsg(c, e.SUCCESS, serializer.Response{
	//		Msg:    err.Error(),
	//		Status: e.Failed,
	//	})
	//	return
	//}

	confirm := new(oa_model.ConfirmActionType)
	// 组装 考勤班次信息
	userInfo := login.GetUserInfoFromC(c)

	queryTimeOptions := make([]oa_model.QueryTimeOption, 0)
	for i := 0; i < len(userInfo.PositionUsers); i++ {
		queryTimeOption := oa_model.QueryTimeOption{
			PositionUID:   strconv.FormatUint(userInfo.PositionUsers[i].PositionID, 10),
			DepartmentUID: strconv.FormatUint(userInfo.PositionUsers[i].DepartmentId, 10),
		}
		queryTimeOptions = append(queryTimeOptions, queryTimeOption)
	}

	nowTime := time.Now()

	times, err := service.WorkingTimeBest(0, queryTimeOptions)
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    e.ErrAttendanceMiss,
			Status: e.Ok,
		})
		return
	}

	confirm.ActionType, confirm.WorkTime, confirm.OnOff, err = service.ConfirmActionTypeWithOaRecord(userInfo, nowTime, times)

	workTimeFull, _ := time.ParseInLocation("2006-01-02 15:04:05", nowTime.Format("2006-01-02")+" "+confirm.WorkTime+":00", time.Local)

	// 在 确认 打卡类型 之后  确认 申请类型
	//if !in {
	//	confirm.ActionType = oa_model.TypeOutWork
	//}

	confirm.ApplyType, confirm.ApplyWorkTime, err = service.ConfirmApplyType(userInfo, workTimeFull, times)

	service.ResponseMsg(c, e.SUCCESS, serializer.Response{
		Data:   confirm,
		Status: e.Ok,
	})
}

func ConfirmActionTypeV1(c *gin.Context) {

	// 组装 考勤班次信息
	userInfo := login.GetUserInfoFromC(c)
	//departmentUUIDs := make([]string, 0)
	//for i := 0; i < len(userInfo.PositionUsers); i++ {
	//	departmentUUIDs = append(departmentUUIDs, strconv.FormatUint(userInfo.PositionUsers[i].DepartmentId, 10))
	//}
	//listByDidReq := new(position.ListByDidRequest)
	//listByDidReq.UserId = userInfo.ID
	//listByDidRes, err := service.PositionProvider.ListByDid(context.Background(), listByDidReq)
	//if err != nil {
	//	service.ResponseMsg(c, e.SUCCESS, serializer.Response{
	//		Msg:    err.Error(),
	//		Status: e.Ok,
	//	})
	//	return
	//}
	queryTimeOptions := make([]oa_model.QueryTimeOption, 0)
	for i := 0; i < len(userInfo.PositionUsers); i++ {
		queryTimeOption := oa_model.QueryTimeOption{
			PositionUID:   strconv.FormatUint(userInfo.PositionUsers[i].PositionID, 10),
			DepartmentUID: strconv.FormatUint(userInfo.PositionUsers[i].DepartmentId, 10),
		}
		queryTimeOptions = append(queryTimeOptions, queryTimeOption)
	}

	nowTime := time.Now()
	var actionType string
	var workTime string

	times, err := service.WorkingTimeBest(0, queryTimeOptions)
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Ok,
		})
		return
	}

	// 查询 员工 当天的 考勤 记录
	oaRecordReq := new(oa.OARecordReq)
	oaRecordReq.StaffUID = userInfo.ID
	oaRecordReq.Dates = []string{time.Now().Format("2006-01-02")}
	oaRecordReq.Page = 1
	oaRecordReq.PageSize = 9999
	oaRecordRes, err := service.GrpcOAImpl.OARecord(context.Background(), oaRecordReq)
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	var currentClickInTime time.Time

	if len(oaRecordRes.Data[len(oaRecordRes.Data)-len(oaRecordRes.Data)].Records) > 0 { // 获取 当天 最后一次打卡点
		hour := oaRecordRes.Data[len(oaRecordRes.Data)-len(oaRecordRes.Data)].Records[len(oaRecordRes.Data[len(oaRecordRes.Data)-len(oaRecordRes.Data)].Records)-1].WorkTime
		currentClickInTime, _ = time.ParseInLocation("2006-01-02 15:04:05", time.Now().Format("2006-01-02")+" "+hour+":00", time.Local)
	}

	// 确认  打卡类型  考勤时间   注: 考勤规则 是 选择 最优的 考勤规则  会与 设置的考勤规则 有出入
	actionType, workTime = oa_logic.ConfirmActionTypeV2(nowTime, currentClickInTime, times)

	// 查询 oa 申请  请假 调休
	// 计算 考勤时间点
	applyReq := new(oa.ApplyRecordReq)
	applyReq.ApplyType = []string{
		oa_model.TypeLeave, // 请假
		oa_model.TypeSick,
		oa_model.TypeAnnualLeave,
		oa_model.TypeMaritalLeave,
		oa_model.TypeMatingCheckLeave,
		oa_model.TypeMaternityLeave,
		oa_model.TypePaternityLeave,
		oa_model.TypeParentalLeave,
		oa_model.TypeNursingLeave,
		oa_model.TypeFuneralLeave,
		oa_model.TypeAbortLeave,
		oa_model.TypeBreastFeedingLeave,
		oa_model.TypeDayOff,   // 调休
		oa_model.TypeOverTime, // 加班
	}
	applyReq.ApplyStatus = []int32{e.ApprovalWorkStatusOk}
	applyReq.BeginTime = time.Now().Format("2006-01-02")
	applyReq.EndTime = time.Now().Format("2006-01-02")
	applyReq.Page = 1
	applyReq.PageSize = 9999
	applyReq.StaffUID = userInfo.ID
	applyRes, err := service.GrpcOAImpl.QueryOaApply(context.Background(), applyReq)
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	workTimeFull, _ := time.ParseInLocation("2006-01-02 15:04:05", nowTime.Format("2006-01-02")+" "+workTime+":00", time.Local)

	if len(applyRes.Data) > 0 {
		for i := 0; i < len(applyRes.Data); i++ {
			var (
				applyStartTimeStr string
				applyEndTimeStr   string
				applyStartTime    time.Time
				applyEndTime      time.Time
			)

			applyTimes := make([]oa_model.ApplyTime, 0)
			_ = copier.CopyWithOption(&applyTimes, applyRes.Data[i].ApplyTimes, copier.Option{DeepCopy: true})
			applyStartTimeStr, applyEndTimeStr = oa_logic.ConvertApplyTimeV2(applyTimes, times)

			applyStartTime, _ = time.ParseInLocation("2006-01-02 15:04:05", applyStartTimeStr, time.Local)
			applyEndTime, _ = time.ParseInLocation("2006-01-02 15:04:05", applyEndTimeStr, time.Local)

			if !workTimeFull.Before(applyStartTime) && !workTimeFull.After(applyEndTime) {
				actionType = applyRes.Data[i].ApplyType
				break
			}
		}
	}

	service.ResponseMsg(c, e.SUCCESS, serializer.Response{
		Data:   actionType + " " + workTime,
		Status: e.Ok,
	})
}

func MissRecordForMakeUpV1(c *gin.Context) {
	req := new(oa.MissRecordReq)

	userInfo := login.GetUserInfoFromC(c)
	req.StaffUID = userInfo.ID

	req.Date = time.Now().Format("2006-01-02")

	res, err := service.GrpcOAImpl.MissRecordForMakeUp(context.Background(), req)

	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	// oa 申请  请假  加班  调休
	applyReq := new(oa.ApplyRecordReq)
	applyReq.StaffUID = userInfo.ID
	applyReq.ApplyType = []string{
		oa_model.TypeMakeUp, // 补卡
	}
	applyReq.ApplyStatus = []int32{e.ApprovalWorkStatusDoing, e.ApprovalWorkStatusOk}
	now, _ := time.ParseInLocation("2006-01-02 15:04:05", time.Now().Format("2006-01-02 15:04:05"), time.Local)
	applyReq.BeginTime = now.AddDate(0, 0, -1).Format("2006-01-02")
	applyReq.EndTime = now.Format("2006-01-02")
	applyReq.Page = 1
	applyReq.PageSize = 1000
	apply, err := service.GrpcOAImpl.QueryOaApply(context.Background(), applyReq)
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	if len(apply.Data) == 0 {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    res.Msg,
			Data:   res,
			Status: e.Ok,
		})
		return
	}

	missMap := make(map[string]string, 0)

	for j := 0; j < len(apply.Data); j++ {
		missMap[strings.Join([]string{apply.Data[j].ApplyTimes[0].Date, apply.Data[j].ApplyTimes[0].Hour}, " ")] = strings.Join([]string{apply.Data[j].ApplyTimes[0].Date, apply.Data[j].ApplyTimes[0].Hour}, " ")
	}

	newRes := new(oa.MissRecordForMakeUpRes)
	newRes.MissRecord = make([]string, 0)
	newRes.Msg = res.Msg
	for i := 0; i < len(res.MissRecord); i++ {

		if _, ok := missMap[res.MissRecord[i]]; ok {
			continue
		}

		newRes.MissRecord = append(newRes.MissRecord, res.MissRecord[i])
	}

	service.ResponseMsg(c, e.SUCCESS, serializer.Response{
		Msg:    newRes.Msg,
		Data:   newRes,
		Status: e.Ok,
	})

}

func MissRecordForMakeUpV2(c *gin.Context) {
	req := new(oa.OtherRecordReq)

	userInfo := login.GetUserInfoFromC(c)
	req.StaffUID = userInfo.ID

	now, _ := time.ParseInLocation("2006-01-02 15:04:05", time.Now().Format("2006-01-02 15:04:05"), time.Local)
	// 0318 之前的逻辑
	//req.StartDate = now.AddDate(0, 0, -1).Format("2006-01-02")
	//req.EndDate = now.Format("2006-01-02")

	// 0318  当月的缺卡记录
	var month string
	if now.Day() > 25 {
		month = now.AddDate(0, 1, 0).Format("2006-01")
		month = month + "-25"
	} else {
		month = now.Format("2006-01")
		month = month + "-25"
	}

	dates := holiday.SplitMonthV1(month, "2006-01-02")
	req.StartDate = dates[len(dates)-len(dates)]
	req.EndDate = dates[len(dates)-1]

	req.ActionType = []string{oa_model.TypeMiss}
	req.Page = 1
	req.PageSize = 9999

	res, err := service.GrpcOAImpl.OtherRecord(context.Background(), req) // 查询 缺卡记录

	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	miss := make([]string, 0)
	missMap := make(map[string]string, 0)
	timeOptionsMap := make(map[string][]*oa.TimeOption, 0)
	//  整理 缺卡 记录
	records := make([]*oa.OAProfile, 0)
	for i := 0; i < len(res.Data); i++ {
		// 0318 不需要再对比了
		//if res.Data[i].WorkDate == req.StartDate { // workDate  是 前一天  那么就比对  workTime  是不是  最后一次 下班打卡 时间  是就添加  不是就 跳过
		//	if res.Data[i].WorkTime != res.Data[i].Time[len(res.Data[i].Time)-1].OffWorkTime {
		//		continue
		//	}
		//}

		miss = append(miss, strings.Join([]string{res.Data[i].WorkDate, res.Data[i].WorkTime}, " "))
		records = append(records, res.Data[i])
		missMap[strings.Join([]string{res.Data[i].WorkDate, res.Data[i].WorkTime}, " ")] = strings.Join([]string{res.Data[i].WorkDate, res.Data[i].WorkTime}, " ")
		timeOptionsMap[res.Data[i].WorkDate] = res.Data[i].Time
	}

	sort.Strings(miss)

	fmt.Println("================================   miss  for  makeUp =======================================")
	fmt.Printf("missMap : %+v \n", missMap)
	fmt.Printf("timeOptionsMap : %+v \n", timeOptionsMap)
	fmt.Printf("records : %+v \n", records)
	fmt.Println("================================   miss  for  makeUp =======================================")

	// oa 申请  请假  加班  调休
	applyReq := new(oa.ApplyRecordReq)
	applyReq.StaffUID = userInfo.ID
	applyReq.ApplyType = []string{
		oa_model.TypeLeave, // 请假
		oa_model.TypeSick,
		oa_model.TypeAnnualLeave,
		oa_model.TypeMaritalLeave,
		oa_model.TypeMatingCheckLeave,
		oa_model.TypeMaternityLeave,
		oa_model.TypePaternityLeave,
		oa_model.TypeParentalLeave,
		oa_model.TypeNursingLeave,
		oa_model.TypeFuneralLeave,
		oa_model.TypeDayOff, // 调休
		//oa_model.TypeOverTime, // 加班
		//oa_model.TypeOutWork, // 外勤
		oa_model.TypeMakeUp, // 补卡
	}
	applyReq.ApplyStatus = []int32{e.ApprovalWorkStatusDoing, e.ApprovalWorkStatusOk}
	//applyReq.BeginTime = now.AddDate(0, 0, -1).Format("2006-01-02")
	//applyReq.EndTime = now.Format("2006-01-02")
	applyReq.BeginTime = dates[len(dates)-len(dates)]
	applyReq.EndTime = dates[len(dates)-1]
	applyReq.Page = 1
	applyReq.PageSize = 1000
	apply, err := service.GrpcOAImpl.QueryOaApply(context.Background(), applyReq)
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	if len(apply.Data) == 0 {
		goto end
	}

	for i := 0; i < len(records); i++ {
		missTime, _ := time.ParseInLocation("2006-01-02 15:04:05", records[i].WorkDate+" "+records[i].WorkTime+":00", time.Local)
		for j := 0; j < len(apply.Data); j++ {
			//   判断  缺卡的 日期 在 申请的 日期内
			if apply.Data[j].ApplyType == oa_model.TypeMakeUp {
				if apply.Data[j].ApplyTimes[len(apply.Data[j].ApplyTimes)-len(apply.Data[j].ApplyTimes)].Date == records[i].WorkDate && apply.Data[j].ApplyTimes[len(apply.Data[j].ApplyTimes)-len(apply.Data[j].ApplyTimes)].Hour == records[i].WorkTime {
					missMap[strings.Join([]string{records[i].WorkDate, records[i].WorkTime}, " ")] = ""
				}
			} else {
				if len(apply.Data[j].ApplyTimes) == 2 {
					var (
						startTime time.Time
						endTime   time.Time
					)
					if apply.Data[j].ApplyTimes[0].Hour != "" {
						startTime, _ = time.ParseInLocation("2006-01-02 15:04:05", apply.Data[j].ApplyTimes[0].Date+" "+apply.Data[j].ApplyTimes[0].Hour+":00", time.Local)
						endTime, _ = time.ParseInLocation("2006-01-02 15:04:05", apply.Data[j].ApplyTimes[1].Date+" "+apply.Data[j].ApplyTimes[1].Hour+":00", time.Local)
						if !startTime.After(missTime) && !endTime.Before(missTime) {
							missMap[strings.Join([]string{records[i].WorkDate, records[i].WorkTime}, " ")] = ""
						}
					} else {
						var (
							startHour      string
							endHour        string
							applyStartDate string
							applyEndDate   string
						)
						if timeOptionsMap[apply.Data[j].ApplyTimes[0].Date] == nil {
							applyStartDate = req.StartDate
						} else {
							applyStartDate = apply.Data[j].ApplyTimes[0].Date
						}

						if timeOptionsMap[apply.Data[j].ApplyTimes[1].Date] == nil {
							applyEndDate = req.EndDate
						} else {
							applyEndDate = apply.Data[j].ApplyTimes[1].Date
						}
						if apply.Data[j].ApplyTimes[0].M == "上午" {
							startHour = timeOptionsMap[applyStartDate][0].OnWorkTime
						} else {
							if len(timeOptionsMap[applyStartDate]) == 1 {
								startHour = "12:00"
							} else if len(timeOptionsMap[applyStartDate]) == 2 {
								startHour = timeOptionsMap[applyStartDate][1].OnWorkTime
							}
						}

						if apply.Data[j].ApplyTimes[1].M == "上午" {
							if len(timeOptionsMap[applyStartDate]) == 1 {
								endHour = "12:00"
							} else if len(timeOptionsMap[applyStartDate]) == 2 {
								endHour = timeOptionsMap[applyStartDate][0].OffWorkTime
							}
						} else {
							if len(timeOptionsMap[applyEndDate]) == 1 {
								endHour = timeOptionsMap[applyEndDate][0].OffWorkTime
							} else if len(timeOptionsMap[applyEndDate]) == 2 {
								endHour = timeOptionsMap[applyEndDate][1].OffWorkTime
							}
						}
						startTime, _ = time.ParseInLocation("2006-01-02 15:04:05", apply.Data[j].ApplyTimes[0].Date+" "+startHour+":00", time.Local)
						endTime, _ = time.ParseInLocation("2006-01-02 15:04:05", apply.Data[j].ApplyTimes[1].Date+" "+endHour+":00", time.Local)
						if !startTime.After(missTime) && !endTime.Before(missTime) {
							missMap[strings.Join([]string{records[i].WorkDate, records[i].WorkTime}, " ")] = ""
						}
					}
				}
			}
		}
	}

end:

	newRes := new(oa.MissRecordForMakeUpRes)
	newRes.MissRecord = make([]string, 0)
	//for k, v := range missMap {
	//	if v != "" {
	//		newRes.MissRecord = append(newRes.MissRecord, k)
	//	}
	//}

	// 排序
	for i := 0; i < len(miss); i++ {
		if missMap[miss[i]] != "" {
			newRes.MissRecord = append(newRes.MissRecord, missMap[miss[i]])
		}
	}

	service.ResponseMsg(c, e.SUCCESS, serializer.Response{
		Msg:    newRes.Msg,
		Data:   newRes,
		Status: e.Ok,
	})

}

func SystemMakeUp(c *gin.Context) {
	req := new(oa.SystemMakeUpReq)
	if err := c.ShouldBind(&req); err != nil {
		logger.Errorf("SystemMakeUp ShouldBind err", err)
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	if req.StaffUID == 0 || req.WorkTime == "" || req.WorkDate == "" {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    e.ErrMissSystemMakeUpParam,
			Status: e.Failed,
		})
		return
	}

	if req.ActionTime != req.WorkTime {
		if common.ConvertWorkDateAndWorkTime(req.ActionDate, req.ActionTime).Before(common.ConvertWorkDateAndWorkTime(req.ActionDate, common.NotWorkDayWorkTimeOn())) {
			service.ResponseMsg(c, e.SUCCESS, serializer.Response{
				Msg:    e.ErrActionTimeNotBeforeNextDateFour,
				Status: e.Failed,
			})
			return
		}
	}

	res, err := service.GrpcOAImpl.SystemMakeUp(context.Background(), req)
	if err != nil {
		service.ResponseMsg(c, e.SUCCESS, serializer.Response{
			Msg:    err.Error(),
			Status: e.Failed,
		})
		return
	}

	service.ResponseMsg(c, e.SUCCESS, serializer.Response{
		Msg:    res.Msg,
		Status: e.Ok,
	})
}