package cn.iocoder.yudao.module.system.service.hr.payslip;

import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.system.controller.admin.hr.vo.payslip.*;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.dataobject.hr.PayslipDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.hr.PayslipMapper;
import cn.iocoder.yudao.module.system.service.attendance.CustomCellStyleHandler;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;

/**
 * 工资条 Service 实现类
 *
 * @author 符溶馨
 */
@Service
public class PayslipServiceImpl implements PayslipService{

    @Resource
    private PayslipMapper payslipMapper;

    @Resource
    private DeptService deptService;

    @Resource
    private AdminUserService userService;

    @Resource
    private FileApi fileApi;

    @Override
    public void createPayslip(List<PayslipCreateReqVO> createReqVO) {

        List<PayslipDO> payslipDOList = BeanUtils.toBean(createReqVO, PayslipDO.class);
        payslipMapper.insertBatch(payslipDOList);
    }

    @Override
    public void updatePayslip(PayslipCreateReqVO updateReqVO) {

        validateIdExists(updateReqVO.getId());

        PayslipDO updateObj = BeanUtils.toBean(updateReqVO, PayslipDO.class);
        payslipMapper.updateById(updateObj);
    }

    @Override
    public PayslipDO getPayslip(Long id) {

        return payslipMapper.selectById(id);
    }

    @Override
    public PageResult<PayslipDO> getPage(PayslipPageReqVO pageReqVO) {

        return payslipMapper.selectPage(pageReqVO);
    }

