package com.huimv.employment.service.order;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.huimv.employment.common.exception.BizException;
import com.huimv.employment.common.exception.ErrorCode;
import com.huimv.employment.dao.entity.FeEmploymentOrder;
import com.huimv.employment.dao.entity.FeEnterprise;
import com.huimv.employment.dao.entity.FeOrderWorkItem;
import com.huimv.employment.dao.entity.FeWorkerRegistration;
import com.huimv.employment.dao.mapper.FeEmploymentOrderMapper;
import com.huimv.employment.dao.mapper.FeWorkerRegistrationMapper;
import com.huimv.employment.service.enterprise.EnterpriseService;
import com.huimv.employment.service.order.dto.TaskPublishDetailResponse;
import com.huimv.employment.service.order.dto.TaskPublishRequest;
import com.huimv.employment.service.order.dto.TaskPublishResponse;
import com.huimv.employment.service.order.dto.TaskPublishWorkItemRequest;
import com.huimv.employment.service.settlement.SettlementService;
import com.huimv.employment.service.settlement.dto.OrderStartSettlementRequest;
import com.huimv.employment.service.settlement.dto.SettlementSummaryResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 任务发布：按订单更新各工种起止日，推进至 {@code task_publish} 并生成结算单。
 * <p>
 * 步骤：登记/招聘阶段 → {@code task_publish} →
 * 生成 {@code fe_settlement_order} 后进入 {@code settlement_pending}。
 * </p>
 */
@Service
public class TaskPublishService {

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

    private static final String STEP_TASK_PUBLISH = "task_publish";
    private static final String STEP_SETTLEMENT_PENDING = "settlement_pending";
    private static final String STEP_SETTLEMENT_PAID = "settlement_paid";
    private static final String STEP_COMPLETED = "completed";
    private static final String REG_COMPLETED = "completed";
    private static final String DISPLAY_SETTLEMENT = "settlement";

    private final FeEmploymentOrderMapper feEmploymentOrderMapper;
    private final FeWorkerRegistrationMapper feWorkerRegistrationMapper;
    private final OrderWorkItemService orderWorkItemService;
    private final SettlementService settlementService;
    private final EnterpriseService enterpriseService;

    public TaskPublishService(FeEmploymentOrderMapper feEmploymentOrderMapper,
                              FeWorkerRegistrationMapper feWorkerRegistrationMapper,
                              OrderWorkItemService orderWorkItemService,
                              SettlementService settlementService,
                              EnterpriseService enterpriseService) {
        this.feEmploymentOrderMapper = feEmploymentOrderMapper;
        this.feWorkerRegistrationMapper = feWorkerRegistrationMapper;
        this.orderWorkItemService = orderWorkItemService;
        this.settlementService = settlementService;
        this.enterpriseService = enterpriseService;
    }

    /**
     * 查询任务发布详情：订单基本信息 + 各工种摘要与计划起止日（供任务发布页回显）。
     */
    public TaskPublishDetailResponse getDetail(Long userId, Long orderId) {
        FeEmploymentOrder order = requireOwnedOrder(userId, orderId);
        List<FeOrderWorkItem> orderItems = orderWorkItemService.listByOrderId(order.getId());

        int planned = orderWorkItemService.sumWorkerCount(orderItems);
        int ready = countCompletedWorkers(order.getId());
        boolean datesComplete = !orderItems.isEmpty();
        List<TaskPublishDetailResponse.WorkItemDetail> items = new ArrayList<>();
        for (FeOrderWorkItem item : orderItems) {
            if (item.getWorkStartDate() == null || item.getWorkEndDate() == null) {
                datesComplete = false;
            }
            items.add(toWorkItemDetail(item));
        }
        if (orderItems.isEmpty()) {
            datesComplete = false;
        }

        String step = order.getCurrentStepCode();
        boolean taskPublished = STEP_TASK_PUBLISH.equals(step)
                || STEP_SETTLEMENT_PENDING.equals(step)
                || isStepAfter(step, STEP_TASK_PUBLISH);

        TaskPublishDetailResponse response = new TaskPublishDetailResponse();
        response.setDraftId(order.getDraftId());
        response.setOrderId(order.getId());
        response.setOrderNo(order.getOrderNo());
        response.setTitle(order.getTitle());
        response.setCurrentStepCode(step);
        response.setDisplayStepCode(toDisplayStep(step));
        response.setPlannedCount(planned);
        response.setRegisteredCount(ready);
        response.setDatesComplete(datesComplete);
        response.setTaskPublished(taskPublished);
        response.setWorkItems(items);
        if (orderItems.isEmpty()) {
            response.setMessage("订单无用工明细");
        } else if (!datesComplete) {
            response.setMessage("请为各工种填写工作起止时间后发布任务");
        } else if (taskPublished) {
            response.setMessage("任务已发布");
        } else {
            response.setMessage("工种起止日已齐，可发布任务");
        }
        return response;
    }

