package service

import (
	butil "enterprise/base/util"
	"enterprise/common/dao"
	"enterprise/common/model"
	"enterprise/common/registry"
	log "github.com/sirupsen/logrus"
	"github.com/smbrave/goutil"
	"github.com/spf13/cast"
	"math"
	"strings"
	"time"
)

type StaffSalary struct {
	user *model.StaffUser
}

func NewStaffSalary(user *model.StaffUser) *StaffSalary {
	return &StaffSalary{
		user: user,
	}
}

func (s *StaffSalary) CalcSalary(salary *model.StaffSalary, month string) (*model.StaffSalary, error) {
	corp, err := dao.NewCorpDao().Get(s.user.CorpId)
	if err != nil {
		return nil, err
	}
	userSalary := s.user.GetSalary()
	userConfig := s.user.GetConfig()
	corpConfig := corp.GetConfig()
	if userSalary.Base == "" && userSalary.Target == "" {
		return nil, nil
	}
	if salary == nil {
		salary = new(model.StaffSalary)
		salary.Month = month
		salary.CorpId = s.user.CorpId
		salary.UserId = s.user.Id
		salary.Username = s.user.Username
	}

	entryTime, _ := time.ParseInLocation("2006-01-02", s.user.EntryDate, time.Local)
	leaveTime, _ := time.ParseInLocation("2006-01-02", s.user.LeaveDate, time.Local)
	isEntryMonth := goutil.If(cast.ToInt(entryTime.Format("200601")) == cast.ToInt(month), true, false)
	isLeaveMonth := goutil.If(cast.ToInt(leaveTime.Format("200601")) == cast.ToInt(month), true, false)

	//社保和公积金
	salary.SocialDeduct = 0
	salary.HouseDeduct = 0
	if isEntryMonth { //入职月不买社保
		salary.SocialDeduct = 0
		salary.HouseDeduct = 0
	}

	salary.ShouldDay = int(s.getTotalWorkDay(s.user.CorpId, s.user.Username, month))
	// 入职月或离职月以大多数人的出勤天数为准
	if isEntryMonth || isLeaveMonth {
		salary.ShouldDay = int(s.getTotalWorkDayMax(s.user.CorpId, month))
	}

	holiday, surplusHoliday := s.getRealVacationDay(month)
	realWorkDays := s.getRealWorkDay(month)
	approvalCheckinDay := s.getApprovalCheckinDay(s.user.CorpId, s.user.Username, month)

	salary.HolidayDay = holiday
	salary.AttendDay = realWorkDays + surplusHoliday
	salary.Salary = cast.ToFloat64(userSalary.Base) + cast.ToFloat64(userSalary.Target)
	salary.SocialDeduct = goutil.If(userConfig.SocialDeduct != "", cast.ToFloat64(userConfig.SocialDeduct), cast.ToFloat64(corpConfig.SocialDeduct))
	salary.HouseDeduct = goutil.If(userConfig.HouseDeduct != "", cast.ToFloat64(userConfig.HouseDeduct), cast.ToFloat64(corpConfig.HouseDeduct))

	extra := make(map[string]interface{})
	extra["approvalCheckinDay"] = approvalCheckinDay //展示依赖
	extra["entryDate"] = s.user.EntryDate            //展示排序依赖
	extra["leaveDate"] = s.user.LeaveDate            //展示排序依赖
	extra["baseSalary"] = userSalary.Base
	extra["targetSalary"] = userSalary.Target

	salary.Extra = goutil.EncodeJSONIndent(extra)

	calculator := registry.NewSalaryCalculator(corp, s.user)
	if calculator != nil {
		calculator.Calculate(salary)
	}
	s.formatFloat(salary)
	return salary, nil
}

func (s *StaffSalary) formatFloat(salary *model.StaffSalary) {
	salary.AwardSalary = butil.FloatCut(salary.AwardSalary)
	salary.AttendSalary = butil.FloatCut(salary.AttendSalary)
	salary.TargetSalary = butil.FloatCut(salary.TargetSalary)
	salary.OtherSalary = butil.FloatCut(salary.OtherSalary)
	salary.AttendDay = butil.FloatCut(salary.AttendDay)
	salary.HolidayDay = butil.FloatCut(salary.HolidayDay)
}

