package service

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

type Staff struct {
	info   *model.StaffInfo
	config *model.StaffConfig
}

func NewStaff(info *model.StaffInfo, config *model.StaffConfig) *Staff {
	return &Staff{
		info:   info,
		config: config,
	}
}

func (s *Staff) GetSlaveSalary(month string) float64 {
	slaveSalarys, err := dao.NewStaffSalaryDao().Query(month, "", &s.info.Username)
	if err != nil {
		log.Errorf("db error :%s", err.Error())
		return 0
	}

	totalSumSalary := float64(0)
	if len(slaveSalarys) != 0 {
		for _, slaveSalary := range slaveSalarys {
			totalSumSalary += slaveSalary.BaseSalary
		}
	}
	return totalSumSalary
}

func (s *Staff) CalcSlaveSalary(salary *model.StaffSalary, month string) (*model.StaffSalary, error) {
	if salary == nil {
		salary = new(model.StaffSalary)
		salary.Month = month
		salary.Username = s.info.Username
	}
	salary.BaseSalary = cast.ToFloat64(s.info.BaseSalary)
	salary.Master = s.info.Master
	extra := make(map[string]interface{})
	extra["entryDate"] = s.info.EntryDate //展示排序依赖
	salary.Extra = goutil.EncodeJSON(extra)

	return salary, nil
}

func (s *Staff) CalcSalary(salary *model.StaffSalary, month string) (*model.StaffSalary, error) {
	staff := s.info
	if salary == nil {
		salary = new(model.StaffSalary)
		salary.Month = month
		salary.Username = s.info.Username
	}

	entryTime, _ := time.ParseInLocation("2006-01-02", staff.EntryDate, time.Local)
	leaveTime, _ := time.ParseInLocation("2006-01-02", staff.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)

	// 试用期折扣
	discount := s.getDiscount(month)

	//社保和公积金
	socialInsurence := goutil.If(s.config.Exist(model.StaffSalarySocialInsurence), cast.ToFloat64(s.config.Get(model.StaffSalarySocialInsurence)), config.SalarySocialInsurence)
	//houseFund := goutil.If(s.config.Exist(model.StaffSalaryHouseFund), cast.ToFloat64(s.config.Get(model.StaffSalaryHouseFund)), config.SalaryHouseFund)
	houseFund := cast.ToFloat64(s.config.Get(model.StaffSalaryHouseFund))
	if isEntryMonth { //入职月不买社保
		socialInsurence = 0
		houseFund = 0
	}

	holiday, surplusHoliday := s.getRealVacationDay(staff.Username, month)
	totalDays := s.getTotalWorkDay(staff.Username, month)
	realWorkDays := s.getRealWorkDay(staff.EntryDate, staff.LeaveDate, staff.Username, month)
	approvalCheckinDay := s.getApprovalCheckinDay(staff.Username, month)

	// 入职月或离职月以大多数人的出勤天数为准
	if isEntryMonth || isLeaveMonth {
		totalDays = s.getTotalWorkDayMax(month)
	}

	salary.BaseSalary = cast.ToFloat64(staff.BaseSalary)

	realWorkDays = realWorkDays + surplusHoliday
	atttendRate := float64(realWorkDays) / float64(totalDays)
	attendSalary := salary.BaseSalary * atttendRate * discount
	staffSalaryPerDay := cast.ToFloat64(s.config.Get(model.StaffSalaryPerDay))
	if staffSalaryPerDay > 0 {
		attendSalary = staffSalaryPerDay * realWorkDays
	}

	extra := make(map[string]interface{})
	extra["approvalCheckinDay"] = approvalCheckinDay //展示依赖
	extra["entryDate"] = staff.EntryDate             //展示排序依赖

	awardSalay, perfSalary := s.getExtraSalary(month)

	salary.Holiday = holiday
	salary.PerfSalary = perfSalary
	salary.AwardSalary = awardSalay
	salary.AttendSalary = butil.FloatCut(attendSalary)
	salary.RealSalary = salary.GetRealSalary()
	salary.SocialInsurence = socialInsurence
	salary.HouseFund = houseFund
	salary.Holiday = holiday
	salary.TotalDay = float64(totalDays)
	salary.RealDay = realWorkDays
	salary.Extra = goutil.EncodeJSONIndent(extra)

	return salary, nil
}