    /**
     * 任务发布。
     * <ol>
     *   <li>按订单 ID 校验归属</li>
     *   <li>更新 {@code fe_order_work_item} 各工种起止日</li>
     *   <li>订单步骤推进到 {@code task_publish}</li>
     *   <li>生成结算单 {@code fe_settlement_order}（进入 settlement_pending）</li>
     * </ol>
     */
    @Transactional(rollbackFor = Exception.class)
    public TaskPublishResponse publish(Long userId, Long orderId, TaskPublishRequest request) {
        if (request == null || request.getWorkItems() == null || request.getWorkItems().isEmpty()) {
            throw new BizException(ErrorCode.BAD_REQUEST, "work_items 不能为空");
        }
        FeEmploymentOrder order = requireOwnedOrder(userId, orderId);

        String step = order.getCurrentStepCode();
        if (STEP_SETTLEMENT_PAID.equals(step) || STEP_COMPLETED.equals(step)) {
            throw new BizException(ErrorCode.BAD_REQUEST,
                    "订单已进入结算支付或办结阶段，不可再发布任务");
        }

        List<FeOrderWorkItem> orderItems = orderWorkItemService.listByOrderId(order.getId());
        if (orderItems.isEmpty()) {
            throw new BizException(ErrorCode.BAD_REQUEST, "订单无用工明细，无法发布任务");
        }

        int planned = orderWorkItemService.sumWorkerCount(orderItems);
        int ready = countCompletedWorkers(order.getId());
        assertRegistrationComplete(planned, ready, request.isAllowUnderstaffed());

        LocalDateTime now = LocalDateTime.now();
        List<TaskPublishResponse.WorkItemBrief> updatedBriefs = applyWorkDates(
                orderItems, request.getWorkItems(), now);

        // 推进到任务已发布
        if (!STEP_TASK_PUBLISH.equals(step)
                && !STEP_SETTLEMENT_PENDING.equals(step)
                && !isStepAfter(step, STEP_TASK_PUBLISH)) {
            FeEmploymentOrder update = new FeEmploymentOrder();
            update.setId(order.getId());
            update.setCurrentStepCode(STEP_TASK_PUBLISH);
            update.setUpdateTime(now);
            feEmploymentOrderMapper.updateById(update);
            order.setCurrentStepCode(STEP_TASK_PUBLISH);
        }

        // 生成结算单（允许未到结束日；幂等：已有结算单则返回）
        OrderStartSettlementRequest settlementReq = new OrderStartSettlementRequest();
        settlementReq.setAllowBeforeEndDate(true);
        SettlementSummaryResponse settlement = settlementService.startSettlement(
                userId, order.getId(), settlementReq);

        FeEmploymentOrder refreshed = feEmploymentOrderMapper.selectById(order.getId());
        String currentStep = refreshed != null ? refreshed.getCurrentStepCode() : order.getCurrentStepCode();

        TaskPublishResponse response = new TaskPublishResponse();
        response.setDraftId(order.getDraftId());
        response.setOrderId(order.getId());
        response.setOrderNo(order.getOrderNo());
        response.setCurrentStepCode(currentStep);
        response.setDisplayStepCode(toDisplayStep(currentStep));
        response.setWorkItems(updatedBriefs);
        response.setSettlement(settlement);
        response.setMessage(Boolean.TRUE.equals(settlement.getCreated())
                ? "任务已发布，结算单已生成"
                : "任务已发布，结算单已存在");

        log.info("任务发布成功 orderId={} step={} settlementId={}",
                order.getId(), currentStep,
                settlement != null ? settlement.getSettlementId() : null);
        return response;
    }

