package com.huimv.employment.service.draft;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.huimv.employment.common.exception.BizException;
import com.huimv.employment.common.exception.ErrorCode;
import com.huimv.employment.dao.entity.FeDraftWorkItem;
import com.huimv.employment.dao.mapper.FeDraftWorkItemMapper;
import com.huimv.employment.service.draft.dto.DraftWorkerGroupItem;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;

/**
 * 草稿用工明细读写（MCP worker_groups ↔ fe_draft_work_item）。
 */
@Service
public class DraftWorkItemService {

    private static final int MAX_GROUPS = 10;
    private static final Set<String> ALLOWED_CYCLES = new HashSet<>();
    /** 仅接受标准组编号，如 grp_01；拒绝「搬运工-浦东仓库」等 Agent 自造值 */
    private static final Pattern CANONICAL_GROUP_NO = Pattern.compile("^grp_(\\d{1,3})$", Pattern.CASE_INSENSITIVE);

    static {
        ALLOWED_CYCLES.add("daily");
        ALLOWED_CYCLES.add("weekly");
        ALLOWED_CYCLES.add("monthly");
    }

    private final FeDraftWorkItemMapper feDraftWorkItemMapper;
    private final ObjectMapper objectMapper;

    public DraftWorkItemService(FeDraftWorkItemMapper feDraftWorkItemMapper, ObjectMapper objectMapper) {
        this.feDraftWorkItemMapper = feDraftWorkItemMapper;
        this.objectMapper = objectMapper;
    }

    public List<FeDraftWorkItem> listByDraftId(Long draftId) {
        if (draftId == null) {
            return Collections.emptyList();
        }
        return feDraftWorkItemMapper.selectList(new LambdaQueryWrapper<FeDraftWorkItem>()
                .eq(FeDraftWorkItem::getDraftId, draftId)
                .orderByAsc(FeDraftWorkItem::getSortOrder)
                .orderByAsc(FeDraftWorkItem::getId));
    }

    public List<DraftWorkerGroupItem> toApiGroups(List<FeDraftWorkItem> items) {
        List<DraftWorkerGroupItem> groups = new ArrayList<>();
        if (items == null) {
            return groups;
        }
        for (FeDraftWorkItem item : items) {
            groups.add(toApiGroup(item));
        }
        return groups;
    }

    public DraftWorkerGroupItem toApiGroup(FeDraftWorkItem item) {
        DraftWorkerGroupItem group = new DraftWorkerGroupItem();
        group.setGroupId(item.getGroupNo());
        group.setJobType(item.getJobType());
        group.setWorkerCount(item.getWorkerCount());
        group.setWorkDays(item.getWorkDays());
        group.setRegion(item.getWorkLocation());
        group.setSettlementCycle(item.getSettlementCycle());
        group.setUnitAmount(item.getUnitAmount());
        group.setTotalAmount(item.getTotalAmount());
        group.setWorkContent(item.getWorkContent());
        group.setWorkStartDate(item.getWorkStartDate());
        group.setWorkEndDate(item.getWorkEndDate());
        return group;
    }

    /**
     * 按 draft_id 写入用工明细：已存在同 group_no 则更新，否则插入；
     * 全量 worker_groups 场景下，未再出现的旧组会删除。
     */
    public List<FeDraftWorkItem> replaceAll(Long draftId, Object groupsValue, LocalDateTime now) {
        return upsertAll(draftId, groupsValue, now, true);
    }

