package com.huimv.employment.service.draft;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.FeConversation;
import com.huimv.employment.dao.entity.FeDraftCostSnapshot;
import com.huimv.employment.dao.entity.FeDraftField;
import com.huimv.employment.dao.entity.FeDraftWorkItem;
import com.huimv.employment.dao.entity.FeEmploymentDraft;
import com.huimv.employment.dao.entity.FeEmploymentOrder;
import com.huimv.employment.dao.entity.FeRegistrationBatch;
import com.huimv.employment.dao.mapper.FeDraftCostSnapshotMapper;
import com.huimv.employment.dao.mapper.FeDraftFieldMapper;
import com.huimv.employment.dao.mapper.FeEmploymentDraftMapper;
import com.huimv.employment.dao.mapper.FeEmploymentOrderMapper;
import com.huimv.employment.service.conversation.ConversationService;
import com.huimv.employment.service.draft.dto.DraftWorkerGroupItem;
import com.huimv.employment.service.draft.dto.McpDraftCalculateResponse;
import com.huimv.employment.service.draft.dto.McpDraftGenerateRequest;
import com.huimv.employment.service.draft.dto.McpDraftGenerateResponse;
import com.huimv.employment.service.draft.dto.McpDraftInfoResponse;
import com.huimv.employment.service.draft.dto.McpDraftSubmitRequest;
import com.huimv.employment.service.draft.dto.McpDraftSubmitResponse;
import com.huimv.employment.service.draft.dto.McpDraftUpdateFields;
import com.huimv.employment.service.draft.dto.McpDraftUpdateRequest;
import com.huimv.employment.service.draft.dto.McpDraftUpdateResponse;
import com.huimv.employment.service.order.OrderWorkItemService;
import com.huimv.employment.service.registration.RegistrationBatchService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * MCP 草稿接口（交互规则 v4 §6.4）：头表 worker_source + 明细表多工种 worker_groups。
 */
@Service
public class McpDraftService {

    private static final Logger log = LoggerFactory.getLogger(McpDraftService.class);

    private static final String DB_STATUS_PENDING = "pending";
    private static final String DB_STATUS_CONFIRMED = "confirmed";
    private static final String ORDER_STATUS_CONFIRMED = "confirmed";
    private static final String STEP_DRAFT = "draft";
    private static final String SUBMIT_SUCCESS = "success";
    private static final String MCP_STATUS_INCOMPLETE = "incomplete";
    private static final String MCP_STATUS_READY = "ready";
    private static final String MCP_STATUS_SUBMITTED = "submitted";
    private static final String MCP_UPDATE_SUCCESS = "success";
    private static final String MCP_UPDATE_FAIL = "fail";
    private static final String MCP_UPDATE_BAN = "ban";
    private static final String SOURCE_AI_INFERRED = "ai_inferred";