    private List<TaskPublishResponse.WorkItemBrief> applyWorkDates(
            List<FeOrderWorkItem> orderItems,
            List<TaskPublishWorkItemRequest> patches,
            LocalDateTime now) {
        Set<Long> usedIds = new HashSet<>();
        List<TaskPublishResponse.WorkItemBrief> briefs = new ArrayList<>();

        for (TaskPublishWorkItemRequest patch : patches) {
            if (patch.getWorkStartDate() == null || patch.getWorkEndDate() == null) {
                throw new BizException(ErrorCode.BAD_REQUEST, "工种起止日期不能为空");
            }
            if (patch.getWorkEndDate().isBefore(patch.getWorkStartDate())) {
                throw new BizException(ErrorCode.BAD_REQUEST,
                        "工作结束日不能早于开始日"
                                + (StringUtils.hasText(patch.getJobType())
                                ? "（" + patch.getJobType() + "）" : ""));
            }
            FeOrderWorkItem matched = matchItem(orderItems, patch, usedIds);
            if (matched == null) {
                throw new BizException(ErrorCode.BAD_REQUEST,
                        "未匹配到用工明细"
                                + describePatch(patch));
            }
            usedIds.add(matched.getId());
            orderWorkItemService.updateWorkDates(
                    matched.getId(), patch.getWorkStartDate(), patch.getWorkEndDate(), now);
            matched.setWorkStartDate(patch.getWorkStartDate());
            matched.setWorkEndDate(patch.getWorkEndDate());

            TaskPublishResponse.WorkItemBrief brief = new TaskPublishResponse.WorkItemBrief();
            brief.setId(matched.getId());
            brief.setGroupNo(matched.getGroupNo());
            brief.setJobType(matched.getJobType());
            brief.setWorkStartDate(patch.getWorkStartDate());
            brief.setWorkEndDate(patch.getWorkEndDate());
            briefs.add(brief);
        }

        // 要求订单全部用工行均已写入起止日（本次更新或已有值）
        for (FeOrderWorkItem item : orderItems) {
            if (item.getWorkStartDate() == null || item.getWorkEndDate() == null) {
                throw new BizException(ErrorCode.BAD_REQUEST,
                        "工种「" + (StringUtils.hasText(item.getJobType()) ? item.getJobType() : item.getGroupNo())
                                + "」缺少工作起止时间，请在 work_items 中补充");
            }
        }
        return briefs;
    }

    private static FeOrderWorkItem matchItem(List<FeOrderWorkItem> items,
                                             TaskPublishWorkItemRequest patch,
                                             Set<Long> usedIds) {
        if (patch.getId() != null) {
            for (FeOrderWorkItem item : items) {
                if (patch.getId().equals(item.getId()) && !usedIds.contains(item.getId())) {
                    return item;
                }
            }
            return null;
        }
        if (StringUtils.hasText(patch.getGroupNo())) {
            String groupNo = patch.getGroupNo().trim();
            for (FeOrderWorkItem item : items) {
                if (groupNo.equals(item.getGroupNo()) && !usedIds.contains(item.getId())) {
                    return item;
                }
            }
            return null;
        }
        if (StringUtils.hasText(patch.getJobType())) {
            String jobType = patch.getJobType().trim();
            FeOrderWorkItem found = null;
            for (FeOrderWorkItem item : items) {
                if (jobType.equals(item.getJobType()) && !usedIds.contains(item.getId())) {
                    if (found != null) {
                        throw new BizException(ErrorCode.BAD_REQUEST,
                                "工种「" + jobType + "」存在多条明细，请传 id 或 group_no 精确匹配");
                    }
                    found = item;
                }
            }
            return found;
        }
        throw new BizException(ErrorCode.BAD_REQUEST, "work_items 项须提供 id、group_no 或 job_type 之一");
    }

    private static String describePatch(TaskPublishWorkItemRequest patch) {
        if (patch.getId() != null) {
            return "（id=" + patch.getId() + "）";
        }
        if (StringUtils.hasText(patch.getGroupNo())) {
            return "（group_no=" + patch.getGroupNo() + "）";
        }
        if (StringUtils.hasText(patch.getJobType())) {
            return "（job_type=" + patch.getJobType() + "）";
        }
        return "";
    }