    /**
     * 按 draft_id 更新首条用工明细（扁平字段补全）；不存在则插入 grp_01。
     */
    public FeDraftWorkItem upsertFirst(Long draftId, DraftWorkerGroupItem patch, LocalDateTime now) {
        if (draftId == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "draft_id 不能为空");
        }
        if (patch == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "用工明细不能为空");
        }
        validateGroups(Collections.singletonList(patch));

        List<FeDraftWorkItem> existing = listByDraftId(draftId);
        if (existing.isEmpty()) {
            FeDraftWorkItem created = toEntity(draftId, patch, "grp_01", 0, now);
            created.setCreateTime(now);
            created.setDelFlag(0);
            feDraftWorkItemMapper.insert(created);
            return created;
        }

        FeDraftWorkItem target = existing.get(0);
        mergeOntoEntity(target, patch, now);
        if (!StringUtils.hasText(target.getGroupNo())) {
            target.setGroupNo("grp_01");
        }
        feDraftWorkItemMapper.updateById(target);
        return target;
    }

    private List<FeDraftWorkItem> upsertAll(Long draftId,
                                            Object groupsValue,
                                            LocalDateTime now,
                                            boolean removeMissingGroups) {
        List<DraftWorkerGroupItem> groups = parseGroups(groupsValue);
        if (groups.isEmpty()) {
            throw new BizException(ErrorCode.BAD_REQUEST, "worker_groups 至少 1 组");
        }
        if (groups.size() > MAX_GROUPS) {
            throw new BizException(ErrorCode.BAD_REQUEST, "worker_groups 最多 " + MAX_GROUPS + " 组");
        }
        validateGroups(groups);

        List<FeDraftWorkItem> existing = listByDraftId(draftId);
        Map<String, FeDraftWorkItem> byGroupNo = new java.util.LinkedHashMap<>();
        for (FeDraftWorkItem item : existing) {
            if (StringUtils.hasText(item.getGroupNo())) {
                byGroupNo.put(item.getGroupNo().trim(), item);
            }
        }

        List<FeDraftWorkItem> saved = new ArrayList<>(groups.size());
        Set<Long> keptIds = new HashSet<>();
        Set<String> usedNos = new HashSet<>();
        int sort = 0;
        for (DraftWorkerGroupItem group : groups) {
            String groupNo = resolveCanonicalGroupNo(group.getGroupId(), sort);
            if (!usedNos.add(groupNo)) {
                throw new BizException(ErrorCode.BAD_REQUEST, "重复的 group_id：" + groupNo);
            }
            FeDraftWorkItem entity = byGroupNo.get(groupNo);
            // 历史脏数据（group_no=工种-地点）按顺序位对齐并纠正编号
            if (entity == null && sort < existing.size()) {
                entity = existing.get(sort);
            }
            if (entity != null) {
                entity.setGroupNo(groupNo);
                applyFullOntoEntity(entity, group, sort, now);
                updateEntityAllowingNulls(entity);
                keptIds.add(entity.getId());
                saved.add(entity);
            } else {
                FeDraftWorkItem created = toEntity(draftId, group, groupNo, sort, now);
                created.setCreateTime(now);
                created.setDelFlag(0);
                feDraftWorkItemMapper.insert(created);
                keptIds.add(created.getId());
                saved.add(created);
            }
            sort++;
        }

        if (removeMissingGroups) {
            for (FeDraftWorkItem item : existing) {
                if (item.getId() != null && !keptIds.contains(item.getId())) {
                    feDraftWorkItemMapper.deleteById(item.getId());
                }
            }
        }
        return saved;
    }

    private static FeDraftWorkItem toEntity(Long draftId,
                                            DraftWorkerGroupItem group,
                                            String groupNo,
                                            int sortOrder,
                                            LocalDateTime now) {
        FeDraftWorkItem entity = new FeDraftWorkItem();
        entity.setDraftId(draftId);
        entity.setGroupNo(groupNo);
        entity.setSortOrder(sortOrder);
        entity.setJobType(trimToNull(group.getJobType()));
        entity.setWorkerCount(group.getWorkerCount());
        entity.setWorkDays(group.getWorkDays());
        entity.setWorkLocation(trimToNull(group.getRegion()));
        entity.setSettlementCycle(trimToNull(group.getSettlementCycle()));
        entity.setUnitAmount(group.getUnitAmount());
        entity.setTotalAmount(resolveTotalAmount(group));
        entity.setWorkContent(trimToNull(group.getWorkContent()));
        entity.setWorkStartDate(group.getWorkStartDate());
        entity.setWorkEndDate(group.getWorkEndDate());
        entity.setUpdateTime(now);
        return entity;
    }

    private static void applyFullOntoEntity(FeDraftWorkItem entity,
                                            DraftWorkerGroupItem group,
                                            int sortOrder,
                                            LocalDateTime now) {
        entity.setSortOrder(sortOrder);
        entity.setJobType(trimToNull(group.getJobType()));
        entity.setWorkerCount(group.getWorkerCount());
        entity.setWorkDays(group.getWorkDays());
        entity.setWorkLocation(trimToNull(group.getRegion()));
        entity.setSettlementCycle(trimToNull(group.getSettlementCycle()));
        entity.setUnitAmount(group.getUnitAmount());
        entity.setTotalAmount(resolveTotalAmount(group));
        entity.setWorkContent(trimToNull(group.getWorkContent()));
        entity.setWorkStartDate(group.getWorkStartDate());
        entity.setWorkEndDate(group.getWorkEndDate());
        entity.setUpdateTime(now);
    }

    /**
     * 全量覆盖更新：允许将 settlement_cycle 等字段写回 null，
     * 避免 MyBatis-Plus 默认 NOT_NULL 策略跳过空值导致「未传结算方式却沿用旧值」。
     */
    private void updateEntityAllowingNulls(FeDraftWorkItem entity) {
        LambdaUpdateWrapper<FeDraftWorkItem> uw = new LambdaUpdateWrapper<>();
        uw.eq(FeDraftWorkItem::getId, entity.getId())
                .set(FeDraftWorkItem::getGroupNo, entity.getGroupNo())
                .set(FeDraftWorkItem::getSortOrder, entity.getSortOrder())
                .set(FeDraftWorkItem::getJobType, entity.getJobType())
                .set(FeDraftWorkItem::getWorkerCount, entity.getWorkerCount())
                .set(FeDraftWorkItem::getWorkDays, entity.getWorkDays())
                .set(FeDraftWorkItem::getWorkLocation, entity.getWorkLocation())
                .set(FeDraftWorkItem::getSettlementCycle, entity.getSettlementCycle())
                .set(FeDraftWorkItem::getUnitAmount, entity.getUnitAmount())
                .set(FeDraftWorkItem::getTotalAmount, entity.getTotalAmount())
                .set(FeDraftWorkItem::getWorkContent, entity.getWorkContent())
                .set(FeDraftWorkItem::getWorkStartDate, entity.getWorkStartDate())
                .set(FeDraftWorkItem::getWorkEndDate, entity.getWorkEndDate())
                .set(FeDraftWorkItem::getUpdateTime, entity.getUpdateTime());
        feDraftWorkItemMapper.update(null, uw);
    }

    /**
     * group_no 规范为 grp_01 / grp_02 …；Agent 传入「工种-地点」等非标准值时忽略并按序生成。
     */
    static String resolveCanonicalGroupNo(String rawGroupId, int sortIndex) {
        String fallback = String.format("grp_%02d", sortIndex + 1);
        if (!StringUtils.hasText(rawGroupId)) {
            return fallback;
        }
        java.util.regex.Matcher matcher = CANONICAL_GROUP_NO.matcher(rawGroupId.trim());
        if (!matcher.matches()) {
            return fallback;
        }
        try {
            int n = Integer.parseInt(matcher.group(1));
            if (n <= 0) {
                return fallback;
            }
            return String.format("grp_%02d", n);
        } catch (NumberFormatException ex) {
            return fallback;
        }
    }

    /** 仅覆盖 patch 中非 null 字段（扁平字段增量补全）。 */
    private static void mergeOntoEntity(FeDraftWorkItem entity, DraftWorkerGroupItem patch, LocalDateTime now) {
        if (patch.getJobType() != null) {
            entity.setJobType(trimToNull(patch.getJobType()));
        }
        if (patch.getWorkerCount() != null) {
            entity.setWorkerCount(patch.getWorkerCount());
        }
        if (patch.getWorkDays() != null) {
            entity.setWorkDays(patch.getWorkDays());
        }
        if (patch.getRegion() != null) {
            entity.setWorkLocation(trimToNull(patch.getRegion()));
        }
        if (patch.getSettlementCycle() != null) {
            entity.setSettlementCycle(trimToNull(patch.getSettlementCycle()));
        }
        if (patch.getUnitAmount() != null) {
            entity.setUnitAmount(patch.getUnitAmount());
        }
        if (patch.getTotalAmount() != null) {
            entity.setTotalAmount(patch.getTotalAmount());
        } else if (patch.getUnitAmount() != null
                || patch.getWorkerCount() != null
                || patch.getWorkDays() != null) {
            // 单价/人数/天数变更且未显式传 total_amount 时，按当前行重算
            entity.setTotalAmount(computeTotalAmount(
                    entity.getUnitAmount(), entity.getWorkerCount(), entity.getWorkDays()));
        }
        if (patch.getWorkContent() != null) {
            entity.setWorkContent(trimToNull(patch.getWorkContent()));
        }
        if (patch.getWorkStartDate() != null) {
            entity.setWorkStartDate(patch.getWorkStartDate());
        }
        if (patch.getWorkEndDate() != null) {
            entity.setWorkEndDate(patch.getWorkEndDate());
        }
        entity.setUpdateTime(now);
    }

    public int sumWorkerCount(List<FeDraftWorkItem> items) {
        int sum = 0;
        if (items == null) {
            return 0;
        }
        for (FeDraftWorkItem item : items) {
            if (item.getWorkerCount() != null && item.getWorkerCount() > 0) {
                sum += item.getWorkerCount();
            }
        }
        return sum;
    }

    public BigDecimal sumTotalAmount(List<FeDraftWorkItem> items) {
        BigDecimal sum = BigDecimal.ZERO;
        if (items == null) {
            return sum;
        }
        for (FeDraftWorkItem item : items) {
            if (item.getTotalAmount() != null) {
                sum = sum.add(item.getTotalAmount());
            }
        }
        return sum;
    }

    public List<String> collectMissingGroupFields(List<FeDraftWorkItem> items) {
        List<String> missing = new ArrayList<>();
        if (items == null || items.isEmpty()) {
            // 生成草稿骨架：扁平必填路径（不含 worker_source，由调用方补）
            missing.addAll(DraftSchemaRegistry.groupRequiredFieldKeys());
            return missing;
        }
        // 单组：继续用扁平字段名，便于 Agent 补全
        if (items.size() == 1) {
            FeDraftWorkItem item = items.get(0);
            if (item.getWorkerCount() == null || item.getWorkerCount() <= 0) {
                missing.add("worker_count");
            }
            if (item.getWorkDays() == null || item.getWorkDays().compareTo(BigDecimal.ZERO) <= 0) {
                missing.add("work_days");
            }
            if (!StringUtils.hasText(item.getJobType())) {
                missing.add("work_type");
            }
            if (!StringUtils.hasText(item.getWorkLocation())) {
                missing.add("work_location");
            }
            if (!isPresentSettlement(item.getSettlementCycle())) {
                missing.add("settlement_mode");
            }
            if (item.getUnitAmount() == null || item.getUnitAmount().compareTo(BigDecimal.ZERO) <= 0) {
                missing.add("unit_amount");
            }
            return missing;
        }
        // 多组：按索引路径
        for (int i = 0; i < items.size(); i++) {
            FeDraftWorkItem item = items.get(i);
            String prefix = "worker_groups[" + i + "]";
            if (item.getWorkerCount() == null || item.getWorkerCount() <= 0) {
                missing.add(prefix + ".worker_count");
            }
            if (item.getWorkDays() == null || item.getWorkDays().compareTo(BigDecimal.ZERO) <= 0) {
                missing.add(prefix + ".work_days");
            }
            if (!StringUtils.hasText(item.getJobType())) {
                missing.add(prefix + ".work_type");
            }
            if (!StringUtils.hasText(item.getWorkLocation())) {
                missing.add(prefix + ".work_location");
            }
            if (!isPresentSettlement(item.getSettlementCycle())) {
                missing.add(prefix + ".settlement_mode");
            }
            if (item.getUnitAmount() == null || item.getUnitAmount().compareTo(BigDecimal.ZERO) <= 0) {
                missing.add(prefix + ".unit_amount");
            }
        }
        return missing;
    }

    /** 结算方式须为合法枚举值，空串/非法值一律视为未提供 */
    private static boolean isPresentSettlement(String settlementCycle) {
        if (!StringUtils.hasText(settlementCycle)) {
            return false;
        }
        return ALLOWED_CYCLES.contains(settlementCycle.trim().toLowerCase());
    }

    @SuppressWarnings("unchecked")
    private List<DraftWorkerGroupItem> parseGroups(Object value) {
        if (value == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "worker_groups 不能为空");
        }
        try {
            if (value instanceof List) {
                List<?> raw = (List<?>) value;
                List<DraftWorkerGroupItem> result = new ArrayList<>(raw.size());
                for (Object element : raw) {
                    if (element instanceof DraftWorkerGroupItem) {
                        result.add((DraftWorkerGroupItem) element);
                    } else if (element instanceof Map) {
                        result.add(objectMapper.convertValue(element, DraftWorkerGroupItem.class));
                    } else {
                        result.add(objectMapper.convertValue(element, DraftWorkerGroupItem.class));
                    }
                }
                return result;
            }
            return objectMapper.convertValue(value,
                    objectMapper.getTypeFactory().constructCollectionType(List.class, DraftWorkerGroupItem.class));
        } catch (IllegalArgumentException ex) {
            throw new BizException(ErrorCode.BAD_REQUEST, "worker_groups 格式不正确");
        }
    }

    private void validateGroups(List<DraftWorkerGroupItem> groups) {
        for (DraftWorkerGroupItem group : groups) {
            if (group.getWorkerCount() != null && group.getWorkerCount() < 0) {
                throw new BizException(ErrorCode.BAD_REQUEST, "worker_count 不能为负");
            }
            if (group.getWorkDays() != null && group.getWorkDays().compareTo(BigDecimal.ZERO) < 0) {
                throw new BizException(ErrorCode.BAD_REQUEST, "work_days 不能为负");
            }
            if (group.getUnitAmount() != null && group.getUnitAmount().compareTo(BigDecimal.ZERO) < 0) {
                throw new BizException(ErrorCode.BAD_REQUEST, "unit_amount 不能为负");
            }
            if (group.getTotalAmount() != null && group.getTotalAmount().compareTo(BigDecimal.ZERO) < 0) {
                throw new BizException(ErrorCode.BAD_REQUEST, "total_amount 不能为负");
            }
            if (StringUtils.hasText(group.getSettlementCycle())
                    && !ALLOWED_CYCLES.contains(group.getSettlementCycle().trim().toLowerCase())) {
                throw new BizException(ErrorCode.BAD_REQUEST,
                        "不支持的 settlement_cycle：" + group.getSettlementCycle());
            }
            if (group.getWorkStartDate() != null && group.getWorkEndDate() != null
                    && group.getWorkEndDate().isBefore(group.getWorkStartDate())) {
                throw new BizException(ErrorCode.BAD_REQUEST, "结束日期不能早于开工日期");
            }
            if (StringUtils.hasText(group.getSettlementCycle())) {
                group.setSettlementCycle(group.getSettlementCycle().trim().toLowerCase());
            }
        }
    }

    /**
     * 优先用入参 total_amount；为空时按 unit_amount × worker_count × work_days 推算。
     */
    private static BigDecimal resolveTotalAmount(DraftWorkerGroupItem group) {
        if (group == null) {
            return null;
        }
        if (group.getTotalAmount() != null && group.getTotalAmount().compareTo(BigDecimal.ZERO) > 0) {
            return group.getTotalAmount();
        }
        return computeTotalAmount(group.getUnitAmount(), group.getWorkerCount(), group.getWorkDays());
    }

    private static BigDecimal computeTotalAmount(BigDecimal unitAmount, Integer workerCount, BigDecimal workDays) {
        if (unitAmount == null || unitAmount.compareTo(BigDecimal.ZERO) <= 0) {
            return null;
        }
        if (workerCount == null || workerCount <= 0) {
            return null;
        }
        if (workDays == null || workDays.compareTo(BigDecimal.ZERO) <= 0) {
            return null;
        }
        return unitAmount
                .multiply(BigDecimal.valueOf(workerCount))
                .multiply(workDays)
                .setScale(2, java.math.RoundingMode.HALF_UP);
    }

    private static String trimToNull(String value) {
        if (value == null) {
            return null;
        }
        String trimmed = value.trim();
        return trimmed.isEmpty() ? null : trimmed;
    }
}
