package com.huimv.employment.service.contract;

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.FeContract;
import com.huimv.employment.dao.entity.FeEmploymentOrder;
import com.huimv.employment.dao.entity.FeEnterprise;
import com.huimv.employment.dao.entity.FeServicePlan;
import com.huimv.employment.dao.entity.FeWorker;
import com.huimv.employment.dao.entity.FeWorkerRegistration;
import com.huimv.employment.dao.mapper.FeContractMapper;
import com.huimv.employment.dao.mapper.FeEmploymentOrderMapper;
import com.huimv.employment.dao.mapper.FeEnterpriseMapper;
import com.huimv.employment.dao.mapper.FeServicePlanMapper;
import com.huimv.employment.dao.mapper.FeWorkerRegistrationMapper;
import com.huimv.employment.service.contract.dto.ContractItemResponse;
import com.huimv.employment.service.contract.dto.ContractSummaryResponse;
import com.huimv.employment.service.enterprise.EnterpriseService;
import com.huimv.employment.service.order.EmploymentOrderService;
import com.huimv.employment.service.worker.WorkerService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Lazy;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ThreadLocalRandom;

/**
 * 电子合同：审核通过后生成待签合同；支持幂等与重新生成。
 */
@Service
public class ContractService {

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

    private static final String SIGN_PENDING = "pending";
    private static final String SIGN_SIGNING = "signing";
    private static final String SIGN_SIGNED = "signed";
    private static final String SIGN_VOIDED = "voided";
    private static final String PROVIDER_MOCK = "mock";
    private static final String REG_CONTRACT_SIGNING = "contract_signing";
    private static final String REG_COMPLETED = "completed";

    private static final DateTimeFormatter CONTRACT_NO_DAY = DateTimeFormatter.ofPattern("yyyyMMdd");

    private final FeContractMapper feContractMapper;
    private final FeEmploymentOrderMapper feEmploymentOrderMapper;
    private final FeWorkerRegistrationMapper feWorkerRegistrationMapper;
    private final FeServicePlanMapper feServicePlanMapper;
    private final FeEnterpriseMapper feEnterpriseMapper;
    private final EnterpriseService enterpriseService;
    private final WorkerService workerService;
    private final ESignClient eSignClient;
    private final EmploymentOrderService employmentOrderService;

    public ContractService(FeContractMapper feContractMapper,
                           FeEmploymentOrderMapper feEmploymentOrderMapper,
                           FeWorkerRegistrationMapper feWorkerRegistrationMapper,
                           FeServicePlanMapper feServicePlanMapper,
                           FeEnterpriseMapper feEnterpriseMapper,
                           EnterpriseService enterpriseService,
                           WorkerService workerService,
                           ESignClient eSignClient,
                           @Lazy EmploymentOrderService employmentOrderService) {
        this.feContractMapper = feContractMapper;
        this.feEmploymentOrderMapper = feEmploymentOrderMapper;
        this.feWorkerRegistrationMapper = feWorkerRegistrationMapper;
        this.feServicePlanMapper = feServicePlanMapper;
        this.feEnterpriseMapper = feEnterpriseMapper;
        this.enterpriseService = enterpriseService;
        this.workerService = workerService;
        this.eSignClient = eSignClient;
        this.employmentOrderService = employmentOrderService;
    }

