package com.ningdatech.carapi.industry.manage;

import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;

import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ningdatech.basic.exception.BizException;
import com.ningdatech.basic.exception.code.ExceptionCode;
import com.ningdatech.basic.model.PageVo;
import com.ningdatech.basic.util.NdDateUtils;
import com.ningdatech.carapi.archives.enums.ApproveStateEnum;
import com.ningdatech.carapi.common.aop.BuildChildrenRegionWrapper;
import com.ningdatech.carapi.common.constant.CommonConstant;
import com.ningdatech.carapi.common.util.CodeUtil;
import com.ningdatech.carapi.common.util.ExcelDownUtil;
import com.ningdatech.carapi.common.util.ExcelExportStyle;
import com.ningdatech.carapi.datascope.aop.LambdaDataScope;
import com.ningdatech.carapi.datascope.contants.LambdaDataScopeTypeEnum;
import com.ningdatech.carapi.driver.entity.Certificate;
import com.ningdatech.carapi.driver.entity.DriverInfo;
import com.ningdatech.carapi.driver.service.ICertificateService;
import com.ningdatech.carapi.driver.service.IDriverInfoService;
import com.ningdatech.carapi.industry.contants.CertificateContant;
import com.ningdatech.carapi.industry.entity.DriverTrainingCertificateJoin;
import com.ningdatech.carapi.industry.entity.DriverTrainingCertificateOper;
import com.ningdatech.carapi.industry.entity.DriverTrainingCertificateOperJoin;
import com.ningdatech.carapi.industry.model.dto.DriverTrainingCertificateExportDTO;
import com.ningdatech.carapi.industry.model.dto.DriverTrainingCertificatePageParamDTO;
import com.ningdatech.carapi.industry.model.dto.DriverTrainingCertificateUpdateDTO;
import com.ningdatech.carapi.industry.model.vo.DriverTrainingCertificateOperVO;
import com.ningdatech.carapi.industry.model.vo.DriverTrainingCertificateVO;
import com.ningdatech.carapi.industry.service.IDriverTrainingCertificateJoinService;
import com.ningdatech.carapi.industry.service.IDriverTrainingCertificateOperJoinService;
import com.ningdatech.carapi.industry.service.IDriverTrainingCertificateOperService;
import com.ningdatech.carapi.sys.helper.RegionsCacheHelper;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
 * @Classname DriverTrainingCertificateManage
 * @Description
 * @Date 2022/11/3 17:57
 * @Created by PoffyZhang
 */
@Component
@Slf4j
@AllArgsConstructor
public class DriverTrainingCertificateManage {

    private final IDriverTrainingCertificateJoinService driverTrainingCertificateJoinService;
    private final IDriverTrainingCertificateOperService driverTrainingCertificateOperService;
    private final IDriverTrainingCertificateOperJoinService driverTrainingCertificateOperJoinService;

    private final ICertificateService certificateService;
    private final IDriverInfoService driverInfoService;
    private final RegionsCacheHelper regionCacheHelper;

    /**
     *  合格证信息列表
     */
    public PageVo<DriverTrainingCertificateVO> infoList(DriverTrainingCertificatePageParamDTO param) {
        Page<DriverTrainingCertificateJoin> page = param.page();
        LambdaQueryWrapper<DriverTrainingCertificateJoin> query
                = Wrappers.lambdaQuery(DriverTrainingCertificateJoin.class);
        DriverTrainingCertificateManage context = (DriverTrainingCertificateManage) AopContext.currentProxy();
        context.search(page,query,param);
        if(CollUtil.isEmpty(page.getRecords())){
            return PageVo.empty();
        }
        List<DriverTrainingCertificateVO> data = page.getRecords().stream().map(w -> convert(w)).collect(Collectors.toList());
        return PageVo.of(data, page.getTotal());
    }

