package main

import (
	"bytes"
	"encoding/base64"
	"errors"
	"fmt"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/xuri/excelize/v2"
)

var dingTalk = new(DingTalk)

type DingTalkService struct {
	isSyncing  bool
	syncLocker *sync.Mutex
}

func (service *DingTalkService) initialize() {
	service.isSyncing = false
	service.syncLocker = new(sync.Mutex)
	dingTalk.refreshToken()
	go func() {
		for {
			time.Sleep(time.Minute * 5)
			dingTalk.refreshToken()
		}
	}()
}

func getSubDepartments(dingTalk *DingTalk, parentId int, departments []Department) []Department {
	subDepartments := dingTalk.getDepartments(parentId)
	for i := 0; i < len(subDepartments); i++ {
		departments = append(departments, subDepartments[i])
		departments = getSubDepartments(dingTalk, subDepartments[i].Id, departments)
	}
	return departments
}

func getDepartmentEmployees(dingTalk *DingTalk, departmentId int, employees []Employee) []Employee {
	departmentEmployees := dingTalk.getEmployees(departmentId)
	for i := 0; i < len(departmentEmployees); i++ {
		employees = append(employees, departmentEmployees[i])
	}
	subDepartments := dingTalk.getDepartments(departmentId)
	for i := 0; i < len(subDepartments); i++ {
		subDepartment := subDepartments[i]
		employees = getDepartmentEmployees(dingTalk, subDepartment.Id, employees)
	}
	return employees
}

func (service *DingTalkService) GeteEmployeeIsLeader(authCode string) bool {
	var employeeId = dingTalk.getEmployeeIdByAuthCode(authCode)
	return dingTalk.getIsLeaderById(employeeId)
}

func (service *DingTalkService) GeteEmployeeNameByAuthCode(authCode string) string {
	return dingTalk.getEmployeeNameByAuthCode(authCode)
}

func (service *DingTalkService) GetDepartmentEmployees(departmentId int) []Employee {
	employees := make([]Employee, 0)
	return getDepartmentEmployees(dingTalk, departmentId, employees)
}

func (service *DingTalkService) GetDepartments(departmentId int) []Department {
	departments := make([]Department, 0)
	return getSubDepartments(dingTalk, departmentId, departments)
}

func (service *DingTalkService) SyncDepartmentEmployees(parentId int, syncedCount *int, employeeCount int, progressId string) {
	defer func() {
		var p = any(recover())
		if p != nil {
			errStr := fmt.Sprintln("error_") + fmt.Sprintf("%v", p)
			progressService.AddOrUpdateProgress(progressId, errStr)
		}
	}()

	employees := dingTalk.getEmployees(parentId)
	for i := 0; i < len(employees); i++ {
		employee := employees[i]
		exist := databaseManager.GetEmployeeById(employee.Id)
		if exist == nil {
			databaseManager.AddEmployee(&employee)
		} else {
			databaseManager.UpdateEmployee(&employee)
		}
		*syncedCount++
		progressValue := *syncedCount * 100 / employeeCount
		progressService.AddOrUpdateProgress(progressId, strconv.Itoa(progressValue))
	}

	departments := dingTalk.getDepartments(parentId)
	for i := 0; i < len(departments); i++ {
		department := departments[i]
		exist := databaseManager.GetDepartmentById(department.Id)
		if exist == nil {
			databaseManager.AddDepartment(&department)
		} else {
			databaseManager.UpdateDepartment(&department)
		}
		service.SyncDepartmentEmployees(department.Id, syncedCount, employeeCount, progressId)
	}
}

func (service *DingTalkService) SyncEmployeess2(progressId string) {
	defer func() {
		var p = any(recover())
		if p != nil {
			errStr := fmt.Sprintln("error_") + fmt.Sprintf("%v", p)
			progressService.AddOrUpdateProgress(progressId, errStr)
			if service.isSyncing {
				service.isSyncing = false
				service.syncLocker.Unlock()
			}
		}
	}()
	if service.isSyncing {
		progressService.AddOrUpdateProgress(progressId, "error_一个同步操作已经在进行中。")
		return
	}
	service.syncLocker.Lock()
	service.isSyncing = true
	dingTalk.refreshToken()
	//Create or update default department
	rootDepartment := Department{1, "VINNO", 0}
	exist := databaseManager.GetDepartmentById(rootDepartment.Id)
	if exist == nil {
		databaseManager.AddDepartment(&rootDepartment)
	} else {
		databaseManager.UpdateDepartment(&rootDepartment)
	}
	employeeCount := dingTalk.getEmployeeCount()
	syncCount := 0
	dingTalkService.SyncDepartmentEmployees(rootDepartment.Id, &syncCount, employeeCount, progressId)
	service.isSyncing = false
	service.syncLocker.Unlock()
}

func (service *DingTalkService) SyncEmployees(departmentId int) {
	service.syncLocker.Lock()
	if service.isSyncing {
		service.syncLocker.Unlock()
		return
	} else {
		service.isSyncing = true
		service.syncLocker.Unlock()
	}
	dingTalk.refreshToken()
	employees := service.GetDepartmentEmployees(departmentId)
	for i := 0; i < len(employees); i++ {
		employee := employees[i]
		exist := databaseManager.GetEmployeeById(employee.Id)
		if exist == nil {
			databaseManager.AddEmployee(&employee)
		} else {
			databaseManager.UpdateEmployee(&employee)
		}
	}
	service.syncLocker.Lock()
	service.isSyncing = false
	service.syncLocker.Unlock()
}

