package com.huimv.employment.service.registration;

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.FeContract;
import com.huimv.employment.dao.entity.FeRegistrationBatch;
import com.huimv.employment.dao.entity.FeWorker;
import com.huimv.employment.dao.entity.FeWorkerRegistration;
import com.huimv.employment.dao.mapper.FeEmploymentOrderMapper;
import com.huimv.employment.dao.mapper.FeRegistrationBatchMapper;
import com.huimv.employment.dao.mapper.FeWorkerRegistrationMapper;
import com.huimv.employment.service.contract.ContractService;
import com.huimv.employment.service.contract.dto.ContractSummaryResponse;
import com.huimv.employment.service.enterprise.EnterpriseService;
import com.huimv.employment.service.registration.dto.WorkerRegistrationApplyRequest;
import com.huimv.employment.service.registration.dto.WorkerRegistrationApplyResponse;
import com.huimv.employment.service.registration.dto.WorkerRegistrationRejectRequest;
import com.huimv.employment.service.registration.dto.WorkerRegistrationReviewResponse;
import com.huimv.employment.service.worker.WorkerService;
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;

/**
 * 用工邀请 / 批次登记：临时工扫码申请；企业主审核通过/驳回。
 */
@Service
public class WorkerRegistrationService {

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

    private static final String BATCH_ACTIVE = "active";
    private static final String REG_UNDER_REVIEW = "under_review";
    /** 审核通过后进入个人签合同阶段 */
    private static final String REG_CONTRACT_SIGNING = "contract_signing";
    private static final String REG_COMPLETED = "completed";
    /** 企业审核驳回（与实名核验失败 verify_failed 区分） */
    private static final String REG_REJECTED = "rejected";
    private static final String STEP_REGISTRATION = "registration";
    private static final String STEP_ENTERPRISE_AUDIT = "enterprise_audit";
    private static final String STEP_CONTRACT_SIGNING = "contract_signing";
    private static final String STEP_PLAN_CONFIRMED = "plan_confirmed";
    private static final String STEP_DRAFT_CREATED = "draft_created";
    private static final String APPLY_SUCCESS = "success";
    private static final String APPLY_ALREADY = "already_applied";
    private static final String REVIEW_APPROVED = "approved";
    private static final String REVIEW_REJECTED = "rejected";
    private static final String REVIEW_ALREADY = "already_reviewed";

    private final FeRegistrationBatchMapper feRegistrationBatchMapper;
    private final FeEmploymentOrderMapper feEmploymentOrderMapper;
    private final FeWorkerRegistrationMapper feWorkerRegistrationMapper;
    private final WorkerService workerService;
    private final EnterpriseService enterpriseService;
    private final ContractService contractService;

    public WorkerRegistrationService(FeRegistrationBatchMapper feRegistrationBatchMapper,
                                     FeEmploymentOrderMapper feEmploymentOrderMapper,
                                     FeWorkerRegistrationMapper feWorkerRegistrationMapper,
                                     WorkerService workerService,
                                     EnterpriseService enterpriseService,
                                     ContractService contractService) {
        this.feRegistrationBatchMapper = feRegistrationBatchMapper;
        this.feEmploymentOrderMapper = feEmploymentOrderMapper;
        this.feWorkerRegistrationMapper = feWorkerRegistrationMapper;
        this.workerService = workerService;
        this.enterpriseService = enterpriseService;
        this.contractService = contractService;
    }