    public DriverTrainingCertificateVO infoDetails(Long id) {

        LambdaQueryWrapper<Certificate> wrapper = Wrappers.lambdaQuery(Certificate.class);
        DriverTrainingCertificateManage context = (DriverTrainingCertificateManage) AopContext.currentProxy();
        context.buildCertificateQuery(wrapper, id);
        Certificate certificate = certificateService.getOne(wrapper);
        if (Objects.isNull(certificate)){
            return null;
        }
        //Certificate certificate = certificateService.getById(id);
        DriverTrainingCertificateOper oper = driverTrainingCertificateOperService
                .getOne(Wrappers.lambdaQuery(DriverTrainingCertificateOper.class)
                .eq(DriverTrainingCertificateOper::getCertificateId, id)
                .last("limit 1"));

        DriverInfo driverInfo = driverInfoService.getById(certificate.getDriverId());

        DriverTrainingCertificateVO vo = new DriverTrainingCertificateVO();
        vo.setId(id);
        vo.setDriverId(certificate.getDriverId());
        vo.setDriverName(certificate.getDriverName());
        vo.setIdCard(driverInfo.getIdCard());
        vo.setCertificateNo(certificate.getCertificateNo());
        if(Objects.nonNull(oper)){
            vo.setCertificateType(oper.getOperationTypeId());
            vo.setCertificateTypeName(oper.getOperationTypeName());
        }

        vo.setIssuedBy(certificate.getIssuedBy());
        vo.setIssueDate(certificate.getIssueDate());
        vo.setDriverLicenseNo(certificate.getDriverLicenseNo());
        vo.setExpiryDate(certificate.getExpiryDate());
        vo.setCompanyId(certificate.getCompanyId());
        vo.setCompanyName(certificate.getCompanyName());
        vo.setRemark(certificate.getRemark());
        return vo;
    }

    @LambdaDataScope(type = LambdaDataScopeTypeEnum.ALL)
    public void buildCertificateQuery(LambdaQueryWrapper<Certificate> wrapper, Long id) {
        wrapper.eq(Certificate::getId,id);
    }

    /**
     *  合格证申请列表
     */
    public PageVo<DriverTrainingCertificateOperVO> operationList(DriverTrainingCertificatePageParamDTO param, Integer operationTypeAppcation) {
        Page<DriverTrainingCertificateOperJoin> page = param.page();
        LambdaQueryWrapper<DriverTrainingCertificateOperJoin> wrapper = Wrappers.lambdaQuery(DriverTrainingCertificateOperJoin.class);
        DriverTrainingCertificateManage context = (DriverTrainingCertificateManage) AopContext.currentProxy();
        context.searchJoin(page,wrapper,param,operationTypeAppcation);
        if(CollUtil.isEmpty(page.getRecords())){
            return PageVo.empty();
        }
        List<DriverTrainingCertificateOperVO> data = page.getRecords().stream().map(w -> convertOper(w)).collect(Collectors.toList());
        return PageVo.of(data, page.getTotal());
    }