    private static TaskPublishDetailResponse.WorkItemDetail toWorkItemDetail(FeOrderWorkItem item) {
        TaskPublishDetailResponse.WorkItemDetail detail = new TaskPublishDetailResponse.WorkItemDetail();
        detail.setId(item.getId());
        detail.setGroupNo(item.getGroupNo());
        detail.setSortOrder(item.getSortOrder());
        detail.setJobType(item.getJobType());
        detail.setWorkerCount(item.getWorkerCount());
        detail.setWorkLocation(item.getWorkLocation());
        detail.setWorkDays(item.getWorkDays());
        detail.setSettlementCycle(item.getSettlementCycle());
        detail.setUnitAmount(item.getUnitAmount());
        detail.setTotalAmount(item.getTotalAmount());
        detail.setWorkContent(item.getWorkContent());
        detail.setWorkStartDate(item.getWorkStartDate());
        detail.setWorkEndDate(item.getWorkEndDate());
        return detail;
    }

    /**
     * 任务发布前校验本单人员登记/招聘是否完成：
     * 已完成登记人数（reg_status=completed）须达到计划人数；
     * 未招满时仅当显式 allow_understaffed=true 才放行，且至少须有 1 人已完成登记。
     */
    private static void assertRegistrationComplete(int planned, int ready, boolean allowUnderstaffed) {
        if (ready <= 0) {
            throw new BizException(ErrorCode.BAD_REQUEST, "人员登记未完成：尚无已完成登记的工人，无法发布任务");
        }
        if (planned > 0 && ready < planned) {
            if (!allowUnderstaffed) {
                throw new BizException(ErrorCode.BAD_REQUEST,
                        "人员登记未完成：已登记 " + ready + "/" + planned
                                + " 人。确认缺编发布请传 allow_understaffed=true");
            }
        }
    }

    private int countCompletedWorkers(Long orderId) {
        Long count = feWorkerRegistrationMapper.selectCount(
                new LambdaQueryWrapper<FeWorkerRegistration>()
                        .eq(FeWorkerRegistration::getOrderId, orderId)
                        .eq(FeWorkerRegistration::getRegStatus, REG_COMPLETED));
        return count != null ? count.intValue() : 0;
    }

    private FeEmploymentOrder requireOwnedOrder(Long userId, Long orderId) {
        enterpriseService.requireEnterpriseUser(userId);
        FeEnterprise enterprise = enterpriseService.findEnterpriseByUserId(userId);
        if (enterprise == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "请先完成企业登记");
        }
        if (orderId == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "订单 ID 无效");
        }
        FeEmploymentOrder order = feEmploymentOrderMapper.selectById(orderId);
        if (order == null) {
            throw new BizException(ErrorCode.NOT_FOUND, "用工订单不存在");
        }
        if (order.getEnterpriseId() == null || !order.getEnterpriseId().equals(enterprise.getId())) {
            // 企业未绑定时，回退校验下单用户
            if (order.getUserId() == null || !order.getUserId().equals(userId)) {
                throw new BizException(ErrorCode.FORBIDDEN, "无权操作该订单");
            }
        }
        return order;
    }

    private static String toDisplayStep(String internal) {
        if (STEP_SETTLEMENT_PENDING.equals(internal)
                || STEP_SETTLEMENT_PAID.equals(internal)
                || DISPLAY_SETTLEMENT.equals(internal)) {
            return DISPLAY_SETTLEMENT;
        }
        if (STEP_TASK_PUBLISH.equals(internal)) {
            return STEP_TASK_PUBLISH;
        }
        if (STEP_COMPLETED.equals(internal)) {
            return "invoice_archive";
        }
        return STEP_TASK_PUBLISH;
    }

    private static boolean isStepAfter(String current, String target) {
        if (!StringUtils.hasText(current) || !StringUtils.hasText(target)) {
            return false;
        }
        String[] steps = {
                "draft", "draft_created", "plan_confirmed", "enterprise_auth",
                "registration", "worker_registration", "worker_recruitment",
                "enterprise_audit", "contract_signing",
                "task_publish", "settlement_pending", "settlement",
                "settlement_paid", "invoice_archive", "completed"
        };
        int curIdx = -1;
        int targetIdx = -1;
        for (int i = 0; i < steps.length; i++) {
            if (steps[i].equals(current)) {
                curIdx = i;
            }
            if (steps[i].equals(target)) {
                targetIdx = i;
            }
        }
        return curIdx >= 0 && targetIdx >= 0 && curIdx > targetIdx;
    }
}