func (service *DingTalkService) SendTextMessage(userId string, text string) {
	dingTalk.sendTextMessage([]string{userId}, text)
}

func (service *DingTalkService) SendUrlMessage(userId string, title string, text string, imageUrl string, url string) {
	dingTalk.sendUrlMessage([]string{userId}, title, text, imageUrl, url)
}

func (service *DingTalkService) SendActionCard(userId string, title string, markdown string, singleTitle string, singleUrl string) {
	dingTalk.sendActionCard([]string{userId}, title, markdown, singleTitle, singleUrl)
}

type LoginService struct {
}

func createSession(accountId string) *Session {
	session := new(Session)
	session.sessionId = NewUUId()
	session.accountId = accountId
	session.updateTime = time.Now()
	sessionManager.RemoveSessionByAccountId(session.accountId)
	sessionManager.AddSession(session)
	return session
}

func (service *LoginService) Login(name string, password string) string {

	result := databaseManager.GetAdminByName(name)
	if result == nil {
		var err any = errors.New("用户名不存在。")
		panic(err)
	} else {
		if result.Password != password {
			var err any = errors.New("密码错误。")
			panic(err)
		} else {
			//强制替换session
			session := createSession(result.Id)
			return session.sessionId
		}
	}
}

func (service *LoginService) Logoff(sessionId string) {
	sessionManager.RemoveSessionBySessionId(sessionId)
}

func CheckSession(sessionId string) {
	if strings.Index(sessionId, "ViewCode") != 0 {
		session := sessionManager.GetSessionBySessionId(sessionId)
		if session == nil {
			var err any = errors.New("无效的登录信息。")
			panic(err)
		}
	}
}

type AdminService struct {
}

func (service *AdminService) Add(sessionId string, admin Admin) {
	CheckSession(sessionId)
	exist := databaseManager.GetAdminByName(admin.Name)
	if exist != nil {
		var err any = "管理员已经存在."
		panic(err)
	}
	databaseManager.AddAdmin(&admin)
}

func (service *AdminService) GetAdminIds(sessionId string) []string {
	CheckSession(sessionId)
	return []string{
		"14033025461181119",   //邓
		"0117116430501258458", //高
		"1403411622464238794", //邢
	}
}

func (service *AdminService) Update(sessionId string, adminName string, adminPassword string) {
	CheckSession(sessionId)
	exist := databaseManager.GetAdminByName(adminName)
	if exist == nil {
		var err any = "管理员不存在."
		panic(err)
	}
	exist.Password = adminPassword
	databaseManager.UpdateAdminPassword(exist)
}

func (service *AdminService) Get(sessionId string, id string) Admin {
	CheckSession(sessionId)
	return *databaseManager.GetAdminById(id)
}

type DepartmentService struct {
}

func (service *DepartmentService) GetDepartment(sessionId string, departmentId int) Department {
	CheckSession(sessionId)
	result := databaseManager.GetDepartmentById(departmentId)
	return *result
}

func (service *DepartmentService) GetChildDepartments(sessionId string, departmentId int) []Department {
	CheckSession(sessionId)
	departments := make([]Department, 0)
	result := databaseManager.GetDepartmentsByParentId(departmentId)
	for i := 0; i < len(result); i++ {
		departments = append(departments, *result[i])
	}
	return departments
}

type EmployeeService struct {
}

func getEmployeesByDepartmentId(departmentId int, employees []Employee) []Employee {
	result := databaseManager.GetEmployeeManyByDepartmentId(departmentId)
	for i := 0; i < len(result); i++ {
		employees = append(employees, *result[i])
	}
	departments := databaseManager.GetDepartmentsByParentId(departmentId)
	for i := 0; i < len(departments); i++ {
		employees = getEmployeesByDepartmentId(departments[i].Id, employees)
	}
	return employees
}

func (service *EmployeeService) GetReviewers(sessionId string) []Employee {
	CheckSession(sessionId)
	employees := make([]Employee, 0)
	result := databaseManager.GetReviewers()
	for i := 0; i < len(result); i++ {
		employees = append(employees, *result[i])
	}
	return employees
}

func (service *EmployeeService) GetManyByDepartmentId(sessionId string, departmentId int) []Employee {
	CheckSession(sessionId)
	employees := make([]Employee, 0)
	employees = getEmployeesByDepartmentId(departmentId, employees)
	return employees
}

func (service *EmployeeService) GetLeaveQuotas(email string, leaveType int) []LeaveQuota {
	employee := databaseManager.GetEmployeeByEmail(email)
	if employee != nil {
		return dingTalk.getLeaveQuotas(employee.Id, leaveType)
	} else {
		var err any = "账号不存在。"
		panic(err)
	}
}

func (service *EmployeeService) UpdateEmployee(sessionId string, employee Employee) {
	CheckSession(sessionId)
	databaseManager.UpdateEmployee(&employee)
}

func (service *EmployeeService) GetEmployeeCount(sessionId string) int {
	CheckSession(sessionId)
	return int(databaseManager.GetEmployeeCount())
}

func (service *EmployeeService) GetStruggleEmployeeCount(sessionId string) int {
	CheckSession(sessionId)
	return int(databaseManager.GetStruggleEmployeeCount())
}

func (service *EmployeeService) UpdateEmployeeDepartment(sessionId string, employee Employee) {
	CheckSession(sessionId)
	databaseManager.UpdateEmployeeDepartment(&employee)
}

func (service *EmployeeService) UpdateEmployeeIsStruggle(sessionId string, employee Employee) {
	CheckSession(sessionId)
	databaseManager.UpdateEmployeeIsStruggle(&employee)
}