    public DriverTrainingCertificateOperVO approve(Long id, Long userId) {

        LambdaQueryWrapper<DriverTrainingCertificateOper> wrapper = Wrappers.lambdaQuery(DriverTrainingCertificateOper.class);
        DriverTrainingCertificateManage context = (DriverTrainingCertificateManage) AopContext.currentProxy();
        context.buildQuery(wrapper, id);
        DriverTrainingCertificateOper oper = driverTrainingCertificateOperService.getOne(wrapper);
        if(Objects.isNull(oper)){
            throw new BizException(ExceptionCode.OPERATION_EX.getCode(), "此id证书操作不存在");
        }
        oper.setUpdateBy(userId);
        oper.setAuditState(ApproveStateEnum.AUDIT_SUCESS.getCode());
        oper.setAuditStateName(ApproveStateEnum.AUDIT_SUCESS.getDesc());
        driverTrainingCertificateOperService.updateById(oper);
        // 将审核通过的证书信息保存到证书表中
        // 如果是新增证书，审核后先判断合格证列表中是否已经存在该驾驶员的合格证信息
        Long driverId = oper.getDriverId();
        if (oper.getOperationTypeId().equals(CertificateContant.OPERATION_TYPE_APPCATION)){
            List<Certificate> certificateList = certificateService.list(Wrappers.lambdaQuery(Certificate.class)
                    .eq(Certificate::getDriverId, driverId)
                    .gt(Certificate::getExpiryDate, LocalDateTime.now()));
            if (CollectionUtil.isNotEmpty(certificateList)) {
                throw new BizException("该驾驶员证书已上传");
            }
        }
        DriverInfo driverInfo = driverInfoService.getById(driverId);
        Certificate certificate = new Certificate();
        BeanUtils.copyProperties(oper,certificate);
        certificate.setUserId(driverInfo.getUserId());
        certificate.setBirth(driverInfo.getBirth());
        certificate.setGender(driverInfo.getGender());
        certificate.setNativePlace(driverInfo.getNativePlace());
        certificate.setHealth(driverInfo.getHealth());
        certificate.setPhoneNo(driverInfo.getPhoneNo());
        certificate.setAddress(driverInfo.getAddress());
        certificate.setDriverLicenseType(driverInfo.getDriverLicenseType());
        certificate.setRegionId(driverInfo.getRegionId());
        certificate.setRegionName(regionCacheHelper.getDisplayName(driverInfo.getRegionId()));
        certificate.setCompanyId(driverInfo.getCompanyId());
        certificate.setCompanyName(driverInfo.getCompanyName());
        // 如果是补换证书，更新原来存在的证书信息
        if (oper.getOperationTypeId().equals(CertificateContant.OPERATION_TYPE_REISSUE)){
            certificate.setId(oper.getCertificateId());
        }
        certificateService.saveOrUpdate(certificate);
        return BeanUtil.copyProperties(oper,DriverTrainingCertificateOperVO.class);
    }

    @LambdaDataScope(type = LambdaDataScopeTypeEnum.ALL)
    public void buildQuery(LambdaQueryWrapper<DriverTrainingCertificateOper> wrapper, Long id) {
        wrapper.eq(DriverTrainingCertificateOper::getId,id);
    }

    public DriverTrainingCertificateOperVO unapprove(Long id, Long userId) {
        LambdaQueryWrapper<DriverTrainingCertificateOper> wrapper = Wrappers.lambdaQuery(DriverTrainingCertificateOper.class);
        DriverTrainingCertificateManage context = (DriverTrainingCertificateManage) AopContext.currentProxy();
        context.buildQuery(wrapper, id);
        DriverTrainingCertificateOper oper = driverTrainingCertificateOperService.getOne(wrapper);
        if(Objects.isNull(oper)){
            throw new BizException(ExceptionCode.OPERATION_EX.getCode(), "此id证书操作不存在");
        }
        oper.setUpdateBy(userId);
        oper.setAuditState(ApproveStateEnum.AUDIT.getCode());
        oper.setAuditStateName(ApproveStateEnum.AUDIT.getDesc());
        driverTrainingCertificateOperService.updateById(oper);
        return BeanUtil.copyProperties(oper,DriverTrainingCertificateOperVO.class);
    }

    /**
     * 查询
     * @param page
     * @param param
     */
    @LambdaDataScope(wrapperIndex = 1)
    @BuildChildrenRegionWrapper(wrapperIndex = 1,entityIndex = 2)
    public void search(Page<DriverTrainingCertificateJoin> page,LambdaQueryWrapper<DriverTrainingCertificateJoin> wrapper,DriverTrainingCertificatePageParamDTO param) {
        wrapper.like(StringUtils.isNotBlank(param.getCompanyName()), DriverTrainingCertificateJoin::getCompanyName, param.getCompanyName())
                .like(StringUtils.isNotBlank(param.getIdCard()), DriverTrainingCertificateJoin::getIdCard, param.getIdCard())
                .like(StringUtils.isNotBlank(param.getDriverName()), DriverTrainingCertificateJoin::getDriverName, param.getDriverName())
                .eq(StringUtils.isNotBlank(param.getTrainingResult()), DriverTrainingCertificateJoin::getTrainingResult, param.getTrainingResult())
                .ge(Objects.nonNull(param.getStartTime()), DriverTrainingCertificateJoin::getTrainingTime, param.getStartTime())
                .le(Objects.nonNull(param.getEndTime()), DriverTrainingCertificateJoin::getTrainingTime, param.getEndTime())
                .orderByDesc(DriverTrainingCertificateJoin::getUpdateOn);
         driverTrainingCertificateJoinService.pageJoin(page, wrapper);
    }

