package cn.com.ty.lift.enterprise.oa.controller;

import cn.com.ty.lift.common.model.CountPage;
import cn.com.ty.lift.common.utils.DateUtils;
import cn.com.ty.lift.common.utils.ValuePool;
import cn.com.ty.lift.common.verify.Val;
import cn.com.ty.lift.common.verify.Validate;
import cn.com.ty.lift.common.verify.Validation;
import cn.com.ty.lift.enterprise.oa.dto.AttendanceRequest;
import cn.com.ty.lift.enterprise.oa.dto.AttendanceResponse;
import cn.com.ty.lift.enterprise.oa.entity.Attendance;
import cn.com.ty.lift.enterprise.oa.entity.MaintenanceCompany;
import cn.com.ty.lift.enterprise.oa.service.AttendanceImgService;
import cn.com.ty.lift.enterprise.oa.service.AttendanceService;
import cn.com.ty.lift.enterprise.oa.service.MaintenanceCompanyService;
import cn.com.xwy.boot.web.dto.RestResponse;
import cn.hutool.core.io.IoUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * <p>
 * 打卡签到记录 前端控制器
 * </p>
 *
 * @author wcz
 * @since 2019-12-04
 */
@Slf4j
@RestController
@AllArgsConstructor
@RequestMapping("/oa/attendance")
public class AttendanceController {

    private AttendanceService         attendanceService;
    private AttendanceImgService      attendanceImgService;
    private MaintenanceCompanyService maintenanceCompanyService;

    private static final Map<String, String> headerAlias = new HashMap<>();

    static {
        headerAlias.put("username", "姓名");
        headerAlias.put("typeDesc", "打卡类型");
        headerAlias.put("clockDate", "日期");
        headerAlias.put("requiredTime", "应打卡时间");
        headerAlias.put("clockTime", "打卡时间");
        headerAlias.put("address", "地点");
        headerAlias.put("remarks", "备注");
        headerAlias.put("statusDesc", "是否迟到/早退");
    }

    /**
     * 根据id查找
     *
     * @param request AttendanceRequest
     * @return RestResponse
     */
    @PostMapping("findOne")
    @Validation(fields = {"id"})
    public RestResponse findOne(@Val @RequestBody AttendanceRequest request) {
        AttendanceResponse result = attendanceService.infoById(request);
        if (Objects.nonNull(result)) {
            result.setImgs(attendanceImgService.listByAttendance(request.getId()));
        }
        return RestResponse.success(result);
    }

    /**
     * 根据条件查询
     *
     * @param request AttendanceRequest
     * @return RestResponse
     */
    @PostMapping("list")
    @Validation(fields = "mtCompanyId")
    public RestResponse list(@Val @RequestBody AttendanceRequest request) {
        //签到管理: 按日期和打卡时间由近及远排序
        LocalDate begin = request.getBegin();
        LocalDate end = request.getEnd();
        if(Objects.nonNull(begin) && Objects.nonNull(end)){
            Validate.isTrue(begin.isBefore(end), ValuePool.beginTimeToEndTimeIllegal(begin, end));
        }
        request.addDesc("create_date");
//        if (Objects.isNull(request.getClockTime())) {
//            request.setBegin(LocalDate.now().plusDays(0 - DateUtils.daysOfYear()));
//            request.setEnd(LocalDate.now());
//        }
        IPage<AttendanceResponse> pages = attendanceService.pageByCondition(request);
        long count = attendanceService.countByMtCompany(request.getMtCompanyId());
        return RestResponse.success(CountPage.getCountPage(count, pages));
    }

