package com.huimv.employment.service.order;

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.FeConversation;
import com.huimv.employment.dao.entity.FeDraftField;
import com.huimv.employment.dao.entity.FeEmploymentDraft;
import com.huimv.employment.dao.entity.FeEmploymentOrder;
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.DraftSchemaRegistry;
import com.huimv.employment.service.order.dto.McpOrderCloneRequest;
import com.huimv.employment.service.order.dto.McpOrderCloneResponse;
import com.huimv.employment.service.order.dto.McpOrderListResponse;
import com.huimv.employment.service.order.dto.McpOrderSummaryItem;
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.math.RoundingMode;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * MCP 订单查询与克隆（v2 §6.3.6、§6.3.7）。
 */
@Service
public class McpOrderService {

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

    private static final String STATUS_SUCCESS = "success";
    private static final String DB_STATUS_PENDING = "pending";
    private static final String DB_STATUS_CANCELLED = "cancelled";
    private static final String SCENARIO_CLONE = "existing_worker_regularization";
    private static final String SOURCE_HISTORY = "history_preference";

    private static final int DEFAULT_LIMIT = 10;
    private static final int MAX_LIMIT = 50;

    private static final DateTimeFormatter DATE_FORMAT = DateTimeFormatter.ISO_LOCAL_DATE;

    private final FeEmploymentOrderMapper feEmploymentOrderMapper;
    private final FeEmploymentDraftMapper feEmploymentDraftMapper;
    private final FeDraftFieldMapper feDraftFieldMapper;
    private final ConversationService conversationService;
    private final ObjectMapper objectMapper;

    public McpOrderService(FeEmploymentOrderMapper feEmploymentOrderMapper,
                           FeEmploymentDraftMapper feEmploymentDraftMapper,
                           FeDraftFieldMapper feDraftFieldMapper,
                           ConversationService conversationService,
                           ObjectMapper objectMapper) {
        this.feEmploymentOrderMapper = feEmploymentOrderMapper;
        this.feEmploymentDraftMapper = feEmploymentDraftMapper;
        this.feDraftFieldMapper = feDraftFieldMapper;
        this.conversationService = conversationService;
        this.objectMapper = objectMapper;
    }

    public McpOrderListResponse listOrders(String sessionId, String status, Integer limit) {
        FeConversation conversation = conversationService.requireBySessionId(sessionId);
        int resolvedLimit = resolveLimit(limit);

        LambdaQueryWrapper<FeEmploymentOrder> wrapper = new LambdaQueryWrapper<FeEmploymentOrder>()
                .eq(FeEmploymentOrder::getEnterpriseId, conversation.getEnterpriseId())
                .orderByDesc(FeEmploymentOrder::getCreateTime)
                .last("LIMIT " + resolvedLimit);
        // 注意：.eq(condition, column, value) 的 value 会先求值，status 为空时勿直接 status.trim()
        if (StringUtils.hasText(status)) {
            wrapper.eq(FeEmploymentOrder::getOrderStatus, status.trim());
        }

        List<FeEmploymentOrder> orders = feEmploymentOrderMapper.selectList(wrapper);
        McpOrderListResponse response = new McpOrderListResponse();
        for (FeEmploymentOrder order : orders) {
            response.getOrders().add(toSummaryItem(order));
        }
        return response;
    }

    @Transactional(rollbackFor = Exception.class)
    public McpOrderCloneResponse cloneToDraft(String sessionId, McpOrderCloneRequest request) {
        FeConversation conversation = conversationService.requireBySessionId(sessionId);
        FeEmploymentOrder order = requireEnterpriseOrder(conversation.getEnterpriseId(), request.getOrderId());
        FeEmploymentDraft sourceDraft = order.getDraftId() != null
                ? feEmploymentDraftMapper.selectById(order.getDraftId()) : null;

        cancelPendingDrafts(conversation.getId());
        LocalDateTime now = LocalDateTime.now();
        FeEmploymentDraft draft = buildDraftFromOrder(conversation, order, sourceDraft, now);
        try {
            feEmploymentDraftMapper.insert(draft);
        } catch (DuplicateKeyException ex) {
            throw new BizException(ErrorCode.BAD_REQUEST, "草稿编号冲突，请重试");
        }
        syncClonedFieldMetadata(draft, sourceDraft, now);
        recalculateMissingFields(draft, now);
        log.debug("订单 {} 已克隆为草稿 draftId={} session={}", order.getOrderNo(), draft.getId(), sessionId);

        McpOrderCloneResponse response = new McpOrderCloneResponse();
        response.setStatus(STATUS_SUCCESS);
        response.setDraftId(draft.getId());
        return response;
    }