func (s *Staff) getExtraSalary(month string) (float64, float64) {
	monthTime, _ := time.ParseInLocation("200601", month, time.Local)
	startDay := monthTime.Format("2006-01-02")
	endDay := monthTime.AddDate(0, 1, -1).Format("2006-01-02")
	datas, err := dao.NewUnifyAdData().QueryOwnerData(s.info.Username, startDay, endDay)
	if err != nil {
		log.Errorf("db error :%s", err.Error())
		return 0, 0
	}
	sumProfitAmount := int64(0)
	for _, data := range datas {
		sumProfitAmount += data.PayAmount - data.Cost
	}
	totalProfit := cast.ToFloat64(goutil.FormatMoney(sumProfitAmount))

	//1算提成
	awardSalary := float64(0)
	if s.config.Exist(model.StaffSalaryProfitDeduct) {
		deductRate := cast.ToFloat64(s.config.Get(model.StaffSalaryProfitDeduct))
		awardSalary = totalProfit * deductRate
		if awardSalary < 0 {
			awardSalary = 0
		}
	}

	//2算绩效
	perfSalary := cast.ToFloat64(s.info.PerfSalary)
	if s.config.Exist(model.StaffSalaryProfitTarget) {
		profitTarget := cast.ToFloat64(s.config.Get(model.StaffSalaryProfitTarget))
		if totalProfit < 0 {
			perfSalary = 0
			awardSalary = 0
		} else if totalProfit < profitTarget {
			perfSalary = perfSalary * (totalProfit / profitTarget)
			awardSalary = 0
		}
	}
	return awardSalary, perfSalary
}

func (s *Staff) getDiscount(month string) float64 {
	officalTime, _ := time.ParseInLocation("2006-01-02", s.info.OfficialDate, time.Local)
	monthTime, _ := time.ParseInLocation("200601", month, time.Local)
	startDate := cast.ToInt(monthTime.Format("20060102"))
	endDate := cast.ToInt(monthTime.AddDate(0, 1, -1).Format("20060102"))
	officialDate := cast.ToInt(strings.ReplaceAll(s.info.OfficialDate, "-", ""))
	isOfficialMonth := goutil.If(cast.ToInt(officalTime.Format("200601")) == cast.ToInt(month), true, false)

	// 试用期折扣
	discount := cast.ToFloat64(s.config.Get(model.StaffSalaryExpDiscount))
	if discount == 0.0 {
		discount = 0.8
	}
	if cast.ToInt(monthTime.Format("200601")) > cast.ToInt(officalTime.Format("200601")) {
		discount = 1.0
	}
	if discount < 1.0 && isOfficialMonth {
		totalMonthDay := float64(endDate - startDate + 1)
		discount = discount*float64(officialDate-startDate)/totalMonthDay + 1*float64(endDate-officialDate+1)/totalMonthDay
	}
	return discount
}

func (s *Staff) getRealVacationDay(username, month string) (float64, float64) {
	// 休假申请
	approveVacations, err := dao.NewApprovalVacationDao().GetByUsername(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 *Staff) getApprovalCheckinDay(username, month string) int {
	approvalDay := int(0)
	userCheckins, err := dao.NewCheckinDao().Query(username, month, false)
	if err != nil {
		log.Errorf("db error :%s", err.Error())
		return approvalDay
	}
	for _, checkin := range userCheckins {

		approvalCheckin, _ := dao.NewApprovalCheckinDao().GetByUsernameDay(username, checkin.Day)
		if approvalCheckin != nil {
			approvalDay += 1
		}

	}
	return approvalDay
}

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

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

	//离职当天算工作日
	leaveTime, _ := time.ParseInLocation("2006-01-02", 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
		}

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

		////有请假申请的不算出勤
		approvalVacation, _ := dao.NewApprovalVacationDao().GetByUsernameDay(username, checkin.Day)
		if approvalVacation != nil {
			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 *Staff) getTotalWorkDay(username, month string) int64 {
	checkins, _ := dao.NewCheckinDao().Query(username, month, false)
	return int64(len(checkins))
}

func (s *Staff) getTotalWorkDayMax(month string) int64 {
	// 最多人数的应出勤天数 为真正的出勤天数
	userCounts, err := dao.NewCheckinDao().CountUsername(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

}