    /**
     * 设置打卡时间
     *
     * @param request AttendanceRequest
     * @return RestResponse
     */
    @PostMapping("setupTime")
    @Validation(fields = {"mtCompanyId"})
    public RestResponse setupTime(@Val @RequestBody AttendanceRequest request) {
        LocalTime topTime = request.getTopTime();
        LocalTime downTime = request.getDownTime();
        boolean top = Objects.nonNull(topTime);
        boolean down = Objects.nonNull(downTime);
        boolean am = top && down;
        //上下班时间成对设置
        Validate.notTrue(top ^ down, ValuePool.ATTEND_SET_AM_TIME);
        if (am) {
            Validate.notTrue(topTime.isAfter(downTime), ValuePool.attendInvalidAmTime(topTime, downTime));
        }

        LocalTime topTimePm = request.getTopTimePm();
        LocalTime downTimePm = request.getDownTimePm();
        boolean topPm = Objects.nonNull(topTimePm);
        boolean downPm = Objects.nonNull(downTimePm);
        boolean pm = topPm && downPm;
        Validate.notTrue(topPm ^ downPm, ValuePool.ATTEND_SET_PM_TIME);
        if (pm) {
            Validate.notTrue(topTimePm.isAfter(downTimePm), ValuePool.attendInvalidPmTime(topTimePm, downTimePm));
        }
        //上午下班 < 下午上班
        if (down && topPm) {
            Validate.notTrue(downTime.isAfter(topTimePm), ValuePool.attendAmDownPmTop(downTime, topTimePm));
        }

        LocalTime topTimeOt = request.getTopTimeOt();
        LocalTime downTimeOt = request.getDownTimeOt();
        boolean topOt = Objects.nonNull(topTimeOt);
        boolean downOt = Objects.nonNull(downTimeOt);
        boolean ot = topOt && downOt;
        Validate.notTrue(topOt ^ downOt, ValuePool.ATTEND_SET_OT_TIME);
        if (ot) {
            Validate.notTrue(topTimeOt.isAfter(downTimeOt), ValuePool.attendInvalidOtTime(topTimeOt, downTimeOt));
        }
        // 下午下班 < 加班上班
        if (downPm && topOt) {
            Validate.notTrue(downTimePm.isAfter(topTimeOt), ValuePool.attendPmDownOtTop(downTimePm, topTimeOt));
        }
        //三组时间至少设置一组
        Validate.isTrue(am || pm || ot, ValuePool.ATTEND_MISSING_TIME);

        MaintenanceCompany entity = maintenanceCompanyService.getById(request.getMtCompanyId());
        Validate.notNull(entity, ValuePool.ATTEND_MAINTAIN_COMPANY_MISSING);

        entity.setTopTime(topTime);
        entity.setDownTime(downTime);
        entity.setTopTimePm(topTimePm);
        entity.setDownTimePm(downTimePm);
        entity.setTopTimeOt(topTimeOt);
        entity.setDownTimeOt(downTimeOt);

        boolean result = maintenanceCompanyService.updateById(entity);
        return RestResponse.success(result);
    }