    private FeEmploymentOrder requireEnterpriseOrder(Long enterpriseId, String orderId) {
        if (!StringUtils.hasText(orderId)) {
            throw new BizException(ErrorCode.BAD_REQUEST, "order_id 不能为空");
        }
        FeEmploymentOrder order = feEmploymentOrderMapper.selectOne(new LambdaQueryWrapper<FeEmploymentOrder>()
                .eq(FeEmploymentOrder::getOrderNo, orderId.trim())
                .last("LIMIT 1"));
        if (order == null) {
            throw new BizException(ErrorCode.NOT_FOUND, "订单不存在");
        }
        if (!enterpriseId.equals(order.getEnterpriseId())) {
            throw new BizException(ErrorCode.FORBIDDEN, "无权访问该订单");
        }
        return order;
    }

    private void cancelPendingDrafts(Long conversationId) {
        feEmploymentDraftMapper.update(null, new LambdaUpdateWrapper<FeEmploymentDraft>()
                .eq(FeEmploymentDraft::getConversationId, conversationId)
                .eq(FeEmploymentDraft::getStatus, DB_STATUS_PENDING)
                .set(FeEmploymentDraft::getStatus, DB_STATUS_CANCELLED)
                .set(FeEmploymentDraft::getUpdateTime, LocalDateTime.now()));
    }

    private FeEmploymentDraft buildDraftFromOrder(FeConversation conversation,
                                                  FeEmploymentOrder order,
                                                  FeEmploymentDraft sourceDraft,
                                                  LocalDateTime now) {
        FeEmploymentDraft draft = new FeEmploymentDraft();
        draft.setDraftNo(UUID.randomUUID().toString().replace("-", ""));
        draft.setEnterpriseId(conversation.getEnterpriseId());
        draft.setUserId(conversation.getUserId());
        draft.setConversationId(conversation.getId());
        draft.setScenario(SCENARIO_CLONE);
        draft.setStatus(DB_STATUS_PENDING);
        draft.setTitle(buildCloneTitle(order));
        draft.setWorkerCount(order.getWorkerCount());
        draft.setWorkDays(order.getWorkDays());
        draft.setWorkType(order.getWorkType());
        draft.setWorkLocation(order.getWorkLocation());
        draft.setWorkLocationSource("history_preference");
        draft.setWorkStartDate(order.getWorkStartDate());
        draft.setWorkEndDate(order.getWorkEndDate());
        draft.setEstimatedTotal(order.getTotalOutflow());
        if (order.getTotalOutflow() != null && order.getWorkerCount() != null && order.getWorkerCount() > 0) {
            draft.setEstimatedPerCapita(order.getTotalOutflow()
                    .divide(BigDecimal.valueOf(order.getWorkerCount()), 2, RoundingMode.HALF_UP));
        }
        if (sourceDraft != null) {
            draft.setWorkContent(sourceDraft.getWorkContent());
            draft.setSettlementMode(sourceDraft.getSettlementMode());
            draft.setDailyWage(sourceDraft.getDailyWage());
            draft.setJobRequirements(sourceDraft.getJobRequirements());
            draft.setInsuranceRequired(sourceDraft.getInsuranceRequired());
            draft.setInsuranceLevel(sourceDraft.getInsuranceLevel());
            draft.setCheckInRequired(sourceDraft.getCheckInRequired());
            draft.setQrValidDays(sourceDraft.getQrValidDays() != null ? sourceDraft.getQrValidDays() : 7);
        } else {
            draft.setInsuranceRequired(Boolean.TRUE);
            draft.setCheckInRequired(Boolean.FALSE);
            draft.setQrValidDays(7);
        }
        draft.setMissingFields("[]");
        draft.setRiskPrompts("[]");
        draft.setCreateTime(now);
        draft.setUpdateTime(now);
        return draft;
    }