func (s *StaffSalary) getRealVacationDay(month string) (float64, float64) {
	// 休假申请
	approveVacations, err := dao.NewApprovalVacationDao().GetByUsername(s.user.CorpId, s.user.Username, month, "")
	if err != nil {
		log.Errorf("db error :%s", err.Error())
		return 0, 0
	}

	holiday := float64(0)
	surplusHoliday := float64(0)
	for _, vac := range approveVacations {
		startTime, _ := time.ParseInLocation("2006-01-02 15:04:05", vac.VacationStartTime, time.Local)
		endTime, _ := time.ParseInLocation("2006-01-02 15:04:05", vac.VacationEndTime, time.Local)

		//同一天请假时长大于8小时算一天
		if startTime.Format("2006-01-02") == endTime.Format("2006-01-02") && vac.VacationDuration > 1 {
			holiday += 1
		} else {
			holiday += vac.VacationDuration
			//不是整天数,把剩余的算上
			span := vac.VacationDuration - math.Floor(vac.VacationDuration)
			surplusHoliday += goutil.If(math.Abs(span) < 0.000001, 0, 1-span)
		}
	}
	return holiday, surplusHoliday
}

func (s *StaffSalary) getApprovalCheckinDay(corpId int64, username, month string) int {
	approvalDay := int(0)
	userCheckins, err := dao.NewCheckinDao().Query(corpId, username, month, false)
	if err != nil {
		log.Errorf("db error :%s", err.Error())
		return approvalDay
	}
	for _, checkin := range userCheckins {
		approvalCheckin, _ := dao.NewApprovalCheckinDao().GetByUsernameDay(corpId, username, checkin.Day)
		if approvalCheckin != nil {
			approvalDay += 1
		}

	}
	return approvalDay
}

func (s *StaffSalary) getRealWorkDay(month string) float64 {
	realWorkdays := float64(0)
	userCheckins, err := dao.NewCheckinDao().Query(s.user.CorpId, s.user.Username, month, false)
	if err != nil {
		log.Errorf("db error :%s", err.Error())
		return realWorkdays
	}
	approvalCheckinDay := 0

	//入职当天算工作日
	entryTime, _ := time.ParseInLocation("2006-01-02", s.user.EntryDate, time.Local)
	if entryTime.Format("200601") == month {
		realWorkdays += 1
	}

	//离职当天算工作日
	leaveTime, _ := time.ParseInLocation("2006-01-02", s.user.LeaveDate, time.Local)
	if leaveTime.Format("200601") == month {
		realWorkdays += 1
	}

	for _, checkin := range userCheckins {

		//入职离职当天已经算过了
		if entryTime.Format("2006-01-02") == checkin.Day || leaveTime.Format("2006-01-02") == checkin.Day {
			continue
		}

		if checkin.Exception == "" {
			realWorkdays += 1
			continue
		}

		//有请假申请的不算出勤
		approvalVacation, _ := dao.NewApprovalVacationDao().GetByUsernameDay(s.user.CorpId, s.user.Username, checkin.Day)
		if approvalVacation != nil {
			continue
		}

		//有补卡申请就直接算出勤
		approvalCheckin, _ := dao.NewApprovalCheckinDao().GetByUsernameDay(s.user.CorpId, s.user.Username, checkin.Day)
		if approvalCheckin != nil {
			realWorkdays += 1
			approvalCheckinDay += 1
			continue
		}

		// 迟到的按时间折算
		if strings.Contains(checkin.Exception, "上班打卡:时间异常") {
			stTime := time.Unix(checkin.StartTime, 0)
			later := float64(stTime.Hour() - 8) //迟到小时数,从9点算
			if later > 8 {
				later = 8
			}
			realWorkdays += (8 - later) / 8
			continue
		}
	}
	return realWorkdays
}

func (s *StaffSalary) getTotalWorkDay(corpId int64, username, month string) int64 {
	checkins, _ := dao.NewCheckinDao().Query(corpId, username, month, false)
	return int64(len(checkins))
}

func (s *StaffSalary) getTotalWorkDayMax(corpId int64, month string) int64 {
	// 最多人数的应出勤天数 为真正的出勤天数
	userCounts, err := dao.NewCheckinDao().CountUsername(corpId, month)
	if err != nil {
		log.Errorf("db error :%s", err.Error())
		return 0
	}
	mp := make(map[int64]int)
	totalDays := int64(0)
	for _, uc := range userCounts {
		mp[uc.Count] += 1
	}
	minCount := 0
	for k, v := range mp {
		if v > minCount {
			minCount = v
			totalDays = k
		}
	}
	return totalDays

}