func (service *EmployeeService) UpdateEmployeeIsReviewer(sessionId string, employee Employee) {
	CheckSession(sessionId)
	databaseManager.UpdateEmployeeIsReviewer(&employee)
}

func (service *EmployeeService) GetByName(sessionId string, employeeName string) Employee {
	CheckSession(sessionId)
	result := databaseManager.GetEmployeeByName(employeeName)
	if result == nil {
		var err any = "账号不存在。"
		panic(err)
	}
	return *result
}

func (service *EmployeeService) Get(sessionId string, employeeId string) Employee {
	CheckSession(sessionId)
	result := databaseManager.GetEmployeeById(employeeId)
	if result == nil {
		var err any = "账号不存在。"
		panic(err)
	}
	return *result
}

func (service *EmployeeService) Delete(sessionId string, employeeId string) {
	CheckSession(sessionId)
	result := databaseManager.GetEmployeeById(employeeId)
	if result == nil {
		var err any = "账号不存在。"
		panic(err)
	}
	databaseManager.DeleteEmployee(employeeId)
}

type WorkingDayService struct {
}

func (service *WorkingDayService) Update(sessionId string, workingDayInfo WorkingDayInfo) {
	CheckSession(sessionId)
	exist := databaseManager.GetWorkingDayInfoById(workingDayInfo.Id)
	if exist == nil {
		var err any = "工作日不存在。"
		panic(err)
	}
	databaseManager.UpdateWorkingDayInfo(&workingDayInfo)
}

func (service *WorkingDayService) GetMany(sessionId string, employeeId string, statisticsId string) []WorkingDayInfo {
	CheckSession(sessionId)
	workingDayInfos := make([]WorkingDayInfo, 0)
	result := databaseManager.GetWorkingDayInfoMany(employeeId, statisticsId)
	for i := 0; i < len(result); i++ {
		workingDayInfos = append(workingDayInfos, *result[i])
	}
	return workingDayInfos
}

type WorkingDayStatisticsService struct {
	checkHistory map[string]*CheckHistory
}

func (service *WorkingDayStatisticsService) CheckEmployeeStatistics(email string, startStr string, endStr string) int {
	if service.checkHistory == nil {
		service.checkHistory = make(map[string]*CheckHistory)
	}
	if service.checkHistory[strings.ToLower(email)] != nil {
		now := time.Now()
		history := service.checkHistory[strings.ToLower(email)]
		if history.CheckDay.Year() == now.Year() && history.CheckDay.Month() == now.Month() && history.CheckDay.Day() == now.Day() {
			return history.TotalUnits
		}
	}
	totalUnits := 0
	employee := databaseManager.GetEmployeeByEmail(email)
	if employee == nil {
		var err any = "查询的员工不存在。"
		panic(err)
	}
	start := strToDateTime(startStr)
	end := strToDateTime(endStr)
	holidayInfos := databaseManager.GetHolidayDayInfos(time.Now().AddDate(-1, 0, 0), time.Now().AddDate(1, 0, 0))
	holidays := make(map[int64]*HolidayInfo)
	adjustDays := make(map[int64]*HolidayInfo)
	for i := 0; i < len(holidayInfos); i++ {
		holidays[holidayInfos[i].Holiday.Unix()] = holidayInfos[i]
		if holidayInfos[i].IsAdjusted {
			adjustDays[holidayInfos[i].AdjustedDay.Unix()] = holidayInfos[i]
		}
	}
	attendanceList := dingTalk.getEmployeeAttendanceDataList(employee.Id, start, end)
	statisticsId := NewUUId()
	for j := 0; j < len(attendanceList); j++ {
		attendanceData := attendanceList[j]
		day := attendanceData.Day
		workingDayInfo := new(WorkingDayInfo)
		workingDayInfo.Id = NewUUId()
		workingDayInfo.StatisticsId = statisticsId
		workingDayInfo.EmployeeId = employee.Id
		workingDayInfo.Day = day
		workingDayInfo.Start = attendanceData.StartTime
		if attendanceData.StartResult != "正常" {
			workingDayInfo.StartResult = attendanceData.StartResult
		}
		workingDayInfo.End = attendanceData.EndTime
		if attendanceData.EndResult != "正常" {
			workingDayInfo.EndResult = attendanceData.EndResult
		}
		workingDayInfo.State = attendanceData.State

		isHoliday := holidays[attendanceData.Day.Unix()] != nil
		workingDayInfo.IsHoliday = isHoliday
		if isHoliday {
			holidayInfo := holidays[attendanceData.Day.Unix()]
			if holidayInfo.IsAdjusted {
				workingDayInfo.HolidayAdjusted = true
				workingDayInfo.AdjustedDay = holidayInfo.AdjustedDay
			}
		}
		isAdjustedDay := adjustDays[attendanceData.Day.Unix()] != nil
		if isAdjustedDay {
			holidayInfo := adjustDays[attendanceData.Day.Unix()]
			day = holidayInfo.Holiday
			workingDayInfo.HolidayAdjusted = true
			workingDayInfo.AdjustedDay = holidayInfo.Holiday
			if workingDayInfo.State == 休息 {
				workingDayInfo.State = 调班
			}
			//此天是调班日，按休假的那天算
			workingDayInfo.RequiredWorkingHours = calcRequiredWorkingHours(day, employee.AttendanceRules, attendanceData, false)
			workingDayInfo.WorkingHours = calcWorkingHours(day, employee.AttendanceRules, attendanceData, false, true, false)
		} else {
			if workingDayInfo.HolidayAdjusted {
				//此天是休息日，但是如果来按休假的那天来计算
				workingDayInfo.RequiredWorkingHours = calcRequiredWorkingHours(workingDayInfo.AdjustedDay, employee.AttendanceRules, attendanceData, true)
				workingDayInfo.WorkingHours = calcWorkingHours(workingDayInfo.AdjustedDay, employee.AttendanceRules, attendanceData, true, true, false)
			} else {
				//此天可能休息也可能不休息，但是没有调班
				workingDayInfo.RequiredWorkingHours = calcRequiredWorkingHours(day, employee.AttendanceRules, attendanceData, isHoliday)
				if isHoliday {
					holiday := holidays[day.Unix()]
					isNationalLegal := false
					if holiday != nil {
						isNationalLegal = holiday.IsNationalLegal
					}
					workingDayInfo.WorkingHours = calcWorkingHours(day, employee.AttendanceRules, attendanceData, true, false, isNationalLegal)
				} else {
					workingDayInfo.WorkingHours = calcWorkingHours(day, employee.AttendanceRules, attendanceData, false, false, false)
				}
			}
		}
		workingDayInfo.IsWeekend = day.Weekday() == time.Saturday || day.Weekday() == time.Sunday
		overHours := workingDayInfo.WorkingHours - workingDayInfo.RequiredWorkingHours
		if overHours < 0 {
			overHours = 0
		}
		units := int(overHours / 2.5)
		if units > 1 {
			//工作日最多1次
			if !workingDayInfo.IsWeekend && !workingDayInfo.IsHoliday {
				units = 1
			} else {
				//周末必须达标3次
				if units >= 3 {
					units = 3
				} else {
					units = 0
				}
			}
		}
		workingDayInfo.ObtainedUnits = units
		workingDayInfo.AdjustedObtainedUnits = workingDayInfo.ObtainedUnits
		databaseManager.AddWorkingDayInfo(workingDayInfo)
		totalUnits += workingDayInfo.ObtainedUnits
	}
	history := new(CheckHistory)
	history.CheckDay = time.Now()
	history.TotalUnits = totalUnits
	service.checkHistory[strings.ToLower(email)] = history
	return totalUnits
}