    @Override
    public PageResult<PayslipDO> getMyPage(PayslipPageReqVO pageReqVO) {

        return payslipMapper.selectMyPage(pageReqVO, getLoginUserId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class) // 添加事务，异常则回滚所有导入
    public void importPayslip(List<PayslipImportExcelVO> importList, String date, Long deptId, String companyName, Boolean isUpdate) {

        if (CollUtil.isEmpty(importList)) {
            throw exception(PAYSLIP_IMPORT_LIST_IS_EMPTY);
        }

        if (payslipMapper.selectCount(new LambdaQueryWrapperX<PayslipDO>()
                .eq(PayslipDO::getSalaryDate, date)
                .eqIfPresent(PayslipDO::getCompanyDeptId, deptId)
                .eq(PayslipDO::getIsConfirm, 0)) > 0L) {

            throw exception(PAYSLIP_ISSUED);
        }

        // 判断如果选择覆盖， 则删除原有数据重新插入
        if (isUpdate) {
            payslipMapper.deletePayslip(date, deptId);
        }else {
            Long count = payslipMapper.selectCount(new LambdaQueryWrapperX<PayslipDO>()
                    .eq(PayslipDO::getSalaryDate, date)
                    .eqIfPresent(PayslipDO::getCompanyDeptId, deptId));

            if (count > 0L) {
                throw exception(PAYSLIP_EXISTS);
            }
        }

        List<String> idCards = convertList(importList, PayslipImportExcelVO::getIdcard);
        Map<String, AdminUserDO> userMap = convertMap(userService.getUserListByIdCard(idCards), AdminUserDO::getIdcard);

        List<PayslipDO> payslipDOList = new ArrayList<>();
        importList.forEach(data -> {

            PayslipDO payslipDO = new PayslipDO();
            try {
                payslipDO.setUserId(userMap.get(data.getIdcard()).getId());
                payslipDO.setDeptId(userMap.get(data.getIdcard()).getDeptId());
            } catch (Exception e) {
                throw exception(PAYSLIP_ID_CARD_ERROR, data.getNickname());
            }
            payslipDO.setCompanyDeptId(deptId);
            payslipDO.setSalary(data.getSalary());
            payslipDO.setSalaryDate(date);
            payslipDO.setBankName(data.getBankName());
            payslipDO.setBankNo(data.getBankNo());
            payslipDO.setRemark(data.getRemark());
            payslipDO.setCompanyName(companyName);

            /* begin 设置明细数据 */
            List<PayslipDetail> details = new ArrayList<>();
            PayslipDetail detail = new PayslipDetail();
            // 设置类型名称
            detail.setName("月总收入");
            // 设置明细数据
            List<PayslipDetail> detailList = new ArrayList<>();
            // 基本工资
            detailList.add(new PayslipDetail()
                    .setCode("jbgz")
                    .setName("基本工资")
                    .setValue(data.getJbgz()));
            // 防暑降温费
            detailList.add(new PayslipDetail()
                    .setCode("fsjwf")
                    .setName("防暑降温费")
                    .setValue(data.getFsjwf()));
            // 误餐补助
            detailList.add(new PayslipDetail()
                    .setCode("wcbz")
                    .setName("误餐补助")
                    .setValue(data.getWcbz()));
            // 福利费
            detailList.add(new PayslipDetail()
                    .setCode("flf")
                    .setName("福利费")
                    .setValue(data.getFlf()));

            // 设置大类数据
            detail.setChildren(detailList);
            details.add(detail);

            // 设置合计数据
            details.add(new PayslipDetail()
                    .setCode("sum")
                    .setName("合计")
                    .setValue(data.getSum()));

            // 设置试用期工资数据
            details.add(new PayslipDetail()
                    .setCode("syqgz")
                    .setName("试用期工资")
                    .setValue(data.getSyqgz()));

            // 设置试用期工资数据
            details.add(new PayslipDetail()
                    .setCode("yfgz")
                    .setName("应发工资")
                    .setValue(data.getYfgz()));

            // 设置代扣代缴项数据
            detailList = new ArrayList<>();
            detailList.add(new PayslipDetail()
                    .setCode("ylbx")
                    .setName("养老")
                    .setValue(data.getYlbx()));
            detailList.add(new PayslipDetail()
                    .setCode("sybx")
                    .setName("失业")
                    .setValue(data.getSybx()));
            detailList.add(new PayslipDetail()
                    .setCode("yb")
                    .setName("医保")
                    .setValue(data.getYb()));
            detailList.add(new PayslipDetail()
                    .setCode("deyl")
                    .setName("大额医疗")
                    .setValue(data.getDeyl()));
            details.add(new PayslipDetail()
                    .setName("代扣代缴项")
                    .setChildren(detailList));

            // 设置代缴合计数据
            details.add(new PayslipDetail()
                    .setCode("djSum")
                    .setName("代缴合计")
                    .setValue(data.getDjSum()));

            // 设置公积金数据
            details.add(new PayslipDetail()
                    .setCode("gjj")
                    .setName("公积金")
                    .setValue(data.getGjj()));

            // 设置个税数据
            details.add(new PayslipDetail()
                    .setCode("gs")
                    .setName("个税")
                    .setValue(data.getGs()));
            /* end */
            payslipDO.setDetails(details);

            payslipDOList.add(payslipDO);
        });

        // 批量插入
        payslipMapper.insertBatch(payslipDOList);
    }

    @Override
    public void issuedPayslip(PayslipPageReqVO updateReqVO) {

        List<Long> deptIds = convertList(deptService.getChildDept(updateReqVO.getDeptId()), DeptDO::getId);

        // 更新工资条状态 为待确认中
        payslipMapper.update(new PayslipDO().setIsConfirm(0), new LambdaQueryWrapperX<PayslipDO>()
                .eq(PayslipDO::getSalaryDate, updateReqVO.getDate())
                .inIfPresent(PayslipDO::getDeptId, deptIds)
                .isNull(PayslipDO::getIsConfirm));
    }

    @Override
    public void exportList(HttpServletResponse response, PayslipPageReqVO exportReqVO) {

        try {

            // 获取工资条列表
            List<PayslipRespVO> list = new ArrayList<>();
            if (exportReqVO.getDeptId() != null) {
                list = BeanUtils.toBean(getPage(exportReqVO).getList(), PayslipRespVO.class);
            }else {
                list = BeanUtils.toBean(getMyPage(exportReqVO).getList(), PayslipRespVO.class);
            }

            if (CollUtil.isEmpty(list)) {
                return;
            }

            // 获取用户详情
            List<Long> userIds = convertList(list, PayslipRespVO::getUserId);
            Map<Long, AdminUserDO> userMap = convertMap(userService.getUserList(userIds), AdminUserDO::getId);

            // 获取电子签名Map
            Map<Long, String> signImgPathMap = fileApi.getUserSignImgPathMap(userIds).getCheckedData();

            List<String> nameList = new ArrayList<>();
            List<List<Object>> data = new ArrayList<>();
            int count = 1;
            for (PayslipRespVO payslipRespVO : list) {

                List<Object> row = new ArrayList<>();
                row.add(String.valueOf(count++));
                row.add(payslipRespVO.getUserName());
                row.add(userMap.get(payslipRespVO.getUserId()).getIdcard());

                Map<String, Object> detailMap = payslipRespVO.getDetails().stream()
                        .map(item -> {
                            List<PayslipDetail> children = new ArrayList<>();
                            if (item.getChildren() != null) {
                                children.addAll(item.getChildren());
                            }else {
                                children.add(item);
                            }
                            return children.stream().collect(Collectors.toMap(PayslipDetail::getCode,
                                    value -> value.getValue() == null ? 0 : value.getValue()));
                        })
                        .flatMap(map -> map.entrySet().stream()) // 将每个 Map 转换为 Entry 流
                        .collect(Collectors.toMap(
                                Map.Entry::getKey,   // 使用 Entry 的 key
                                Map.Entry::getValue, // 使用 Entry 的 value
                                (existing, replacement) -> existing // 处理键冲突，保留现有值
                        ));

                row.add(detailMap.get("jbgz").toString());
                row.add(detailMap.get("fsjwf").toString());
                row.add(detailMap.get("wcbz").toString());
                row.add(detailMap.get("flf").toString());
                row.add(detailMap.get("sum").toString());
                row.add(detailMap.get("syqgz").toString());
                row.add(detailMap.get("yfgz").toString());
                row.add(detailMap.get("ylbx").toString());
                row.add(detailMap.get("sybx").toString());
                row.add(detailMap.get("yb").toString());
                row.add(detailMap.get("deyl").toString());
                row.add(detailMap.get("djSum").toString());
                row.add(detailMap.get("gjj").toString());
                row.add(detailMap.get("gs").toString());
                row.add(payslipRespVO.getSalary().toString());
                row.add(payslipRespVO.getBankName());
                row.add(payslipRespVO.getBankNo());
                row.add(payslipRespVO.getRemark());
                if (payslipRespVO.getIsConfirm() == 1) {
                    row.add(new URL(signImgPathMap.get(payslipRespVO.getUserId())));
                    nameList.add(payslipRespVO.getUserName());
                }else {
                    row.add("");
                }

                data.add(row);
            }

            // 获取公司信息
            DeptDO deptDO = deptService.getDept(exportReqVO.getDeptId());
            String headTitle = deptDO.getName() + exportReqVO.getDate() + "工资条";

            EasyExcel.write(response.getOutputStream())
                    .head(getHead(headTitle, list.get(0)))
                    .autoCloseStream(false)
                    .excelType(ExcelTypeEnum.XLS)
                    .registerWriteHandler(new CustomCellStyleHandler(nameList))
                    .sheet("工资条")
                    .doWrite(data);
            response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("工资条", StandardCharsets.UTF_8.name()));
            response.setContentType("application/vnd.ms-excel;charset=UTF-8");

        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private List<List<String>> getHead(String headTitle, PayslipRespVO payslipRespVO) {

        List<List<String>> head = new ArrayList<>();
        head.add(Arrays.asList(headTitle, "序号", "序号"));
        head.add(Arrays.asList(headTitle, "姓名", "姓名"));
        head.add(Arrays.asList(headTitle, "身份证号码", "身份证号码"));
        for (PayslipDetail detail : payslipRespVO.getDetails()) {

            if (detail.getChildren() == null) {
                head.add(Arrays.asList(headTitle, detail.getName(), detail.getName()));
            }else {

                for (PayslipDetail child : detail.getChildren()) {

                    head.add(Arrays.asList(headTitle, detail.getName(), child.getName()));
                }
            }
        }
        head.add(Arrays.asList(headTitle, "实发工资", "实发工资"));
        head.add(Arrays.asList(headTitle, "开户行", "开户行"));
        head.add(Arrays.asList(headTitle, "卡号", "卡号"));
        head.add(Arrays.asList(headTitle, "备注", "备注"));
        head.add(Arrays.asList(headTitle, "签名", "签名"));

        return head;
    }

    private void validateIdExists(Long id) {
        if (payslipMapper.selectById(id) == null) {
            throw exception(PAYSLIP_NOT_EXISTS);
        }
    }
}
