package com.ruoyi.web.arap.service.impl;

import com.ruoyi.web.arap.domain.ArapOffset;
import com.ruoyi.web.arap.domain.ArapOffsetItem;
import com.ruoyi.web.arap.domain.vo.ArapOpenPayVo;
import com.ruoyi.web.arap.domain.vo.ArapOrderOpenVo;
import com.ruoyi.web.arap.mapper.ArapOffsetMapper;
import com.ruoyi.web.arap.service.IArapOffsetService;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;

@Service
public class ArapOffsetServiceImpl implements IArapOffsetService {

    @Autowired
    private ArapOffsetMapper arapOffsetMapper;

    @Override
    public List<ArapOpenPayVo> listOpenPay(ArapOffset query) {
        syncBaseData(query.getOrgId());
        if (query.getOffStartDate() == null && query.getOffEndDate() == null && query.getOffDate() != null) {
            query.setOffStartDate(query.getOffDate());
            query.setOffEndDate(query.getOffDate());
        }
        return arapOffsetMapper.selectOpenPayList(query);
    }

    @Override
    public List<ArapOrderOpenVo> listOrderOpen(String orgId, String custNo, Date startDate, Date endDate, String remark) {
        syncBaseData(orgId);
        return arapOffsetMapper.selectOrderOpenList(orgId, custNo, startDate, endDate, remark);
    }

    @Override
    public ArapOffset getOffset(Long id, String orgId) {
        ArapOffset head = arapOffsetMapper.selectOffsetById(id, orgId);
        if (head == null) return null;
        head.setItemList(arapOffsetMapper.selectOffsetItems(id, orgId));
        return head;
    }