func (service *WorkingDayStatisticsService) CheckEmployeePerformanceHours(email string, startStr string, endStr string) int {
	totalHours := 0.0
	employee := databaseManager.GetEmployeeByEmail(email)
	if employee == nil {
		var err any = "查询的员工不存在。"
		panic(err)
	}
	start := strToDateTime(startStr)
	end := strToDateTime(endStr)
	attendanceList := dingTalk.getEmployeeAttendanceDataList(employee.Id, start, end)
	for j := 0; j < len(attendanceList); j++ {
		attendanceData := attendanceList[j]
		hours := calcPerformanceHours(employee.AttendanceRules, attendanceData)
		totalHours += hours
	}
	return int(totalHours / 8)
}

func (service *WorkingDayStatisticsService) Update(sessionId string, workingDayStatistics WorkingDayStatistics) {
	CheckSession(sessionId)
	exist := databaseManager.GetWorkingDayStatisticsById(workingDayStatistics.Id)
	if exist == nil {
		var err any = "工作统计不存在。"
		panic(err)
	}
	databaseManager.UpdateWorkingDayStatistics(&workingDayStatistics)
}

func (service *WorkingDayStatisticsService) UpdateComment(sessionId string, workingDayStatisticsId string, comment string) {
	CheckSession(sessionId)
	exist := databaseManager.GetWorkingDayStatisticsById(workingDayStatisticsId)
	if exist == nil {
		var err any = "工作统计不存在。"
		panic(err)
	}
	databaseManager.UpdateWorkingDayStatisticsComment(workingDayStatisticsId, comment)
}

func (service *WorkingDayStatisticsService) Get(sessionId string, workingDayStatisticsId string) WorkingDayStatistics {
	CheckSession(sessionId)
	result := databaseManager.GetWorkingDayStatisticsById(workingDayStatisticsId)
	return *result
}

func (service *WorkingDayStatisticsService) GetMany(sessionId string, workingDayStatisticsIds []string) []WorkingDayStatistics {
	CheckSession(sessionId)
	workingDayStatisticsList := make([]WorkingDayStatistics, 0)
	result := databaseManager.GetWorkingDayStatisticsByIds(workingDayStatisticsIds)
	for i := 0; i < len(result); i++ {
		workingDayStatisticsList = append(workingDayStatisticsList, *result[i])
	}
	return workingDayStatisticsList
}

type StatisticsTableService struct {
}

func createDepartmentName(departmentId int) string {
	if departmentId != 1 {
		department := databaseManager.GetDepartmentById(departmentId)
		if department != nil {
			departmentName := department.Name
			if department.ParentId != 1 {
				return createDepartmentName(department.ParentId) + "/" + departmentName
			} else {
				return departmentName
			}
		}
	}
	return ""
}