    private static final Set<String> ALLOWED_SOURCE_TYPES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
            "intent", "clone", "button")));
    private static final Set<String> ALLOWED_WORKER_SOURCES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
            "existing", "recruit")));

    private final FeEmploymentDraftMapper feEmploymentDraftMapper;
    private final FeDraftFieldMapper feDraftFieldMapper;
    private final FeDraftCostSnapshotMapper feDraftCostSnapshotMapper;
    private final FeEmploymentOrderMapper feEmploymentOrderMapper;
    private final ConversationService conversationService;
    private final DraftCostCalculationEngine draftCostCalculationEngine;
    private final DraftWorkItemService draftWorkItemService;
    private final OrderWorkItemService orderWorkItemService;
    private final RegistrationBatchService registrationBatchService;
    private final ObjectMapper objectMapper;

    public McpDraftService(FeEmploymentDraftMapper feEmploymentDraftMapper,
                           FeDraftFieldMapper feDraftFieldMapper,
                           FeDraftCostSnapshotMapper feDraftCostSnapshotMapper,
                           FeEmploymentOrderMapper feEmploymentOrderMapper,
                           ConversationService conversationService,
                           DraftCostCalculationEngine draftCostCalculationEngine,
                           DraftWorkItemService draftWorkItemService,
                           OrderWorkItemService orderWorkItemService,
                           RegistrationBatchService registrationBatchService,
                           ObjectMapper objectMapper) {
        this.feEmploymentDraftMapper = feEmploymentDraftMapper;
        this.feDraftFieldMapper = feDraftFieldMapper;
        this.feDraftCostSnapshotMapper = feDraftCostSnapshotMapper;
        this.feEmploymentOrderMapper = feEmploymentOrderMapper;
        this.conversationService = conversationService;
        this.draftCostCalculationEngine = draftCostCalculationEngine;
        this.draftWorkItemService = draftWorkItemService;
        this.orderWorkItemService = orderWorkItemService;
        this.registrationBatchService = registrationBatchService;
        this.objectMapper = objectMapper;
    }

    @Transactional(rollbackFor = Exception.class)
    public McpDraftGenerateResponse generate(String sessionId, McpDraftGenerateRequest request) {
        validateGenerateRequest(request);
        FeConversation conversation = conversationService.requireBySessionId(sessionId);
        FeEmploymentDraft existing = findPendingDraft(conversation.getId());
        if (existing != null) {
            LocalDateTime now = LocalDateTime.now();
            recalculateMissingFields(existing, now);
            return toGenerateResponse(feEmploymentDraftMapper.selectById(existing.getId()));
        }
        if ("clone".equalsIgnoreCase(request.getSourceType().trim())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "请使用 POST /api/mcp/orders/clone 克隆历史订单");
        }

        LocalDateTime now = LocalDateTime.now();
        FeEmploymentDraft draft = buildInitialDraft(conversation, now);
        try {
            feEmploymentDraftMapper.insert(draft);
        } catch (DuplicateKeyException ex) {
            throw new BizException(ErrorCode.BAD_REQUEST, "草稿编号冲突，请重试");
        }
        recalculateMissingFields(draft, now);
        return toGenerateResponse(feEmploymentDraftMapper.selectById(draft.getId()));
    }

    @Transactional(rollbackFor = Exception.class)
    public McpDraftUpdateResponse update(String sessionId, McpDraftUpdateRequest request) {
        FeConversation conversation = conversationService.requireBySessionId(sessionId);
        FeEmploymentDraft draft = requireDraftInConversation(conversation.getId(), request.getDraftId());
        if (!isDbEditable(draft)) {
            return banResponse(draft.getId());
        }
        try {
            LocalDateTime now = LocalDateTime.now();
            Set<String> updatedKeys = applyMcpFields(draft, request.getFields(), now);
            draft.setUpdateTime(now);
            // 避免用内存中的旧 missing_fields 覆盖即将重算的结果
            draft.setMissingFields(null);
            feEmploymentDraftMapper.updateById(draft);
            syncFieldMetadata(draft.getId(), updatedKeys, draft, now);
            recalculateMissingFields(draft, now);
            McpDraftUpdateResponse response = successResponse(draft.getId());
            response.setMissingFields(parseMissingFields(draft.getMissingFields()));
            return response;
        } catch (BizException ex) {
            log.debug("MCP 草稿更新失败 sessionId={} draftId={}: {}",
                    sessionId, request.getDraftId(), ex.getMessage());
            McpDraftUpdateResponse response = new McpDraftUpdateResponse();
            response.setDraftId(request.getDraftId());
            response.setStatus(MCP_UPDATE_FAIL);
            return response;
        }
    }

    public McpDraftInfoResponse getInfo(String sessionId, Long draftId) {
        FeConversation conversation = conversationService.requireBySessionId(sessionId);
        FeEmploymentDraft draft = requireDraftInConversation(conversation.getId(), draftId);
        return toInfoResponse(draft);
    }

    @Transactional(rollbackFor = Exception.class)
    public McpDraftCalculateResponse calculate(String sessionId, Long draftId) {
        FeConversation conversation = conversationService.requireBySessionId(sessionId);
        FeEmploymentDraft draft = requireReadyDraft(conversation.getId(), draftId);
        List<FeDraftWorkItem> items = draftWorkItemService.listByDraftId(draft.getId());
        List<DraftCostCalculationEngine.CalculatedScheme> schemes =
                draftCostCalculationEngine.calculate(draft, items);
        persistCostSnapshots(draft, schemes);
        McpDraftCalculateResponse response = draftCostCalculationEngine.toApiResponse(schemes, items);
        response.setDraftId(draft.getId());
        response.setDraftSummary(draftCostCalculationEngine.buildDraftSummary(draft, items));
        return response;
    }

    @Transactional(rollbackFor = Exception.class)
    public McpDraftSubmitResponse submit(String sessionId, McpDraftSubmitRequest request) {
        FeConversation conversation = conversationService.requireBySessionId(sessionId);
        FeEmploymentDraft draft = requireReadyDraft(conversation.getId(), request.getDraftId());
        if (draft.getEnterpriseId() == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "请先完成企业认证并绑定企业后再提交");
        }
        String schemeCode = request.getSelectedSchemeCode().trim().toUpperCase();
        if (!draftCostCalculationEngine.isAllowedSchemeCode(schemeCode)) {
            throw new BizException(ErrorCode.BAD_REQUEST, "不支持的方案编码：" + request.getSelectedSchemeCode());
        }
        FeDraftCostSnapshot snapshot = findLatestSnapshot(draft.getId(), schemeCode);
        if (snapshot == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "请先调用 calculate 获取方案后再提交");
        }

        List<FeDraftWorkItem> items = draftWorkItemService.listByDraftId(draft.getId());
        LocalDateTime now = LocalDateTime.now();
        FeEmploymentOrder order = buildOrderFromDraft(draft, snapshot, items, now);
        order.setOrderNo("tmp_" + UUID.randomUUID().toString().replace("-", "").substring(0, 20));
        try {
            feEmploymentOrderMapper.insert(order);
        } catch (DuplicateKeyException ex) {
            throw new BizException(ErrorCode.BAD_REQUEST, "订单编号冲突，请重试");
        }
        order.setOrderNo(generateOrderNo(order.getId()));
        feEmploymentOrderMapper.updateById(order);
        orderWorkItemService.copyFromDraftItems(order.getId(), items, now);

        FeEmploymentDraft draftUpdate = new FeEmploymentDraft();
        draftUpdate.setId(draft.getId());
        draftUpdate.setStatus(DB_STATUS_CONFIRMED);
        draftUpdate.setConfirmedAt(now);
        draftUpdate.setSelectedPlanId(snapshot.getPlanId());
        draftUpdate.setUpdateTime(now);
        feEmploymentDraftMapper.updateById(draftUpdate);

        FeRegistrationBatch batch = registrationBatchService.createForOrder(order, draft, now);

        McpDraftSubmitResponse response = new McpDraftSubmitResponse();
        response.setDraftId(draft.getId());
        response.setStatus(SUBMIT_SUCCESS);
        response.setOrderId(order.getOrderNo());
        response.setBatchId(batch.getBatchNo());
        return response;
    }

    private FeEmploymentDraft requireDraftInConversation(Long conversationId, Long draftId) {
        if (draftId == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "draft_id 不能为空");
        }
        FeEmploymentDraft draft = feEmploymentDraftMapper.selectById(draftId);
        if (draft == null || !conversationId.equals(draft.getConversationId())) {
            throw new BizException(ErrorCode.NOT_FOUND, "草稿不存在或不属于当前会话");
        }
        return draft;
    }

    private FeEmploymentDraft requirePendingDraft(Long conversationId, Long draftId) {
        FeEmploymentDraft draft = requireDraftInConversation(conversationId, draftId);
        if (!DB_STATUS_PENDING.equals(draft.getStatus())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "草稿已提交，不可继续操作");
        }
        return draft;
    }

    private FeEmploymentDraft requireReadyDraft(Long conversationId, Long draftId) {
        FeEmploymentDraft draft = requirePendingDraft(conversationId, draftId);
        List<String> missing = parseMissingFields(draft.getMissingFields());
        if (!missing.isEmpty()) {
            throw new BizException(ErrorCode.BAD_REQUEST,
                    "草稿信息不完整，请先补全字段：" + String.join(", ", missing));
        }
        return draft;
    }

    private void persistCostSnapshots(FeEmploymentDraft draft,
                                      List<DraftCostCalculationEngine.CalculatedScheme> schemes) {
        feDraftCostSnapshotMapper.delete(new LambdaQueryWrapper<FeDraftCostSnapshot>()
                .eq(FeDraftCostSnapshot::getDraftId, draft.getId()));
        LocalDateTime now = LocalDateTime.now();
        for (DraftCostCalculationEngine.CalculatedScheme scheme : schemes) {
            FeDraftCostSnapshot snapshot = new FeDraftCostSnapshot();
            snapshot.setDraftId(draft.getId());
            snapshot.setPlanId(scheme.getPlanId());
            snapshot.setPlanCode(scheme.getCode());
            snapshot.setPlanName(scheme.getTitle());
            snapshot.setEmployerTotalOutflow(scheme.getEmployerTotalOutflow());
            snapshot.setEmployerAvgCostPerWorker(scheme.getEmployerAvgCostPerWorker());
            snapshot.setWorkerTakeHomePerWorker(scheme.getWorkerTakeHomePerWorker());
            snapshot.setIsRecommended(scheme.isRecommended());
            snapshot.setComplianceTags(writeJsonArray(scheme.getTags()));
            snapshot.setCalcVersion(draftCostCalculationEngine.calcVersion());
            snapshot.setCreateTime(now);
            feDraftCostSnapshotMapper.insert(snapshot);
        }
    }

    private FeDraftCostSnapshot findLatestSnapshot(Long draftId, String schemeCode) {
        return feDraftCostSnapshotMapper.selectOne(new LambdaQueryWrapper<FeDraftCostSnapshot>()
                .eq(FeDraftCostSnapshot::getDraftId, draftId)
                .eq(FeDraftCostSnapshot::getPlanCode, schemeCode)
                .orderByDesc(FeDraftCostSnapshot::getCreateTime)
                .last("LIMIT 1"));
    }

    private FeEmploymentOrder buildOrderFromDraft(FeEmploymentDraft draft,
                                                  FeDraftCostSnapshot snapshot,
                                                  List<FeDraftWorkItem> items,
                                                  LocalDateTime now) {
        FeEmploymentOrder order = new FeEmploymentOrder();
        order.setEnterpriseId(draft.getEnterpriseId());
        order.setUserId(draft.getUserId());
        order.setDraftId(draft.getId());
        order.setSelectedPlanId(snapshot.getPlanId());
        order.setTitle(StringUtils.hasText(draft.getTitle()) ? draft.getTitle() : "用工订单");
        order.setOrderStatus(ORDER_STATUS_CONFIRMED);
        order.setWorkerSource(draft.getWorkerSource());
        order.setCurrentStepCode(STEP_DRAFT);
        order.setCreateTime(now);
        order.setUpdateTime(now);
        order.setDelFlag(0);
        return order;
    }

    private static String generateOrderNo(Long orderId) {
        return String.format("ord_%d", orderId);
    }

    private String writeJsonArray(List<String> values) {
        try {
            return objectMapper.writeValueAsString(values);
        } catch (Exception ex) {
            return "[]";
        }
    }

    private void validateGenerateRequest(McpDraftGenerateRequest request) {
        if (request == null || !StringUtils.hasText(request.getSourceType())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "source_type 不能为空");
        }
        String sourceType = request.getSourceType().trim().toLowerCase();
        if (!ALLOWED_SOURCE_TYPES.contains(sourceType)) {
            throw new BizException(ErrorCode.BAD_REQUEST, "不支持的 source_type：" + request.getSourceType());
        }
    }

    private FeEmploymentDraft buildInitialDraft(FeConversation conversation, LocalDateTime now) {
        FeEmploymentDraft draft = new FeEmploymentDraft();
        draft.setDraftNo(UUID.randomUUID().toString().replace("-", ""));
        // 会话不绑企业；enterprise_id 在企业认证节点再回填
        draft.setEnterpriseId(null);
        draft.setUserId(conversation.getUserId());
        draft.setConversationId(conversation.getId());
        if (StringUtils.hasText(conversation.getTitle())) {
            String title = conversation.getTitle().trim();
            draft.setTitle(title.length() <= 200 ? title : title.substring(0, 200));
        }
        draft.setStatus(DB_STATUS_PENDING);
        draft.setMissingFields("[]");
        draft.setRiskPrompts("[]");
        draft.setCreateTime(now);
        draft.setUpdateTime(now);
        return draft;
    }

    private FeEmploymentDraft findPendingDraft(Long conversationId) {
        return feEmploymentDraftMapper.selectOne(new LambdaQueryWrapper<FeEmploymentDraft>()
                .eq(FeEmploymentDraft::getConversationId, conversationId)
                .eq(FeEmploymentDraft::getStatus, DB_STATUS_PENDING)
                .orderByDesc(FeEmploymentDraft::getCreateTime)
                .last("LIMIT 1"));
    }

    private Set<String> applyMcpFields(FeEmploymentDraft draft, McpDraftUpdateFields fields, LocalDateTime now) {
        if (fields == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "fields 不能为空");
        }
        Set<String> updatedKeys = new HashSet<>();
        if (fields.getWorkerSource() != null) {
            applySingleField(draft, "worker_source", fields.getWorkerSource(), now);
            updatedKeys.add("worker_source");
        }
        if (fields.getWorkerGroups() != null) {
            applySingleField(draft, "worker_groups", fields.getWorkerGroups(), now);
            updatedKeys.add("worker_groups");
        } else if (hasFlatWorkFieldUpdates(fields)) {
            applyFlatWorkFields(draft, fields, updatedKeys, now);
        }
        if (fields.getTitle() != null) {
            applySingleField(draft, "title", fields.getTitle(), now);
            updatedKeys.add("title");
        }
        if (updatedKeys.isEmpty()) {
            throw new BizException(ErrorCode.BAD_REQUEST, "fields 不能为空");
        }
        return updatedKeys;
    }

    private static boolean hasFlatWorkFieldUpdates(McpDraftUpdateFields fields) {
        return fields.getWorkerCount() != null
                || fields.getWorkDays() != null
                || fields.getWorkType() != null
                || fields.getWorkLocation() != null
                || fields.getSettlementMode() != null
                || fields.getUnitAmount() != null
                || fields.getTotalAmount() != null;
    }

    private void applyFlatWorkFields(FeEmploymentDraft draft,
                                     McpDraftUpdateFields fields,
                                     Set<String> updatedKeys,
                                     LocalDateTime now) {
        DraftWorkerGroupItem patch = new DraftWorkerGroupItem();
        if (fields.getWorkerCount() != null) {
            patch.setWorkerCount(fields.getWorkerCount());
            updatedKeys.add("worker_count");
        }
        if (fields.getWorkDays() != null) {
            patch.setWorkDays(fields.getWorkDays());
            updatedKeys.add("work_days");
        }
        if (fields.getWorkType() != null) {
            patch.setJobType(toStringValue(fields.getWorkType()));
            updatedKeys.add("work_type");
        }
        if (fields.getWorkLocation() != null) {
            patch.setRegion(toStringValue(fields.getWorkLocation()));
            updatedKeys.add("work_location");
        }
        if (fields.getSettlementMode() != null) {
            patch.setSettlementCycle(toStringValue(fields.getSettlementMode()));
            updatedKeys.add("settlement_mode");
        }
        if (fields.getUnitAmount() != null) {
            patch.setUnitAmount(fields.getUnitAmount());
            updatedKeys.add("unit_amount");
        }
        if (fields.getTotalAmount() != null) {
            patch.setTotalAmount(fields.getTotalAmount());
            updatedKeys.add("total_amount");
        }
        draftWorkItemService.upsertFirst(draft.getId(), patch, now);
    }

    private void applySingleField(FeEmploymentDraft draft, String key, Object value, LocalDateTime now) {
        switch (key) {
            case "worker_source":
                String source = toStringValue(value);
                if (StringUtils.hasText(source) && !ALLOWED_WORKER_SOURCES.contains(source.toLowerCase())) {
                    throw new BizException(ErrorCode.BAD_REQUEST, "不支持的 worker_source：" + source);
                }
                draft.setWorkerSource(source != null ? source.toLowerCase() : null);
                break;
            case "worker_groups":
                draftWorkItemService.replaceAll(draft.getId(), value, now);
                break;
            case "title":
                draft.setTitle(toStringValue(value));
                break;
            default:
                throw new BizException(ErrorCode.BAD_REQUEST, "不支持的字段：" + key);
        }
    }

    private void recalculateMissingFields(FeEmploymentDraft draft, LocalDateTime now) {
        List<String> missing = new ArrayList<>();
        if (!StringUtils.hasText(draft.getWorkerSource())) {
            missing.add("worker_source");
        }
        List<FeDraftWorkItem> items = draftWorkItemService.listByDraftId(draft.getId());
        missing.addAll(draftWorkItemService.collectMissingGroupFields(items));
        try {
            FeEmploymentDraft update = new FeEmploymentDraft();
            update.setId(draft.getId());
            update.setMissingFields(objectMapper.writeValueAsString(missing));
            update.setUpdateTime(now);
            feEmploymentDraftMapper.updateById(update);
            draft.setMissingFields(update.getMissingFields());
        } catch (Exception ex) {
            throw new BizException(ErrorCode.INTERNAL_ERROR);
        }
    }

    private McpDraftGenerateResponse toGenerateResponse(FeEmploymentDraft draft) {
        List<String> missing = parseMissingFields(draft.getMissingFields());
        McpDraftGenerateResponse response = new McpDraftGenerateResponse();
        response.setDraftId(draft.getId());
        response.setSchema(DraftSchemaRegistry.buildSchema());
        response.setMissingFields(missing);
        response.setStatus(missing.isEmpty() ? MCP_STATUS_READY : MCP_STATUS_INCOMPLETE);
        return response;
    }

    private McpDraftInfoResponse toInfoResponse(FeEmploymentDraft draft) {
        List<String> missing = parseMissingFields(draft.getMissingFields());
        boolean editable = isDbEditable(draft);
        McpDraftInfoResponse response = new McpDraftInfoResponse();
        response.setDraftId(draft.getId());
        response.setData(toDataMap(draft));
        response.setMissingFields(missing);
        response.setEditable(editable);
        if (!DB_STATUS_PENDING.equals(draft.getStatus())) {
            response.setStatus(MCP_STATUS_SUBMITTED);
        } else {
            response.setStatus(missing.isEmpty() ? MCP_STATUS_READY : MCP_STATUS_INCOMPLETE);
        }
        return response;
    }

    private Map<String, Object> toDataMap(FeEmploymentDraft draft) {
        List<FeDraftWorkItem> items = draftWorkItemService.listByDraftId(draft.getId());
        List<DraftWorkerGroupItem> groups = draftWorkItemService.toApiGroups(items);
        FeDraftWorkItem first = items.isEmpty() ? null : items.get(0);
        Map<String, Object> data = new LinkedHashMap<>();
        data.put("worker_source", draft.getWorkerSource());
        data.put("worker_count", first != null ? first.getWorkerCount() : null);
        data.put("work_days", first != null ? first.getWorkDays() : null);
        data.put("work_type", first != null ? first.getJobType() : null);
        data.put("work_location", first != null ? first.getWorkLocation() : null);
        data.put("settlement_mode", first != null ? first.getSettlementCycle() : null);
        data.put("unit_amount", first != null ? first.getUnitAmount() : null);
        BigDecimal totalAmount = draftWorkItemService.sumTotalAmount(items);
        data.put("total_amount", totalAmount.compareTo(BigDecimal.ZERO) > 0 ? totalAmount : null);
        data.put("worker_groups", groups);
        data.put("total_worker_count", draftWorkItemService.sumWorkerCount(items));
        return data;
    }

    private boolean isDbEditable(FeEmploymentDraft draft) {
        return DB_STATUS_PENDING.equals(draft.getStatus());
    }

    private McpDraftUpdateResponse successResponse(Long draftId) {
        McpDraftUpdateResponse response = new McpDraftUpdateResponse();
        response.setDraftId(draftId);
        response.setStatus(MCP_UPDATE_SUCCESS);
        return response;
    }

    private McpDraftUpdateResponse banResponse(Long draftId) {
        McpDraftUpdateResponse response = new McpDraftUpdateResponse();
        response.setDraftId(draftId);
        response.setStatus(MCP_UPDATE_BAN);
        return response;
    }

    private void syncFieldMetadata(Long draftId,
                                   Set<String> updatedFieldKeys,
                                   FeEmploymentDraft draft,
                                   LocalDateTime now) {
        Map<String, String> labels = DraftSchemaRegistry.fieldLabels();
        for (String fieldKey : updatedFieldKeys) {
            upsertField(draftId, fieldKey, labels.get(fieldKey), resolveFieldText(draft, fieldKey),
                    SOURCE_AI_INFERRED, false, now);
        }
    }

    private void upsertField(Long draftId,
                             String fieldKey,
                             String fieldLabel,
                             String fieldValue,
                             String source,
                             boolean isDefault,
                             LocalDateTime now) {
        FeDraftField existing = feDraftFieldMapper.selectOne(new LambdaQueryWrapper<FeDraftField>()
                .eq(FeDraftField::getDraftId, draftId)
                .eq(FeDraftField::getFieldKey, fieldKey)
                .last("LIMIT 1"));
        if (existing != null) {
            FeDraftField update = new FeDraftField();
            update.setId(existing.getId());
            update.setFieldValue(fieldValue);
            update.setSource(source);
            update.setIsDefault(isDefault);
            update.setUpdateTime(now);
            feDraftFieldMapper.updateById(update);
            return;
        }
        FeDraftField created = new FeDraftField();
        created.setDraftId(draftId);
        created.setFieldKey(fieldKey);
        created.setFieldLabel(fieldLabel);
        created.setFieldValue(fieldValue);
        created.setSource(source);
        created.setEditable(true);
        created.setIsDefault(isDefault);
        created.setCreateTime(now);
        created.setUpdateTime(now);
        feDraftFieldMapper.insert(created);
    }

    private String resolveFieldText(FeEmploymentDraft draft, String fieldKey) {
        List<FeDraftWorkItem> items = draftWorkItemService.listByDraftId(draft.getId());
        FeDraftWorkItem first = items.isEmpty() ? null : items.get(0);
        switch (fieldKey) {
            case "worker_source":
                return draft.getWorkerSource();
            case "title":
                return draft.getTitle();
            case "worker_count":
                return first != null && first.getWorkerCount() != null
                        ? String.valueOf(first.getWorkerCount()) : null;
            case "work_days":
                return first != null && first.getWorkDays() != null
                        ? first.getWorkDays().toPlainString() : null;
            case "work_type":
                return first != null ? first.getJobType() : null;
            case "work_location":
                return first != null ? first.getWorkLocation() : null;
            case "settlement_mode":
                return first != null ? first.getSettlementCycle() : null;
            case "unit_amount":
                return first != null && first.getUnitAmount() != null
                        ? first.getUnitAmount().toPlainString() : null;
            case "total_amount":
                BigDecimal total = draftWorkItemService.sumTotalAmount(items);
                return total.compareTo(BigDecimal.ZERO) > 0 ? total.toPlainString() : null;
            case "worker_groups":
                try {
                    return objectMapper.writeValueAsString(draftWorkItemService.toApiGroups(items));
                } catch (Exception ex) {
                    return null;
                }
            default:
                return null;
        }
    }

    private List<String> parseMissingFields(String json) {
        if (!StringUtils.hasText(json)) {
            return Collections.emptyList();
        }
        try {
            List<String> parsed = objectMapper.readValue(json.trim(),
                    objectMapper.getTypeFactory().constructCollectionType(List.class, String.class));
            return parsed != null ? parsed : Collections.emptyList();
        } catch (Exception ex) {
            return Collections.emptyList();
        }
    }

    private static String toStringValue(Object value) {
        if (value == null) {
            return null;
        }
        String text = String.valueOf(value).trim();
        return text.isEmpty() ? null : text;
    }
}