    /**
     * 打卡
     * 11 上午上班 12 上午下班
     * 21 下午上班 22 下午下班
     * 31 加班上班 32 加班下班
     *
     * @param entity Attendance
     * @return RestResponse
     */
    @PostMapping("clock")
    public RestResponse clock(@Valid @RequestBody Attendance entity) {
//        String coordinate = entity.getCoordinate();
//        boolean ret = VerifyUtils.preJudgment(coordinate);
//        if (!ret)
//            return RestResponse.fail("请输入正确的经纬度");
        Long mtCompanyId = entity.getMtCompanyId();
        Long userId = entity.getUserId();
        Integer type = entity.getType();
        //先统计当天是否有对应的打卡记录
        Attendance same = attendanceService.sameByUserAndType(mtCompanyId, userId, type);
        if(Objects.nonNull(same)){
            LocalDateTime createDate = same.getCreateDate();
            String message = Objects.isNull(createDate) ? "" : String.format("(打卡时间：%tF %tT)", createDate, createDate);
            return RestResponse.fail(ValuePool.ATTEND_HAD_CLOCK + message);
        }
        MaintenanceCompany company = maintenanceCompanyService.getById(mtCompanyId);
        Validate.notNull(company, ValuePool.ATTEND_MAINTAIN_COMPANY_MISSING);

        LocalTime now = LocalTime.now();
        //状态（是否迟到早退，0：否，1：是）
        switch (type) {
            case 11:
                LocalTime topTime = company.getTopTime();
                Validate.notNull(topTime, ValuePool.ATTEND_SET_AM_TIME);
                //打卡时间比上班时间早-正常，否则迟到
                entity.setStatus(now.isBefore(topTime) ? 0 : 1);
                entity.setRemarks(ValuePool.ATTEND_AM_TOP_CLOCK);
                break;
            case 12:
                LocalTime downTime = company.getDownTime();
                Validate.notNull(downTime, ValuePool.ATTEND_SET_AM_TIME);
                //打卡时间比下班时间晚-正常，否则早退
                entity.setStatus(now.isAfter(downTime) ? 0 : 1);
                entity.setRemarks(ValuePool.ATTEND_AM_DOWN_CLOCK);
                break;
            case 21:
                LocalTime topTimePm = company.getTopTimePm();
                Validate.notNull(topTimePm, ValuePool.ATTEND_SET_PM_TIME);
                entity.setStatus(now.isBefore(topTimePm) ? 0 : 1);
                entity.setRemarks(ValuePool.ATTEND_PM_TOP_CLOCK);
                break;
            case 22:
                LocalTime downTimePm = company.getDownTimePm();
                Validate.notNull(downTimePm, ValuePool.ATTEND_SET_PM_TIME);
                entity.setStatus(now.isAfter(downTimePm) ? 0 : 1);
                entity.setRemarks(ValuePool.ATTEND_PM_DOWN_CLOCK);
                break;
            case 31:
                LocalTime topTimeOt = company.getTopTimeOt();
                Validate.notNull(topTimeOt, ValuePool.ATTEND_SET_OT_TIME);
                entity.setStatus(now.isBefore(topTimeOt) ? 0 : 1);
                entity.setRemarks(ValuePool.ATTEND_OT_TOP_CLOCK);
                break;
            case 32:
                LocalTime downTimeOt = company.getDownTimeOt();
                Validate.notNull(downTimeOt, ValuePool.ATTEND_SET_OT_TIME);
                entity.setStatus(now.isAfter(downTimeOt) ? 0 : 1);
                entity.setRemarks(ValuePool.ATTEND_OT_DOWN_CLOCK);
                break;
            default:
                return RestResponse.fail(ValuePool.ATTEND_UNKNOWN_TYPE);
        }
        boolean result = attendanceService.save(entity);
        return RestResponse.success(result);
    }

    /**
     * 返回维保公司打卡时间信息
     *
     * @param request AttendanceRequest
     * @return RestResponse
     */
    @PostMapping("time")
    @Validation(fields = {"mtCompanyId"})
    public RestResponse time(@Val @RequestBody AttendanceRequest request) {
        MaintenanceCompany entity = maintenanceCompanyService.getById(request.getMtCompanyId());
        return RestResponse.success(entity);
    }

    /**
     * 根据用户查询打卡记录
     *
     * @param request AttendanceRequest
     * @return RestResponse
     */
    @PostMapping("pageByUser")
    @Validation(fields = {"userId", "clockTime"})
    public RestResponse pageByUser(@Val @RequestBody AttendanceRequest request) {
        request.addDesc("create_date");
        if (Objects.isNull(request.getClockTime())) {
            request.setBegin(LocalDate.now().plusDays(0 - DateUtils.daysOfYear()));
            request.setEnd(LocalDate.now());
        }
        IPage<AttendanceResponse> pages = attendanceService.pageByUser(request);
        return RestResponse.success(pages);
    }

    @PostMapping("export")
    public void export(@Val @RequestBody AttendanceRequest request, HttpServletResponse response) {
        log.info("headerAlias: {}", headerAlias);
        List<AttendanceResponse> attendances = attendanceService.listByIdList(request);
        Validate.notNull(attendances, ValuePool.ATTEND_MISSING);
        try {
            ExcelWriter writer = ExcelUtil.getWriter();
            //设置列别名
            headerAlias.forEach(writer::addHeaderAlias);
            //只导出配置了别名的列属性
            writer.setOnlyAlias(true);
            writer.write(attendances, true);
            writer.autoSizeColumnAll();
            response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + IdWorker.getIdStr() + ".xlsx");
            ServletOutputStream out = response.getOutputStream();
            writer.flush(out, true);
            writer.close();
            IoUtil.close(out);
        } catch (Exception e) {
            log.error("导出打卡记录异常", e);
            throw Validate.validateException("导出打卡记录异常");
        }
    }
}