func (service *StatisticsTableService) Add(sessionId string, title string, startStr string, endStr string, requiredReachedCount int) {
	CheckSession(sessionId)
	exist := databaseManager.GetStatisticsTableByTitle(title)
	if exist != nil {
		var err any = "同名工作统计集合已经存在。"
		panic(err)
	}
	start := strToDateTime(startStr)
	end := strToDateTime(endStr)
	holidayInfos := databaseManager.GetHolidayDayInfos(time.Now().AddDate(-1, 0, 0), time.Now().AddDate(1, 0, 0))
	holidays := make(map[int64]*HolidayInfo)
	adjustDays := make(map[int64]*HolidayInfo)
	for i := 0; i < len(holidayInfos); i++ {
		holidays[holidayInfos[i].Holiday.Unix()] = holidayInfos[i]
		if holidayInfos[i].IsAdjusted {
			adjustDays[holidayInfos[i].AdjustedDay.Unix()] = holidayInfos[i]
		}
	}
	workingDayStatisticsIds := make([]string, 0)
	employees := databaseManager.GetStruggleEmployeeMany()
	for i := 0; i < len(employees); i++ {
		totalUnits := 0
		attendanceList := dingTalk.getEmployeeAttendanceDataList(employees[i].Id, start, end)
		statisticsId := NewUUId()
		for j := 0; j < len(attendanceList); j++ {
			attendanceData := attendanceList[j]
			day := attendanceData.Day
			workingDayInfo := new(WorkingDayInfo)
			workingDayInfo.Id = NewUUId()
			workingDayInfo.StatisticsId = statisticsId
			workingDayInfo.EmployeeId = employees[i].Id
			workingDayInfo.Day = day
			workingDayInfo.Start = attendanceData.StartTime
			if attendanceData.StartResult != "正常" {
				workingDayInfo.StartResult = attendanceData.StartResult
			}
			workingDayInfo.End = attendanceData.EndTime
			if attendanceData.EndResult != "正常" {
				workingDayInfo.EndResult = attendanceData.EndResult
			}
			workingDayInfo.State = attendanceData.State

			isHoliday := holidays[attendanceData.Day.Unix()] != nil
			workingDayInfo.IsHoliday = isHoliday
			if isHoliday {
				holidayInfo := holidays[attendanceData.Day.Unix()]
				if holidayInfo.IsAdjusted {
					workingDayInfo.HolidayAdjusted = true
					workingDayInfo.AdjustedDay = holidayInfo.AdjustedDay
				}
			}
			isAdjustedDay := adjustDays[attendanceData.Day.Unix()] != nil
			if isAdjustedDay {
				holidayInfo := adjustDays[attendanceData.Day.Unix()]
				day = holidayInfo.Holiday
				workingDayInfo.HolidayAdjusted = true
				workingDayInfo.AdjustedDay = holidayInfo.Holiday
				if workingDayInfo.State == 休息 {
					workingDayInfo.State = 调班
				}
				//此天是调班日，按休假的那天算
				workingDayInfo.RequiredWorkingHours = calcRequiredWorkingHours(day, employees[i].AttendanceRules, attendanceData, false)
				workingDayInfo.WorkingHours = calcWorkingHours(day, employees[i].AttendanceRules, attendanceData, false, true, false)
			} else {
				if workingDayInfo.HolidayAdjusted {
					//此天是休息日，但是如果来按休假的那天来计算
					workingDayInfo.RequiredWorkingHours = calcRequiredWorkingHours(workingDayInfo.AdjustedDay, employees[i].AttendanceRules, attendanceData, true)
					workingDayInfo.WorkingHours = calcWorkingHours(workingDayInfo.AdjustedDay, employees[i].AttendanceRules, attendanceData, true, true, false)
				} else {
					//此天可能休息也可能不休息，但是没有调班
					workingDayInfo.RequiredWorkingHours = calcRequiredWorkingHours(day, employees[i].AttendanceRules, attendanceData, isHoliday)
					if isHoliday {
						holiday := holidays[day.Unix()]
						isNationalLegal := false
						if holiday != nil {
							isNationalLegal = holiday.IsNationalLegal
						}
						workingDayInfo.WorkingHours = calcWorkingHours(day, employees[i].AttendanceRules, attendanceData, true, false, isNationalLegal)
					} else {
						workingDayInfo.WorkingHours = calcWorkingHours(day, employees[i].AttendanceRules, attendanceData, false, false, false)
					}
				}

			}
			workingDayInfo.IsWeekend = day.Weekday() == time.Saturday || day.Weekday() == time.Sunday
			overHours := workingDayInfo.WorkingHours - workingDayInfo.RequiredWorkingHours
			if overHours < 0 {
				overHours = 0
			}
			units := int(overHours / 2.5)
			if units > 1 {
				//工作日最多1次
				if !workingDayInfo.IsWeekend && !workingDayInfo.IsHoliday {
					units = 1
				} else {
					//周末必须达标3次
					if units >= 3 {
						units = 3
					} else {
						units = 0
					}
				}
			}
			workingDayInfo.ObtainedUnits = units
			workingDayInfo.AdjustedObtainedUnits = workingDayInfo.ObtainedUnits
			databaseManager.AddWorkingDayInfo(workingDayInfo)
			totalUnits += workingDayInfo.ObtainedUnits
		}
		workingDayStatistics := new(WorkingDayStatistics)
		workingDayStatistics.Id = statisticsId
		workingDayStatistics.Employee = *employees[i]
		workingDayStatistics.DepartmentId = employees[i].DepartmentId
		workingDayStatistics.DepartmentName = createDepartmentName(employees[i].DepartmentId)
		workingDayStatistics.Start = start
		workingDayStatistics.End = end
		workingDayStatistics.RequiredReachedCount = requiredReachedCount
		workingDayStatistics.ReachedCount = totalUnits
		workingDayStatistics.AdjustedReachedCount = totalUnits
		workingDayStatistics.Adjusted = false
		workingDayStatistics.AdjustDescription = ""
		workingDayStatistics.AdjustedBy = ""
		workingDayStatistics.Comment = ""
		workingDayStatistics.Reviewer = ""
		databaseManager.AddWorkingDayStatistics(workingDayStatistics)
		workingDayStatisticsIds = append(workingDayStatisticsIds, workingDayStatistics.Id)
	}
	table := new(StatisticsTable)
	table.Id = NewUUId()
	table.Title = title
	table.RequiredReachedCount = requiredReachedCount
	table.WorkingDayStatisticsIds = workingDayStatisticsIds
	table.CreateTime = time.Now()
	databaseManager.AddStatisticsTable(table)
}