    /**
     * 临时工申请加入用工邀请（scene = 订单编号）。
     * <p>要求已完成档案登记（{@code fe_worker}）。同一批次重复申请返回已有记录。</p>
     */
    @Transactional(rollbackFor = Exception.class)
    public WorkerRegistrationApplyResponse apply(Long userId, WorkerRegistrationApplyRequest request) {
        workerService.requireWorkerUser(userId);
        if (request == null || !StringUtils.hasText(request.getScene())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "scene 不能为空");
        }
        FeWorker worker = workerService.findWorkerByUserId(userId);
        if (worker == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "请先完成临时工档案登记");
        }

        String scene = request.getScene().trim();
        FeRegistrationBatch batch = requireActiveBatchByScene(scene);
        FeEmploymentOrder order = feEmploymentOrderMapper.selectById(batch.getOrderId());
        if (order == null) {
            throw new BizException(ErrorCode.NOT_FOUND, "用工订单不存在");
        }

        FeWorkerRegistration existing = feWorkerRegistrationMapper.selectOne(new LambdaQueryWrapper<FeWorkerRegistration>()
                .eq(FeWorkerRegistration::getBatchId, batch.getId())
                .eq(FeWorkerRegistration::getWorkerId, worker.getId())
                .orderByDesc(FeWorkerRegistration::getId)
                .last("LIMIT 1"));
        if (existing != null) {
            return toResponse(existing, batch, order, APPLY_ALREADY, "您已申请加入该用工，请等待企业确认");
        }

        assertBatchHasCapacity(batch);

        LocalDateTime now = LocalDateTime.now();
        FeWorkerRegistration reg = new FeWorkerRegistration();
        reg.setBatchId(batch.getId());
        reg.setOrderId(order.getId());
        reg.setEnterpriseId(batch.getEnterpriseId());
        reg.setWorkerId(worker.getId());
        reg.setRealName(worker.getRealName());
        reg.setMobileEnc(worker.getMobileEnc());
        reg.setMobileMask(worker.getMobileMask());
        reg.setIdCardNoEnc(worker.getIdCardNoEnc());
        reg.setBankAccountEnc(worker.getBankAccountEnc());
        if (StringUtils.hasText(request.getConfirmedWorkType())) {
            reg.setConfirmedWorkType(request.getConfirmedWorkType().trim());
        } else if (StringUtils.hasText(order.getWorkType())) {
            reg.setConfirmedWorkType(order.getWorkType());
        }
        reg.setRegStatus(REG_UNDER_REVIEW);
        reg.setSubmittedAt(now);
        reg.setRemindCount(0);
        reg.setCreateTime(now);
        reg.setUpdateTime(now);
        reg.setDelFlag(0);
        feWorkerRegistrationMapper.insert(reg);

        bumpBatchCounters(batch, now);
        bumpOrderCountersAndAdvanceToEnterpriseAudit(order, now);

        return toResponse(reg, batch, order, APPLY_SUCCESS, "申请已提交，请等待企业确认");
    }

    /**
     * 企业审核通过用工登记。
     * <p>仅 {@code under_review} 可审；该人 {@code reg_status→contract_signing}（进入个人签合同）；
     * 订单若尚未进入签约阶段则推进到 {@code contract_signing}（不要求整批审完）。</p>
     */
    @Transactional(rollbackFor = Exception.class)
    public WorkerRegistrationReviewResponse approve(Long userId, Long registrationId) {
        enterpriseService.requireEnterpriseUser(userId);
        FeEnterprise enterprise = requireEnterpriseOfUser(userId);
        FeWorkerRegistration reg = requireOwnedRegistration(enterprise.getId(), registrationId);
        FeRegistrationBatch batch = requireBatch(reg.getBatchId());
        FeEmploymentOrder order = requireOrder(reg.getOrderId());

        if (REG_CONTRACT_SIGNING.equals(reg.getRegStatus()) || REG_COMPLETED.equals(reg.getRegStatus())) {
            order = feEmploymentOrderMapper.selectById(order.getId());
            WorkerRegistrationReviewResponse already = toReviewResponse(reg, batch, order, REVIEW_ALREADY, "该登记已审核通过");
            attachContract(already, reg.getId());
            return already;
        }
        if (REG_REJECTED.equals(reg.getRegStatus())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "该登记已被驳回，无法再通过");
        }
        if (!REG_UNDER_REVIEW.equals(reg.getRegStatus())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "当前状态不可审核：" + reg.getRegStatus());
        }

        LocalDateTime now = LocalDateTime.now();
        FeWorkerRegistration update = new FeWorkerRegistration();
        update.setId(reg.getId());
        update.setRegStatus(REG_CONTRACT_SIGNING);
        update.setFailReason(null);
        update.setReviewedAt(now);
        update.setReviewedBy(userId);
        update.setUpdateTime(now);
        feWorkerRegistrationMapper.updateById(update);

        reg.setRegStatus(REG_CONTRACT_SIGNING);
        reg.setFailReason(null);
        reg.setReviewedAt(now);
        reg.setReviewedBy(userId);

        bumpCompletedCounters(batch, now);
        maybeAdvanceOrderToContractSigning(order, now);

        order = feEmploymentOrderMapper.selectById(order.getId());
        WorkerRegistrationReviewResponse response = toReviewResponse(reg, batch, order, REVIEW_APPROVED, "审核通过，已进入合同签署");
        try {
            ContractSummaryResponse contract = contractService.createOrGetPendingForRegistration(reg, order);
            fillContractFields(response, contract);
            if (contract != null && Boolean.TRUE.equals(contract.getCreated())
                    && StringUtils.hasText(contract.getSignUrl())) {
                response.setMessage("审核通过，待签合同已生成");
            } else if (contract != null && !StringUtils.hasText(contract.getSignUrl())) {
                response.setMessage("审核通过，待签合同已创建但签署链接未就绪，请重新生成");
                response.setContractCreateFailed(Boolean.TRUE);
            }
        } catch (Exception ex) {
            log.error("登记 registrationId={} 自动生成待签合同失败", reg.getId(), ex);
            response.setMessage("审核通过，待签合同生成失败，请重新生成");
            response.setContractCreateFailed(Boolean.TRUE);
        }
        return response;
    }

    /**
     * 企业驳回用工登记。
     * <p>仅 {@code under_review} 可驳回；写 fail_reason / reviewed_*，登记置 rejected，并释放名额。
     * 驳回不影响其他人的签约进度，不推进订单到签约。</p>
     */
    @Transactional(rollbackFor = Exception.class)
    public WorkerRegistrationReviewResponse reject(Long userId, Long registrationId,
                                                   WorkerRegistrationRejectRequest request) {
        enterpriseService.requireEnterpriseUser(userId);
        FeEnterprise enterprise = requireEnterpriseOfUser(userId);
        if (request == null || !StringUtils.hasText(request.getFailReason())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "驳回原因不能为空");
        }
        String reason = request.getFailReason().trim();
        if (reason.length() > 500) {
            throw new BizException(ErrorCode.BAD_REQUEST, "驳回原因过长");
        }

        FeWorkerRegistration reg = requireOwnedRegistration(enterprise.getId(), registrationId);
        FeRegistrationBatch batch = requireBatch(reg.getBatchId());
        FeEmploymentOrder order = requireOrder(reg.getOrderId());

        if (REG_REJECTED.equals(reg.getRegStatus())) {
            return toReviewResponse(reg, batch, order, REVIEW_ALREADY, "该登记已驳回");
        }
        if (REG_CONTRACT_SIGNING.equals(reg.getRegStatus()) || REG_COMPLETED.equals(reg.getRegStatus())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "该登记已审核通过，无法驳回");
        }
        if (!REG_UNDER_REVIEW.equals(reg.getRegStatus())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "当前状态不可驳回：" + reg.getRegStatus());
        }

        LocalDateTime now = LocalDateTime.now();
        FeWorkerRegistration update = new FeWorkerRegistration();
        update.setId(reg.getId());
        update.setRegStatus(REG_REJECTED);
        update.setFailReason(reason);
        update.setReviewedAt(now);
        update.setReviewedBy(userId);
        update.setUpdateTime(now);
        feWorkerRegistrationMapper.updateById(update);

        reg.setRegStatus(REG_REJECTED);
        reg.setFailReason(reason);
        reg.setReviewedAt(now);
        reg.setReviewedBy(userId);

        releaseSlotCounters(batch, order, now);

        order = feEmploymentOrderMapper.selectById(order.getId());
        return toReviewResponse(reg, batch, order, REVIEW_REJECTED, "已驳回");
    }

    private FeEnterprise requireEnterpriseOfUser(Long userId) {
        FeEnterprise enterprise = enterpriseService.findEnterpriseByUserId(userId);
        if (enterprise == null) {
            throw new BizException(ErrorCode.NOT_FOUND, "企业信息不存在");
        }
        return enterprise;
    }

    private FeWorkerRegistration requireOwnedRegistration(Long enterpriseId, Long registrationId) {
        if (registrationId == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "registration_id 不能为空");
        }
        FeWorkerRegistration reg = feWorkerRegistrationMapper.selectById(registrationId);
        if (reg == null) {
            throw new BizException(ErrorCode.NOT_FOUND, "登记记录不存在");
        }
        if (reg.getEnterpriseId() == null || !reg.getEnterpriseId().equals(enterpriseId)) {
            throw new BizException(ErrorCode.FORBIDDEN, "无权审核该登记");
        }
        return reg;
    }

    private FeRegistrationBatch requireBatch(Long batchId) {
        FeRegistrationBatch batch = feRegistrationBatchMapper.selectById(batchId);
        if (batch == null) {
            throw new BizException(ErrorCode.NOT_FOUND, "登记批次不存在");
        }
        return batch;
    }

    private FeEmploymentOrder requireOrder(Long orderId) {
        FeEmploymentOrder order = feEmploymentOrderMapper.selectById(orderId);
        if (order == null) {
            throw new BizException(ErrorCode.NOT_FOUND, "用工订单不存在");
        }
        return order;
    }

    private void bumpCompletedCounters(FeRegistrationBatch batch, LocalDateTime now) {
        int completed = batch.getCompletedCount() != null ? batch.getCompletedCount() : 0;
        FeRegistrationBatch update = new FeRegistrationBatch();
        update.setId(batch.getId());
        update.setCompletedCount(completed + 1);
        update.setUpdateTime(now);
        feRegistrationBatchMapper.updateById(update);
        batch.setCompletedCount(completed + 1);
    }

    /**
     * 驳回后释放名额：申请时 registered+1/pending-1，此处回退。
     */
    private void releaseSlotCounters(FeRegistrationBatch batch, FeEmploymentOrder order, LocalDateTime now) {
        int registered = batch.getRegisteredCount() != null ? batch.getRegisteredCount() : 0;
        int pending = batch.getPendingCount() != null ? batch.getPendingCount() : 0;
        FeRegistrationBatch batchUpdate = new FeRegistrationBatch();
        batchUpdate.setId(batch.getId());
        batchUpdate.setRegisteredCount(Math.max(0, registered - 1));
        batchUpdate.setPendingCount(pending + 1);
        batchUpdate.setUpdateTime(now);
        feRegistrationBatchMapper.updateById(batchUpdate);

        int orderRegistered = order.getRegisteredCount() != null ? order.getRegisteredCount() : 0;
        int orderPending = order.getPendingCount() != null ? order.getPendingCount() : 0;
        FeEmploymentOrder orderUpdate = new FeEmploymentOrder();
        orderUpdate.setId(order.getId());
        orderUpdate.setRegisteredCount(Math.max(0, orderRegistered - 1));
        orderUpdate.setPendingCount(orderPending + 1);
        orderUpdate.setUpdateTime(now);
        feEmploymentOrderMapper.updateById(orderUpdate);
    }

    /**
     * 申请成功后：名额计数 + 如需则推进到 enterprise_audit。
     */
    private void bumpOrderCountersAndAdvanceToEnterpriseAudit(FeEmploymentOrder order, LocalDateTime now) {
        int registered = order.getRegisteredCount() != null ? order.getRegisteredCount() : 0;
        int pending = order.getPendingCount() != null ? order.getPendingCount() : 0;
        FeEmploymentOrder update = new FeEmploymentOrder();
        update.setId(order.getId());
        update.setRegisteredCount(registered + 1);
        update.setPendingCount(Math.max(0, pending - 1));
        update.setUpdateTime(now);
        if (shouldEnterEnterpriseAudit(order.getCurrentStepCode())) {
            update.setCurrentStepCode(STEP_ENTERPRISE_AUDIT);
            order.setCurrentStepCode(STEP_ENTERPRISE_AUDIT);
        }
        feEmploymentOrderMapper.updateById(update);
        order.setRegisteredCount(registered + 1);
        order.setPendingCount(Math.max(0, pending - 1));
    }

    private static boolean shouldEnterEnterpriseAudit(String step) {
        if (!StringUtils.hasText(step)) {
            return true;
        }
        String code = step.trim();
        return STEP_REGISTRATION.equals(code)
                || STEP_PLAN_CONFIRMED.equals(code)
                || STEP_DRAFT_CREATED.equals(code);
    }

    /**
     * 有人审核通过后，订单进入 contract_signing（整单进入签约阶段；个人已是 contract_signing）。
     * <p>不要求整批审完；已处于签约及之后步骤则不回退。</p>
     */
    private void maybeAdvanceOrderToContractSigning(FeEmploymentOrder order, LocalDateTime now) {
        if (order == null) {
            return;
        }
        String step = order.getCurrentStepCode();
        if (STEP_CONTRACT_SIGNING.equals(step) || isStepAfter(step, STEP_CONTRACT_SIGNING)) {
            return;
        }
        FeEmploymentOrder update = new FeEmploymentOrder();
        update.setId(order.getId());
        update.setCurrentStepCode(STEP_CONTRACT_SIGNING);
        update.setUpdateTime(now);
        feEmploymentOrderMapper.updateById(update);
        order.setCurrentStepCode(STEP_CONTRACT_SIGNING);
    }

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

    private FeRegistrationBatch requireActiveBatchByScene(String scene) {
        FeRegistrationBatch batch = feRegistrationBatchMapper.selectOne(new LambdaQueryWrapper<FeRegistrationBatch>()
                .eq(FeRegistrationBatch::getQrToken, scene)
                .orderByDesc(FeRegistrationBatch::getId)
                .last("LIMIT 1"));
        if (batch == null) {
            FeEmploymentOrder order = feEmploymentOrderMapper.selectOne(new LambdaQueryWrapper<FeEmploymentOrder>()
                    .eq(FeEmploymentOrder::getOrderNo, scene)
                    .orderByDesc(FeEmploymentOrder::getId)
                    .last("LIMIT 1"));
            if (order != null) {
                batch = feRegistrationBatchMapper.selectOne(new LambdaQueryWrapper<FeRegistrationBatch>()
                        .eq(FeRegistrationBatch::getOrderId, order.getId())
                        .orderByDesc(FeRegistrationBatch::getId)
                        .last("LIMIT 1"));
            }
        }
        if (batch == null) {
            throw new BizException(ErrorCode.NOT_FOUND, "未找到对应用工邀请");
        }
        if (!BATCH_ACTIVE.equals(batch.getStatus())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "该用工邀请已关闭或失效");
        }
        LocalDateTime now = LocalDateTime.now();
        if (batch.getValidUntil() != null && now.isAfter(batch.getValidUntil())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "该用工邀请已过期");
        }
        if (batch.getValidFrom() != null && now.isBefore(batch.getValidFrom())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "该用工邀请尚未生效");
        }
        return batch;
    }

    private void assertBatchHasCapacity(FeRegistrationBatch batch) {
        int expected = batch.getExpectedCount() != null ? batch.getExpectedCount() : 0;
        int registered = batch.getRegisteredCount() != null ? batch.getRegisteredCount() : 0;
        if (expected > 0 && registered >= expected) {
            throw new BizException(ErrorCode.BAD_REQUEST, "该用工名额已满");
        }
    }

    private void bumpBatchCounters(FeRegistrationBatch batch, LocalDateTime now) {
        int registered = batch.getRegisteredCount() != null ? batch.getRegisteredCount() : 0;
        int pending = batch.getPendingCount() != null ? batch.getPendingCount() : 0;
        FeRegistrationBatch update = new FeRegistrationBatch();
        update.setId(batch.getId());
        update.setRegisteredCount(registered + 1);
        update.setPendingCount(Math.max(0, pending - 1));
        update.setUpdateTime(now);
        feRegistrationBatchMapper.updateById(update);
    }

    private static WorkerRegistrationApplyResponse toResponse(FeWorkerRegistration reg,
                                                              FeRegistrationBatch batch,
                                                              FeEmploymentOrder order,
                                                              String status,
                                                              String message) {
        WorkerRegistrationApplyResponse response = new WorkerRegistrationApplyResponse();
        response.setStatus(status);
        response.setRegistrationId(reg.getId());
        response.setBatchNo(batch.getBatchNo());
        response.setOrderNo(order.getOrderNo());
        response.setRegStatus(reg.getRegStatus());
        response.setMessage(message);
        return response;
    }

    private static WorkerRegistrationReviewResponse toReviewResponse(FeWorkerRegistration reg,
                                                                     FeRegistrationBatch batch,
                                                                     FeEmploymentOrder order,
                                                                     String status,
                                                                     String message) {
        WorkerRegistrationReviewResponse response = new WorkerRegistrationReviewResponse();
        response.setStatus(status);
        response.setRegistrationId(reg.getId());
        response.setBatchNo(batch.getBatchNo());
        response.setOrderNo(order.getOrderNo());
        response.setRegStatus(reg.getRegStatus());
        response.setFailReason(reg.getFailReason());
        response.setCurrentStepCode(order.getCurrentStepCode());
        response.setMessage(message);
        return response;
    }

    private void attachContract(WorkerRegistrationReviewResponse response, Long registrationId) {
        try {
            FeContract existing = contractService.findActiveByRegistrationId(registrationId);
            if (existing != null) {
                response.setContractId(existing.getId());
                response.setContractNo(existing.getContractNo());
                response.setSignStatus(existing.getSignStatus());
                response.setSignUrl(existing.getSignUrl());
                response.setContractType(existing.getContractType());
            }
        } catch (Exception ignored) {
            // ignore attach failure
        }
    }

    private static void fillContractFields(WorkerRegistrationReviewResponse response, ContractSummaryResponse contract) {
        if (response == null || contract == null) {
            return;
        }
        response.setContractId(contract.getContractId());
        response.setContractNo(contract.getContractNo());
        response.setSignStatus(contract.getSignStatus());
        response.setSignUrl(contract.getSignUrl());
        response.setContractType(contract.getContractType());
    }
}