    /**
     * 审核通过后创建或返回已有待签合同（同一 registration_id 幂等）。
     * <p>电子签调用失败时仍保留 pending 合同记录，便于后续 {@link #regeneratePendingContract}。</p>
     */
    @Transactional(rollbackFor = Exception.class)
    public ContractSummaryResponse createOrGetPendingForRegistration(FeWorkerRegistration reg, FeEmploymentOrder order) {
        if (reg == null || reg.getId() == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "登记记录无效");
        }
        if (order == null || order.getId() == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "订单无效");
        }

        FeContract existing = findActiveByRegistrationId(reg.getId());
        if (existing != null) {
            if (needsESignRetry(existing)) {
                tryFillESign(existing, reg);
                existing = feContractMapper.selectById(existing.getId());
            }
            return toSummary(existing, false, "已存在待签合同");
        }

        LocalDateTime now = LocalDateTime.now();
        String contractType = resolveContractType(order);
        String title = buildContractTitle(contractType, reg.getRealName(), order.getTitle());
        String contractNo = nextContractNo();

        FeContract contract = new FeContract();
        contract.setContractNo(contractNo);
        contract.setOrderId(order.getId());
        contract.setRegistrationId(reg.getId());
        contract.setWorkerId(reg.getWorkerId());
        contract.setEnterpriseId(order.getEnterpriseId() != null ? order.getEnterpriseId() : reg.getEnterpriseId());
        contract.setContractType(contractType);
        contract.setContractTitle(title);
        contract.setSignStatus(SIGN_PENDING);
        contract.setSignProvider(PROVIDER_MOCK);
        contract.setCreateTime(now);
        contract.setUpdateTime(now);
        contract.setDelFlag(0);

        try {
            feContractMapper.insert(contract);
        } catch (DuplicateKeyException ex) {
            FeContract raced = findActiveByRegistrationId(reg.getId());
            if (raced != null) {
                return toSummary(raced, false, "已存在待签合同");
            }
            throw new BizException(ErrorCode.BAD_REQUEST, "合同编号冲突，请重试");
        }

        try {
            tryFillESign(contract, reg);
        } catch (Exception ex) {
            log.error("登记 registrationId={} 电子签创建失败，合同已落库待重试: {}", reg.getId(), ex.getMessage());
            FeContract saved = feContractMapper.selectById(contract.getId());
            ContractSummaryResponse response = toSummary(saved, true, "待签合同已创建，电子签通道暂不可用，请稍后重新生成");
            return response;
        }

        FeContract saved = feContractMapper.selectById(contract.getId());
        return toSummary(saved, true, "待签合同已生成");
    }

    /**
     * 重新生成签署链接（电子签失败或链接失效时）。
     * <p>仅允许 pending/signing；已签署不可重生成。无合同记录时按登记补建。</p>
     */
    @Transactional(rollbackFor = Exception.class)
    public ContractSummaryResponse regeneratePendingContract(Long userId, Long registrationId) {
        enterpriseService.requireEnterpriseUser(userId);
        FeEnterprise enterprise = enterpriseService.findEnterpriseByUserId(userId);
        if (enterprise == null) {
            throw new BizException(ErrorCode.NOT_FOUND, "企业信息不存在");
        }
        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(enterprise.getId())) {
            throw new BizException(ErrorCode.FORBIDDEN, "无权操作该登记合同");
        }
        if (!REG_CONTRACT_SIGNING.equals(reg.getRegStatus()) && !REG_COMPLETED.equals(reg.getRegStatus())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "仅审核通过待签约的登记可重新生成合同");
        }

        FeEmploymentOrder order = feEmploymentOrderMapper.selectById(reg.getOrderId());
        if (order == null) {
            throw new BizException(ErrorCode.NOT_FOUND, "用工订单不存在");
        }

        FeContract contract = findActiveByRegistrationId(registrationId);
        if (contract == null) {
            return createOrGetPendingForRegistration(reg, order);
        }
        if (SIGN_SIGNED.equals(contract.getSignStatus())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "合同已签署，无法重新生成");
        }
        if (SIGN_VOIDED.equals(contract.getSignStatus())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "合同已作废，无法重新生成");
        }
        if (!SIGN_PENDING.equals(contract.getSignStatus()) && !SIGN_SIGNING.equals(contract.getSignStatus())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "当前签署状态不可重新生成：" + contract.getSignStatus());
        }

        tryFillESign(contract, reg);
        FeContract saved = feContractMapper.selectById(contract.getId());
        return toSummary(saved, false, "签署链接已重新生成");
    }

    public FeContract findActiveByRegistrationId(Long registrationId) {
        if (registrationId == null) {
            return null;
        }
        return feContractMapper.selectOne(new LambdaQueryWrapper<FeContract>()
                .eq(FeContract::getRegistrationId, registrationId)
                .ne(FeContract::getSignStatus, SIGN_VOIDED)
                .orderByDesc(FeContract::getId)
                .last("LIMIT 1"));
    }

    /**
     * 临时工查看自己的合同列表；可选按 {@code sign_status} 过滤。
     */
    public List<ContractItemResponse> listMyContracts(Long userId, String signStatus) {
        FeWorker worker = requireWorkerProfile(userId);
        LambdaQueryWrapper<FeContract> qw = new LambdaQueryWrapper<FeContract>()
                .eq(FeContract::getWorkerId, worker.getId())
                .ne(FeContract::getSignStatus, SIGN_VOIDED)
                .orderByDesc(FeContract::getId);
        if (StringUtils.hasText(signStatus)) {
            qw.eq(FeContract::getSignStatus, signStatus.trim());
        }
        List<FeContract> contracts = feContractMapper.selectList(qw);
        if (contracts == null || contracts.isEmpty()) {
            return Collections.emptyList();
        }
        return toItemList(contracts);
    }

    /**
     * 临时工查看自己的合同详情。
     */
    public ContractItemResponse getMyContract(Long userId, Long contractId) {
        FeWorker worker = requireWorkerProfile(userId);
        FeContract contract = requireOwnedContract(worker.getId(), contractId);
        List<ContractItemResponse> items = toItemList(Collections.singletonList(contract));
        return items.isEmpty() ? null : items.get(0);
    }

    /**
     * 临时工确认签署完成（Mock 电子签闭环；真实电子签可改为回调驱动）。
     * <p>将合同置为 {@code signed}，登记 {@code contract_signing → completed}。
     * 当订单期望人数全部完成后，自动推进到 {@code task_publish}；
     * 未招满（缺编）不自动开工，仍须企业确认。</p>
     */
    @Transactional(rollbackFor = Exception.class)
    public ContractItemResponse confirmSign(Long userId, Long contractId) {
        FeWorker worker = requireWorkerProfile(userId);
        FeContract contract = requireOwnedContract(worker.getId(), contractId);

        if (SIGN_SIGNED.equals(contract.getSignStatus())) {
            employmentOrderService.tryAutoStartWorkWhenFullySigned(contract.getOrderId());
            return getMyContract(userId, contractId);
        }
        if (SIGN_VOIDED.equals(contract.getSignStatus())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "合同已作废，无法签署");
        }
        if (!SIGN_PENDING.equals(contract.getSignStatus()) && !SIGN_SIGNING.equals(contract.getSignStatus())) {
            throw new BizException(ErrorCode.BAD_REQUEST, "当前签署状态不可确认：" + contract.getSignStatus());
        }

        LocalDateTime now = LocalDateTime.now();
        FeContract update = new FeContract();
        update.setId(contract.getId());
        update.setSignStatus(SIGN_SIGNED);
        update.setSignedAt(now);
        update.setUpdateTime(now);
        feContractMapper.updateById(update);

        markRegistrationCompleted(contract.getRegistrationId(), now);
        // 期望人数齐签 → 自动进入开始用工
        employmentOrderService.tryAutoStartWorkWhenFullySigned(contract.getOrderId());
        log.info("合同签署完成 contractId={} workerId={} registrationId={} orderId={}",
                contract.getId(), worker.getId(), contract.getRegistrationId(), contract.getOrderId());
        return getMyContract(userId, contractId);
    }

    /**
     * 签署完成后将登记从 {@code contract_signing} 置为 {@code completed}。
     */
    private void markRegistrationCompleted(Long registrationId, LocalDateTime now) {
        if (registrationId == null) {
            return;
        }
        FeWorkerRegistration reg = feWorkerRegistrationMapper.selectById(registrationId);
        if (reg == null) {
            return;
        }
        if (!REG_CONTRACT_SIGNING.equals(reg.getRegStatus())) {
            return;
        }
        FeWorkerRegistration update = new FeWorkerRegistration();
        update.setId(reg.getId());
        update.setRegStatus(REG_COMPLETED);
        update.setUpdateTime(now);
        feWorkerRegistrationMapper.updateById(update);
    }

    private FeWorker requireWorkerProfile(Long userId) {
        workerService.requireWorkerUser(userId);
        FeWorker worker = workerService.findWorkerByUserId(userId);
        if (worker == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "请先完成临时工实名登记");
        }
        return worker;
    }

    private FeContract requireOwnedContract(Long workerId, Long contractId) {
        if (contractId == null) {
            throw new BizException(ErrorCode.BAD_REQUEST, "合同 ID 无效");
        }
        FeContract contract = feContractMapper.selectById(contractId);
        if (contract == null || SIGN_VOIDED.equals(contract.getSignStatus())) {
            throw new BizException(ErrorCode.NOT_FOUND, "合同不存在");
        }
        if (contract.getWorkerId() == null || !contract.getWorkerId().equals(workerId)) {
            throw new BizException(ErrorCode.FORBIDDEN, "无权查看该合同");
        }
        return contract;
    }

    private List<ContractItemResponse> toItemList(List<FeContract> contracts) {
        Set<Long> orderIds = new HashSet<>();
        Set<Long> enterpriseIds = new HashSet<>();
        for (FeContract c : contracts) {
            if (c.getOrderId() != null) {
                orderIds.add(c.getOrderId());
            }
            if (c.getEnterpriseId() != null) {
                enterpriseIds.add(c.getEnterpriseId());
            }
        }

        Map<Long, FeEmploymentOrder> orderMap = new HashMap<>();
        if (!orderIds.isEmpty()) {
            List<FeEmploymentOrder> orders = feEmploymentOrderMapper.selectBatchIds(orderIds);
            if (orders != null) {
                for (FeEmploymentOrder o : orders) {
                    orderMap.put(o.getId(), o);
                }
            }
        }

        Map<Long, FeEnterprise> enterpriseMap = new HashMap<>();
        if (!enterpriseIds.isEmpty()) {
            List<FeEnterprise> enterprises = feEnterpriseMapper.selectBatchIds(enterpriseIds);
            if (enterprises != null) {
                for (FeEnterprise e : enterprises) {
                    enterpriseMap.put(e.getId(), e);
                }
            }
        }

        List<ContractItemResponse> result = new ArrayList<>(contracts.size());
        for (FeContract c : contracts) {
            result.add(toItem(c, orderMap.get(c.getOrderId()), enterpriseMap.get(c.getEnterpriseId())));
        }
        return result;
    }

    private static ContractItemResponse toItem(FeContract contract,
                                               FeEmploymentOrder order,
                                               FeEnterprise enterprise) {
        ContractItemResponse item = new ContractItemResponse();
        item.setContractId(contract.getId());
        item.setContractNo(contract.getContractNo());
        item.setRegistrationId(contract.getRegistrationId());
        item.setOrderId(contract.getOrderId());
        if (order != null) {
            item.setOrderTitle(order.getTitle());
        }
        item.setEnterpriseId(contract.getEnterpriseId());
        if (enterprise != null) {
            item.setEnterpriseName(enterprise.getName());
        }
        item.setContractType(contract.getContractType());
        item.setContractTitle(contract.getContractTitle());
        item.setSignStatus(contract.getSignStatus());
        item.setSignProvider(contract.getSignProvider());
        item.setSignUrl(contract.getSignUrl());
        item.setExternalContractId(contract.getExternalContractId());
        item.setFileUrl(contract.getFileUrl());
        item.setSignedAt(contract.getSignedAt());
        item.setCreateTime(contract.getCreateTime());
        return item;
    }

    private void tryFillESign(FeContract contract, FeWorkerRegistration reg) {
        ESignClient.ESignCreateResult result = eSignClient.createPendingContract(
                contract.getContractNo(),
                contract.getContractType(),
                contract.getContractTitle(),
                reg != null ? reg.getRealName() : null);
        FeContract update = new FeContract();
        update.setId(contract.getId());
        update.setExternalContractId(result.getExternalContractId());
        update.setSignUrl(result.getSignUrl());
        update.setSignProvider(PROVIDER_MOCK);
        update.setSignStatus(SIGN_PENDING);
        update.setUpdateTime(LocalDateTime.now());
        feContractMapper.updateById(update);
        contract.setExternalContractId(result.getExternalContractId());
        contract.setSignUrl(result.getSignUrl());
    }

    private static boolean needsESignRetry(FeContract contract) {
        if (contract == null) {
            return false;
        }
        if (SIGN_SIGNED.equals(contract.getSignStatus()) || SIGN_VOIDED.equals(contract.getSignStatus())) {
            return false;
        }
        return !StringUtils.hasText(contract.getSignUrl()) || !StringUtils.hasText(contract.getExternalContractId());
    }

    /**
     * 按订单所选方案类型选择合同模板编码。
     */
    private String resolveContractType(FeEmploymentOrder order) {
        if (order.getSelectedPlanId() == null) {
            return "labor";
        }
        FeServicePlan plan = feServicePlanMapper.selectById(order.getSelectedPlanId());
        if (plan == null || !StringUtils.hasText(plan.getPlanType())) {
            return "labor";
        }
        String planType = plan.getPlanType().trim().toLowerCase();
        if ("flexible".equals(planType)) {
            return "labor_flexible";
        }
        if ("traditional".equals(planType)) {
            return "labor_dispatch";
        }
        if ("hybrid".equals(planType)) {
            return "labor_hybrid";
        }
        return "labor";
    }

    private static String buildContractTitle(String contractType, String workerName, String orderTitle) {
        String typeLabel;
        if ("labor_flexible".equals(contractType)) {
            typeLabel = "灵活用工协议";
        } else if ("labor_dispatch".equals(contractType)) {
            typeLabel = "劳务派遣协议";
        } else if ("labor_hybrid".equals(contractType)) {
            typeLabel = "混合用工协议";
        } else {
            typeLabel = "劳动合同";
        }
        String name = StringUtils.hasText(workerName) ? workerName.trim() : "临时工";
        if (StringUtils.hasText(orderTitle)) {
            return typeLabel + "-" + name + "-" + orderTitle.trim();
        }
        return typeLabel + "-" + name;
    }

    private String nextContractNo() {
        String day = LocalDateTime.now().format(CONTRACT_NO_DAY);
        int rand = ThreadLocalRandom.current().nextInt(1000, 10000);
        return "CT" + day + rand;
    }

    private static ContractSummaryResponse toSummary(FeContract contract, boolean created, String message) {
        ContractSummaryResponse response = new ContractSummaryResponse();
        if (contract != null) {
            response.setContractId(contract.getId());
            response.setContractNo(contract.getContractNo());
            response.setRegistrationId(contract.getRegistrationId());
            response.setContractType(contract.getContractType());
            response.setContractTitle(contract.getContractTitle());
            response.setSignStatus(contract.getSignStatus());
            response.setSignProvider(contract.getSignProvider());
            response.setSignUrl(contract.getSignUrl());
            response.setExternalContractId(contract.getExternalContractId());
        }
        response.setCreated(created);
        response.setMessage(message);
        return response;
    }
}