func (service *StatisticsTableService) Add2(sessionId string, title string, startStr string, endStr string, requiredReachedCount int, progressId string) {
	defer func() {
		var p = any(recover())
		if p != nil {
			errStr := fmt.Sprintln("error_") + fmt.Sprintf("%v", p)
			progressService.AddOrUpdateProgress(progressId, errStr)
		}
	}()
	CheckSession(sessionId)
	exist := databaseManager.GetStatisticsTableByTitle(title)
	if exist != nil {
		var err any = "同名工作统计集合已经存在。"
		panic(err)
	}
	start := strToDateTime(startStr)
	end := strToDateTime(endStr)
	holidayInfos := databaseManager.GetHolidayDayInfos(time.Now().AddDate(-1, 0, 0), time.Now().AddDate(1, 0, 0))
	holidays := make(map[int64]*HolidayInfo)
	adjustDays := make(map[int64]*HolidayInfo)
	for i := 0; i < len(holidayInfos); i++ {
		holidays[holidayInfos[i].Holiday.Unix()] = holidayInfos[i]
		if holidayInfos[i].IsAdjusted {
			adjustDays[holidayInfos[i].AdjustedDay.Unix()] = holidayInfos[i]
		}
	}
	workingDayStatisticsIds := make([]string, 0)
	employees := databaseManager.GetStruggleEmployeeMany()
	for i := 0; i < len(employees); i++ {
		totalUnits := 0
		attendanceList := dingTalk.getEmployeeAttendanceDataList(employees[i].Id, start, end)
		statisticsId := NewUUId()
		for j := 0; j < len(attendanceList); j++ {
			attendanceData := attendanceList[j]
			day := attendanceData.Day
			workingDayInfo := new(WorkingDayInfo)
			workingDayInfo.Id = NewUUId()
			workingDayInfo.StatisticsId = statisticsId
			workingDayInfo.EmployeeId = employees[i].Id
			workingDayInfo.Day = day
			workingDayInfo.Start = attendanceData.StartTime
			if attendanceData.StartResult != "正常" {
				workingDayInfo.StartResult = attendanceData.StartResult
			}
			workingDayInfo.End = attendanceData.EndTime
			if attendanceData.EndResult != "正常" {
				workingDayInfo.EndResult = attendanceData.EndResult
			}
			workingDayInfo.State = attendanceData.State

			isHoliday := holidays[attendanceData.Day.Unix()] != nil
			workingDayInfo.IsHoliday = isHoliday
			if isHoliday {
				holidayInfo := holidays[attendanceData.Day.Unix()]
				if holidayInfo.IsAdjusted {
					workingDayInfo.HolidayAdjusted = true
					workingDayInfo.AdjustedDay = holidayInfo.AdjustedDay
				}
			}
			isAdjustedDay := adjustDays[attendanceData.Day.Unix()] != nil
			if isAdjustedDay {
				holidayInfo := adjustDays[attendanceData.Day.Unix()]
				day = holidayInfo.Holiday
				workingDayInfo.HolidayAdjusted = true
				workingDayInfo.AdjustedDay = holidayInfo.Holiday
				if workingDayInfo.State == 休息 {
					workingDayInfo.State = 调班
				}
				//此天是调班日，按休假的那天算
				workingDayInfo.RequiredWorkingHours = calcRequiredWorkingHours(day, employees[i].AttendanceRules, attendanceData, false)
				workingDayInfo.WorkingHours = calcWorkingHours(day, employees[i].AttendanceRules, attendanceData, false, true, false)
			} else {
				if workingDayInfo.HolidayAdjusted {
					//此天是休息日，但是如果来按休假的那天来计算
					workingDayInfo.RequiredWorkingHours = calcRequiredWorkingHours(workingDayInfo.AdjustedDay, employees[i].AttendanceRules, attendanceData, true)
					workingDayInfo.WorkingHours = calcWorkingHours(workingDayInfo.AdjustedDay, employees[i].AttendanceRules, attendanceData, true, true, false)
				} else {
					//此天可能休息也可能不休息，但是没有调班
					workingDayInfo.RequiredWorkingHours = calcRequiredWorkingHours(day, employees[i].AttendanceRules, attendanceData, isHoliday)
					if isHoliday {
						holiday := holidays[day.Unix()]
						isNationalLegal := false
						if holiday != nil {
							isNationalLegal = holiday.IsNationalLegal
						}
						workingDayInfo.WorkingHours = calcWorkingHours(day, employees[i].AttendanceRules, attendanceData, true, false, isNationalLegal)
					} else {
						workingDayInfo.WorkingHours = calcWorkingHours(day, employees[i].AttendanceRules, attendanceData, false, false, false)
					}
				}

			}
			workingDayInfo.IsWeekend = day.Weekday() == time.Saturday || day.Weekday() == time.Sunday
			overHours := workingDayInfo.WorkingHours - workingDayInfo.RequiredWorkingHours
			if overHours < 0 {
				overHours = 0
			}
			units := int(overHours / 2.5)
			if units > 1 {
				//工作日最多1次
				if !workingDayInfo.IsWeekend && !workingDayInfo.IsHoliday {
					units = 1
				} else {
					//周末必须达标3次
					if units >= 3 {
						units = 3
					} else {
						units = 0
					}
				}
			}
			workingDayInfo.ObtainedUnits = units
			workingDayInfo.AdjustedObtainedUnits = workingDayInfo.ObtainedUnits
			databaseManager.AddWorkingDayInfo(workingDayInfo)
			totalUnits += workingDayInfo.ObtainedUnits
		}
		workingDayStatistics := new(WorkingDayStatistics)
		workingDayStatistics.Id = statisticsId
		workingDayStatistics.Employee = *employees[i]
		workingDayStatistics.DepartmentId = employees[i].DepartmentId
		workingDayStatistics.DepartmentName = createDepartmentName(employees[i].DepartmentId)
		workingDayStatistics.Start = start
		workingDayStatistics.End = end
		workingDayStatistics.RequiredReachedCount = requiredReachedCount
		workingDayStatistics.ReachedCount = totalUnits
		workingDayStatistics.AdjustedReachedCount = totalUnits
		workingDayStatistics.Adjusted = false
		workingDayStatistics.AdjustDescription = ""
		workingDayStatistics.AdjustedBy = ""
		workingDayStatistics.Comment = ""
		workingDayStatistics.Reviewer = ""
		databaseManager.AddWorkingDayStatistics(workingDayStatistics)
		workingDayStatisticsIds = append(workingDayStatisticsIds, workingDayStatistics.Id)
		employeeCount := len(employees)
		progressValue := (i + 1) * 100 / employeeCount
		progressService.AddOrUpdateProgress(progressId, strconv.Itoa(progressValue))
	}
	table := new(StatisticsTable)
	table.Id = NewUUId()
	table.Title = title
	table.RequiredReachedCount = requiredReachedCount
	table.WorkingDayStatisticsIds = workingDayStatisticsIds
	table.CreateTime = time.Now()
	databaseManager.AddStatisticsTable(table)
}