    /**
     * 查询
     * @param page
     * @param param
     */
    @LambdaDataScope(wrapperIndex = 1)
    @BuildChildrenRegionWrapper(entityIndex = 2,wrapperIndex = 1)
    public void searchJoin(Page<DriverTrainingCertificateOperJoin> page,LambdaQueryWrapper<DriverTrainingCertificateOperJoin> wrapper,DriverTrainingCertificatePageParamDTO param,Integer operationTypeAppcation) {
        wrapper.like(StringUtils.isNotBlank(param.getCompanyName()),DriverTrainingCertificateOperJoin::getCompanyName, param.getCompanyName())
                .like(StringUtils.isNotBlank(param.getIdCard()),DriverTrainingCertificateOperJoin::getIdCard, param.getIdCard())
                .like(StringUtils.isNotBlank(param.getDriverName()),DriverTrainingCertificateOperJoin::getDriverName, param.getDriverName())
                .eq(StringUtils.isNotBlank(param.getTrainingResult()),DriverTrainingCertificateOperJoin::getTrainingResult, param.getTrainingResult())
                .ge(Objects.nonNull(param.getStartTime()),DriverTrainingCertificateOperJoin::getTrainingTime, param.getStartTime())
                .le(Objects.nonNull(param.getEndTime()),DriverTrainingCertificateOperJoin::getTrainingTime, param.getEndTime())
                .eq(DriverTrainingCertificateOperJoin::getOperationTypeId,operationTypeAppcation)
                .orderByDesc(DriverTrainingCertificateOperJoin::getUpdateOn);
        driverTrainingCertificateOperJoinService.pageJoin(page, wrapper);
    }

    private DriverTrainingCertificateVO convert(DriverTrainingCertificateJoin driver) {
        if(Objects.isNull(driver)){
            return null;
        }
        DriverTrainingCertificateVO vo = BeanUtil.copyProperties(driver, DriverTrainingCertificateVO.class);
        CodeUtil.searchCompanyNameAndRegionName(vo);
        return vo;
    }

    private DriverTrainingCertificateOperVO convertOper(DriverTrainingCertificateOperJoin driver) {
        DriverTrainingCertificateOperVO vo = BeanUtil.copyProperties(driver, DriverTrainingCertificateOperVO.class);
        CodeUtil.searchCompanyNameAndRegionName(vo);
        return vo;
    }