    @Override
    public List<ArapOffset> listOffset(ArapOffset query) {
        return arapOffsetMapper.selectOffsetList(query);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int saveOffset(ArapOffset form, String orgId, String operator) {
        if (form == null || form.getItemList() == null || form.getItemList().isEmpty()) {
            throw new ServiceException("保存失败，冲销明细不能为空");
        }
        if (StringUtils.isEmpty(form.getCustNo())) {
            throw new ServiceException("保存失败，客户编号不能为空");
        }
        if (form.getOffDate() == null) {
            form.setOffDate(new Date());
        }
        String offNo = generateNo(orgId, form.getOffDate());
        form.setOffNo(offNo);
        form.setOrgId(orgId);
        form.setDelFlag("0");
        form.setRevStatus("0");
        form.setCreateBy(operator);
        form.setUpdateBy(operator);

        BigDecimal totalOff = BigDecimal.ZERO;
        BigDecimal totalOpen = BigDecimal.ZERO;

        for (ArapOffsetItem item : form.getItemList()) {
            BigDecimal open = nvl(item.getOpenAmt());
            BigDecimal off = nvl(item.getOffAmt());
            BigDecimal disc = nvl(item.getOffDiscAmt());
            if (off.add(disc).compareTo(open) > 0) {
                throw new ServiceException("保存失败，冲销金额+折让金额不能超过未清款");
            }
            item.setUnoffAmt(open.subtract(off).subtract(disc));
            totalOff = totalOff.add(off).add(disc);
            totalOpen = totalOpen.add(open);
        }
        form.setTotalOpenAmt(totalOpen);
        form.setAvailAmt(totalOpen);
        form.setTotalOffAmt(totalOff);

        arapOffsetMapper.insertOffset(form);
        for (ArapOffsetItem item : form.getItemList()) {
            item.setOrgId(orgId);
            item.setOffId(form.getId());
            item.setOffNo(form.getOffNo());
            item.setRevStatus("0");
            item.setDelFlag("0");
            item.setCreateBy(operator);
            item.setUpdateBy(operator);
        }
        arapOffsetMapper.insertOffsetItemBatch(form.getItemList());

        // 回写货单未清金额
        for (ArapOffsetItem item : form.getItemList()) {
            BigDecimal used = nvl(item.getOffAmt()).add(nvl(item.getOffDiscAmt()));
            if (used.compareTo(BigDecimal.ZERO) <= 0) continue;
            arapOffsetMapper.decreaseOrderOpen(orgId, item.getDocType(), item.getDocNo(), used, operator);
        }

        // 回写未结收付款单（顺序抵扣）
        BigDecimal remain = totalOff;
        List<Long> openPayIds = form.getOpenPayIds() == null ? Collections.emptyList() : form.getOpenPayIds();
        List<ArapOpenPayVo> openList = arapOffsetMapper.selectOpenPayList(buildOpenPayIdsQuery(orgId, openPayIds));
        for (ArapOpenPayVo p : openList) {
            if (remain.compareTo(BigDecimal.ZERO) <= 0) break;
            BigDecimal canUse = abs(nvl(p.getOpenAmt()));
            BigDecimal use = canUse.min(remain);
            if (use.compareTo(BigDecimal.ZERO) > 0) {
                arapOffsetMapper.decreaseOpenPay(p.getId(), orgId, use, operator);
                remain = remain.subtract(use);
            }
        }
        return 1;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int reverseOffset(ArapOffset form, String orgId, String operator) {
        if (form == null || form.getId() == null) {
            throw new ServiceException("反冲销失败，冲销单ID不能为空");
        }
        if (form.getReverseItemIds() == null || form.getReverseItemIds().isEmpty()) {
            throw new ServiceException("反冲销失败，请选择至少一条货单");
        }
        ArapOffset head = arapOffsetMapper.selectOffsetById(form.getId(), orgId);
        if (head == null) {
            throw new ServiceException("反冲销失败，冲销单不存在");
        }
        List<ArapOffsetItem> all = arapOffsetMapper.selectOffsetItems(head.getId(), orgId);
        Map<Long, ArapOffsetItem> map = new HashMap<>();
        for (ArapOffsetItem it : all) map.put(it.getId(), it);

        int n = 0;
        for (Long itemId : form.getReverseItemIds()) {
            ArapOffsetItem it = map.get(itemId);
            if (it == null || "1".equals(it.getRevStatus())) continue;
            BigDecimal used = nvl(it.getOffAmt()).add(nvl(it.getOffDiscAmt()));
            arapOffsetMapper.increaseOrderOpen(orgId, it.getDocType(), it.getDocNo(), used, operator);
            arapOffsetMapper.markItemReversed(itemId, orgId, operator);
            n++;
        }
        // 只要有任意反冲，主单标记已反冲
        if (n > 0) {
            arapOffsetMapper.updateOffsetReverseStatus(head.getId(), orgId, "1", operator);
        }
        return n;
    }

    private void syncBaseData(String orgId) {
        arapOffsetMapper.syncOpenPayFromRecv(orgId);
        arapOffsetMapper.syncOpenPayFromRefund(orgId);
        arapOffsetMapper.syncOrderOpenFromDelivery(orgId);
        arapOffsetMapper.syncOrderOpenFromReturn(orgId);
    }

    private String generateNo(String orgId, Date offDate) {
        String dateText = new SimpleDateFormat("yyyyMMdd").format(offDate);
        Integer maxSeq = arapOffsetMapper.selectTodayMaxSeq(orgId, dateText);
        int next = (maxSeq == null ? 0 : maxSeq) + 1;
        return dateText + String.format("%04d", next);
    }

    private BigDecimal nvl(BigDecimal n) { return n == null ? BigDecimal.ZERO : n; }
    private BigDecimal abs(BigDecimal n) { return n == null ? BigDecimal.ZERO : n.abs(); }

    private ArapOffset buildOpenPayIdsQuery(String orgId, List<Long> ids) {
        ArapOffset q = new ArapOffset();
        q.setOrgId(orgId);
        if (ids != null && !ids.isEmpty()) {
            q.getParams().put("idList", ids);
        }
        return q;
    }
}