func (service *StatisticsTableService) Delete(sessionId string, statisticsTableId string) {
	CheckSession(sessionId)
	databaseManager.DeleteStatisticsTable(statisticsTableId)
}

func (service *StatisticsTableService) CreateAndDownloadReport(sessionId string, reportTitle string, workingDayStatisticsList []string) string {
	CheckSession(sessionId)
	f := excelize.NewFile()
	oldName := f.GetSheetName(0)
	f.SetSheetName(oldName, reportTitle)
	//写表头
	header := map[string]string{"A": "员工姓名", "B": "部门", "C": "达标次数", "D": "已调整", "E": "调整说明", "F": "备注", "G": "审核人"}
	row := 1
	style, err := f.NewStyle(`{"font":{"bold":true,"color":"#000000"}}`)
	if err == nil {
		f.SetCellStyle(reportTitle, "A1", "G1", style)
	}
	f.SetColWidth(reportTitle, "A", "A", 25)
	f.SetColWidth(reportTitle, "B", "B", 35)
	f.SetColWidth(reportTitle, "C", "D", 10)
	f.SetColWidth(reportTitle, "E", "F", 35)
	f.SetColWidth(reportTitle, "G", "G", 25)
	for k, v := range header {
		f.SetCellValue(reportTitle, k+strconv.Itoa(row), v)
	}
	row++
	for i := 0; i < len(workingDayStatisticsList); i++ {
		statistics := databaseManager.GetWorkingDayStatisticsById(workingDayStatisticsList[i])
		if statistics != nil {
			adjustedStr := "否"
			if statistics.Adjusted {
				adjustedStr = "是"
			}
			f.SetCellValue(reportTitle, "A"+strconv.Itoa(row), statistics.Employee.Name)
			f.SetCellValue(reportTitle, "B"+strconv.Itoa(row), statistics.DepartmentName)
			f.SetCellValue(reportTitle, "C"+strconv.Itoa(row), statistics.AdjustedReachedCount)
			f.SetCellValue(reportTitle, "D"+strconv.Itoa(row), adjustedStr)
			f.SetCellValue(reportTitle, "E"+strconv.Itoa(row), statistics.AdjustDescription)
			f.SetCellValue(reportTitle, "F"+strconv.Itoa(row), statistics.Comment)
			f.SetCellValue(reportTitle, "G"+strconv.Itoa(row), statistics.Reviewer)
			row++
		}
	}
	buffer := new(bytes.Buffer)
	f.Write(buffer)
	f.Close()
	encodeString := base64.StdEncoding.EncodeToString(buffer.Bytes())
	return encodeString
}

func (service *StatisticsTableService) GetMany(sessionId string) []StatisticsTable {
	CheckSession(sessionId)
	statisticsTables := make([]StatisticsTable, 0)
	result := databaseManager.GetStatisticsTableMany()
	for i := 0; i < len(result); i++ {
		statisticsTables = append(statisticsTables, *result[i])
	}
	return statisticsTables
}

type ViewCodeService struct {
}

func (service *ViewCodeService) SendViewCode(sessionId string, title string, receiver string, cc string, contentTemplate string, viewCode ViewCode) {
	CheckSession(sessionId)
	emailSetting := databaseManager.GetEmailSetting()
	if emailSetting == nil {
		var err any = "服务器邮件参数未配置。"
		panic(err)
	}
	//Create url
	url := "http://localhost:8088/viewstatistcs?viewCode=" + viewCode.Code
	domainSetting := databaseManager.GetDomainSetting()
	if domainSetting != nil {
		url = domainSetting.Domain + "/viewstatistcs?viewCode=" + viewCode.Code
	}
	content := contentTemplate
	content = strings.Replace(content, "{URL}", url, -1)
	content = strings.Replace(content, "{ExpiredTime}", viewCode.ExpiredTime.Format("2006-01-02"), -1)
	sendEmail(emailSetting.Host, emailSetting.Port, emailSetting.Sender, emailSetting.SenderPassword, receiver, cc, title, content)
}