    public void infoListExport(HttpServletResponse response, DriverTrainingCertificatePageParamDTO param) {
        param.page(CommonConstant.EXPORT_PAGE_NUMBER, CommonConstant.EXPORT_PAGE_SIZE);
        PageVo<DriverTrainingCertificateVO> page = infoList(param);

        List<DriverTrainingCertificateExportDTO> collect = page.getRecords().stream().map(r -> {
            DriverTrainingCertificateExportDTO exportDTO = new DriverTrainingCertificateExportDTO();
            BeanUtils.copyProperties(r, exportDTO);
            String trainingTimeStr = NdDateUtils.format(r.getTrainingTime(), NdDateUtils.DEFAULT_DATE_TIME_FORMAT);
            exportDTO.setTrainingTime(trainingTimeStr);
            String effectiveTimeStr = NdDateUtils.format(r.getTrainingTime(), NdDateUtils.DEFAULT_DATE_TIME_FORMAT);
            exportDTO.setEffectiveTime(effectiveTimeStr);
            return exportDTO;
        }).collect(Collectors.toList());
        String fileName = "驾驶员培训合格证书信息列表";
        ExcelDownUtil.setFileName(fileName, response);
        // 数据导出处理函数
        try {
            EasyExcel.write(response.getOutputStream(), DriverTrainingCertificateExportDTO.class)
                    .autoCloseStream(false)
                    .registerWriteHandler(ExcelExportStyle.formalStyle())
                    .sheet(fileName)
                    .doWrite(collect);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public DriverTrainingCertificateVO modify(DriverTrainingCertificateUpdateDTO data, Long userId) {
        DriverTrainingCertificateVO certificateVO = new DriverTrainingCertificateVO();
        BeanUtils.copyProperties(data,certificateVO);
        Long certificateId = data.getId();
        Certificate certificate = certificateService.getById(certificateId);
        BeanUtils.copyProperties(data,certificate);
        certificate.setUpdateOn(LocalDateTime.now());
        certificateService.updateById(certificate);
        return certificateVO;
    }

    public Long save(DriverTrainingCertificateUpdateDTO data, Long userId) {

        Long driverId = data.getDriverId();
        String certificateNo = data.getCertificateNo();

        // 校验合格证号是否可用
        Certificate check = certificateService.getOne(Wrappers.lambdaQuery(Certificate.class)
                .eq(Certificate::getCertificateNo, certificateNo)
                .ne(Certificate::getId,data.getId()));

        if (Objects.nonNull(check)) {
            throw new BizException("证书编号重复!");
        }

        Certificate oldCertificate = certificateService.getOne(Wrappers.lambdaQuery(Certificate.class)
                .eq(Certificate::getCertificateNo, certificateNo));

        DriverTrainingCertificateOper certificateOper = new DriverTrainingCertificateOper();
        BeanUtils.copyProperties(data,certificateOper);
        Integer certificateType = data.getCertificateType();
        if (certificateType.equals(CertificateContant.OPERATION_TYPE_APPCATION)){
            certificateOper.setOperationTypeId(CertificateContant.OPERATION_TYPE_APPCATION);
            certificateOper.setOperationTypeName(CertificateContant.OPERATION_TYPE_APPCATION_NAME);
        } else if (certificateType.equals(CertificateContant.OPERATION_TYPE_REISSUE)){
            certificateOper.setOperationTypeId(CertificateContant.OPERATION_TYPE_REISSUE);
            // 从证书列表中查出要补换的证书ID
            DriverInfo driverInfo = driverInfoService.getById(driverId);
            Certificate certificate = certificateService.getOne(Wrappers.lambdaQuery(Certificate.class)
                    .eq(Certificate::getUserId, driverInfo.getUserId()));
            if (Objects.nonNull(certificate)){
                certificateOper.setCertificateId(certificate.getId());
            }
            certificateOper.setOperationTypeName(CertificateContant.OPERATION_TYPE_REISSUE_NAME);
        }
        certificateOper.setOperationTime(LocalDateTime.now());
        certificateOper.setApplication(data.getApplication());
        certificateOper.setApplicationReason(data.getApplicationReason());
        if(Objects.isNull(data.getId())){
            certificateOper.setAuditState(ApproveStateEnum.AUDIT.getCode());
            certificateOper.setAuditStateName(ApproveStateEnum.AUDIT.getDesc());
            certificateOper.setCreateOn(LocalDateTime.now());
            certificateOper.setCreateBy(userId);
        }
        driverTrainingCertificateOperService.saveOrUpdate(certificateOper);

        if (Objects.nonNull(oldCertificate)) {
            oldCertificate.setDriverLicenseNo(data.getDriverLicenseNo());
            oldCertificate.setRemark(data.getRemark());
            certificateService.updateById(oldCertificate);
        }

        return certificateOper.getId();
    }
}