    private void syncClonedFieldMetadata(FeEmploymentDraft draft, FeEmploymentDraft sourceDraft, LocalDateTime now) {
        java.util.Map<String, String> labels = DraftSchemaRegistry.fieldLabels();
        upsertFieldIfPresent(draft.getId(), "worker_count", labels.get("worker_count"),
                draft.getWorkerCount(), now);
        upsertFieldIfPresent(draft.getId(), "work_days", labels.get("work_days"),
                draft.getWorkDays(), now);
        upsertFieldIfPresent(draft.getId(), "total_amount", labels.get("total_amount"),
                draft.getEstimatedTotal(), now);
        upsertFieldIfPresent(draft.getId(), "work_type", labels.get("work_type"),
                draft.getWorkType(), now);
        upsertFieldIfPresent(draft.getId(), "work_location", labels.get("work_location"),
                draft.getWorkLocation(), now);
        upsertFieldIfPresent(draft.getId(), "settlement_mode", labels.get("settlement_mode"),
                draft.getSettlementMode(), now);
        if (sourceDraft != null) {
            upsertFieldIfPresent(draft.getId(), "work_content", labels.get("work_content"),
                    draft.getWorkContent(), now);
            upsertFieldIfPresent(draft.getId(), "daily_wage", labels.get("daily_wage"),
                    draft.getDailyWage(), now);
            upsertFieldIfPresent(draft.getId(), "job_requirements", labels.get("job_requirements"),
                    draft.getJobRequirements(), now);
        }
    }

    private void upsertFieldIfPresent(Long draftId, String fieldKey, String fieldLabel, Object value, LocalDateTime now) {
        if (value == null) {
            return;
        }
        String text = value instanceof BigDecimal
                ? ((BigDecimal) value).toPlainString()
                : String.valueOf(value);
        if (!StringUtils.hasText(text)) {
            return;
        }
        upsertField(draftId, fieldKey, fieldLabel, text.trim(), now);
    }

    private void upsertField(Long draftId, String fieldKey, String fieldLabel, String fieldValue, 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_HISTORY);
            update.setIsDefault(true);
            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_HISTORY);
        created.setEditable(true);
        created.setIsDefault(true);
        created.setCreateTime(now);
        created.setUpdateTime(now);
        feDraftFieldMapper.insert(created);
    }

    private void recalculateMissingFields(FeEmploymentDraft draft, LocalDateTime now) {
        List<String> missing = new ArrayList<>();
        for (String key : DraftSchemaRegistry.requiredFieldKeys()) {
            if (isFieldEmpty(draft, key)) {
                missing.add(key);
            }
        }
        try {
            FeEmploymentDraft update = new FeEmploymentDraft();
            update.setId(draft.getId());
            update.setMissingFields(objectMapper.writeValueAsString(missing));
            update.setUpdateTime(now);
            feEmploymentDraftMapper.updateById(update);
        } catch (Exception ex) {
            throw new BizException(ErrorCode.INTERNAL_ERROR);
        }
    }

    private boolean isFieldEmpty(FeEmploymentDraft draft, String key) {
        switch (key) {
            case "worker_count":
                return draft.getWorkerCount() == null || draft.getWorkerCount() <= 0;
            case "work_days":
                return draft.getWorkDays() == null || draft.getWorkDays().compareTo(BigDecimal.ZERO) <= 0;
            case "total_amount":
                return draft.getEstimatedTotal() == null || draft.getEstimatedTotal().compareTo(BigDecimal.ZERO) <= 0;
            case "work_type":
                return !StringUtils.hasText(draft.getWorkType());
            case "work_location":
                return !StringUtils.hasText(draft.getWorkLocation());
            case "settlement_mode":
                return !StringUtils.hasText(draft.getSettlementMode());
            default:
                return true;
        }
    }

    private McpOrderSummaryItem toSummaryItem(FeEmploymentOrder order) {
        McpOrderSummaryItem item = new McpOrderSummaryItem();
        item.setOrderId(order.getOrderNo());
        item.setSummary(buildSummary(order));
        item.setStatus(order.getOrderStatus());
        if (order.getCreateTime() != null) {
            item.setCreatedAt(order.getCreateTime().toLocalDate().format(DATE_FORMAT));
        }
        return item;
    }

    private static String buildSummary(FeEmploymentOrder order) {
        int workers = order.getWorkerCount() != null ? order.getWorkerCount() : 0;
        if (order.getTotalOutflow() != null) {
            String amount = order.getTotalOutflow().stripTrailingZeros().toPlainString();
            return workers + "人/" + amount + "元";
        }
        if (StringUtils.hasText(order.getTitle())) {
            return order.getTitle();
        }
        return workers > 0 ? workers + "人" : "用工订单";
    }

    private static String buildCloneTitle(FeEmploymentOrder order) {
        if (StringUtils.hasText(order.getTitle())) {
            String title = order.getTitle().trim();
            return title.length() <= 200 ? title : title.substring(0, 200);
        }
        return "历史订单复用";
    }

    private static int resolveLimit(Integer limit) {
        if (limit == null || limit <= 0) {
            return DEFAULT_LIMIT;
        }
        return Math.min(limit, MAX_LIMIT);
    }
}