func (service *ViewCodeService) Get(code string) ViewCode {
	result := databaseManager.GetViewCode(code)
	if result == nil {
		var err any = "统计码不存在。"
		panic(err)
	}
	return *result
}

func (service *ViewCodeService) Add(sessionId string, viewCode ViewCode) {
	CheckSession(sessionId)
	exist := databaseManager.GetViewCode(viewCode.Code)
	if exist != nil {
		var err any = "统计码已存在。"
		panic(err)
	}
	databaseManager.AddViewCode(&viewCode)
}

type EmailService struct {
}

func (service *EmailService) GetEmailSetting(sessionId string) EmailSetting {
	CheckSession(sessionId)
	emailSetting := databaseManager.GetEmailSetting()
	if emailSetting == nil {
		return EmailSetting{"", "", "", "", ""}
	}
	return *emailSetting
}

func (service *EmailService) AddOrUpdateEmailSetting(sessionId string, setting EmailSetting) {
	CheckSession(sessionId)
	emailSetting := databaseManager.GetEmailSetting()
	if emailSetting == nil {
		databaseManager.AddEmailSetting(&setting)
	} else {
		emailSetting.Host = setting.Host
		emailSetting.Port = setting.Port
		emailSetting.Sender = setting.Sender
		emailSetting.SenderPassword = setting.SenderPassword
		databaseManager.UpdateEmailSetting(emailSetting)
	}
}

type DomainService struct {
}

func (service *DomainService) GetDomainSetting(sessionId string) DomainSetting {
	CheckSession(sessionId)
	domainSetting := databaseManager.GetDomainSetting()
	if domainSetting == nil {
		return DomainSetting{"", ""}
	}
	return *domainSetting
}

func (service *DomainService) AddOrUpdateDomainSetting(sessionId string, setting DomainSetting) {
	CheckSession(sessionId)
	domainSetting := databaseManager.GetDomainSetting()
	if domainSetting == nil {
		databaseManager.AddDomainSetting(&setting)
	} else {
		domainSetting.Domain = setting.Domain
		databaseManager.UpdateDomainSetting(domainSetting)
	}
}

type MessageTemplateService struct {
}

func (service *MessageTemplateService) GetMessageTemplate(sessionId string, name string) MessageTemplate {
	CheckSession(sessionId)
	template := databaseManager.GetMessageTemplate(name)
	if template == nil {
		return MessageTemplate{"", "", ""}
	}
	return *template
}

func (service *MessageTemplateService) DeleteMessageTemplate(sessionId string, name string) {
	CheckSession(sessionId)
	template := databaseManager.GetMessageTemplate(name)
	if template == nil {
		var err any = "模板不存在。"
		panic(err)
	}
	databaseManager.DeleteMessageTemplate(template)
}

func (service *MessageTemplateService) AddOrUpdateGetMessageTemplate(sessionId string, template MessageTemplate) {
	CheckSession(sessionId)
	exist := databaseManager.GetMessageTemplate(template.Name)
	if exist == nil {
		databaseManager.AddMessageTemplate(&template)
	} else {
		exist.Content = template.Content
		databaseManager.UpdateMessageTemplate(exist)
	}
}

type HolidayService struct {
}

func (service *HolidayService) GetMany(sessionId string) []HolidayInfo {
	CheckSession(sessionId)
	holidays := make([]HolidayInfo, 0)
	result := databaseManager.GetHolidayDayInfos(time.Now().AddDate(-1, 0, 0), time.Now().AddDate(1, 0, 0))
	for i := 0; i < len(result); i++ {
		holidays = append(holidays, *result[i])
	}
	return holidays
}

func (service *HolidayService) Add(sessionId string, holiday HolidayInfo) {
	CheckSession(sessionId)
	exist := databaseManager.GetHolidayInfo(holiday.Holiday)
	if exist != nil {
		var err any = "节假日已存在。"
		panic(err)
	}
	databaseManager.AddHolidayInfo(&holiday)
}

func (service *HolidayService) Delete(sessionId string, holiday HolidayInfo) {
	CheckSession(sessionId)
	exist := databaseManager.GetHolidayInfo(holiday.Holiday)
	if exist == nil {
		var err any = "节假日不存在。"
		panic(err)
	}
	databaseManager.DeleteHolidayInfo(holiday.Id)
}

type ProgressService struct {
	progressMap map[string]string
	syncLocker  *sync.Mutex
}

func (service *ProgressService) initialize() {
	service.syncLocker = new(sync.Mutex)
	service.progressMap = make(map[string]string)
}

func (service *ProgressService) AddOrUpdateProgress(progressId string, progressValue string) {
	service.syncLocker.Lock()
	service.progressMap[progressId] = progressValue
	service.syncLocker.Unlock()
}

func (service *ProgressService) GetProgress(progressId string) string {
	service.syncLocker.Lock()
	if value, ok := service.progressMap[progressId]; ok {
		service.syncLocker.Unlock()
		return value
	} else {
		service.syncLocker.Unlock()
		return "0"
	}
}

func (service *ProgressService) DeleteProgress(progressId string) {
	service.syncLocker.Lock()
	delete(service.progressMap, progressId)
	service.syncLocker.Unlock()
}
